Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-04-14 18:08:29 +00:00
parent 9b762f50fe
commit f55c925355
72 changed files with 826 additions and 296 deletions

View File

@ -44,7 +44,7 @@ docs-lint markdown:
- .default-retry
- .docs:rules:docs-lint
# When updating the image version here, update it in /scripts/lint-doc.sh too.
image: registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.15-vale-2.15.0-markdownlint-0.31.0
image: registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.15-vale-2.15.5-markdownlint-0.31.1
stage: lint
needs: []
script:

View File

@ -1,3 +1,10 @@
include:
- project: gitlab-org/quality/pipeline-common
ref: 0.3.6
file:
- /ci/allure-report.yml
- /ci/knapsack-report.yml
.review-qa-base:
extends:
- .use-docker-in-docker
@ -43,27 +50,13 @@
when: always
.allure-report-base:
image:
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.4.2
entrypoint: [""]
extends: .generate-allure-report-base
stage: post-qa
variables:
GIT_STRATEGY: none
STORAGE_CREDENTIALS: $QA_ALLURE_REPORT_GCS_CREDENTIALS
GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
allow_failure: true
script:
- |
allure-report-publisher upload gcs \
--results-glob="qa/tmp/allure-results/*" \
--bucket="gitlab-qa-allure-reports" \
--prefix="$ALLURE_REPORT_PATH_PREFIX/$CI_COMMIT_REF_SLUG" \
--update-pr="comment" \
--copy-latest \
--ignore-missing-results \
--color
ALLURE_RESULTS_GLOB: qa/tmp/allure-results/*
review-qa-smoke:
extends:
@ -121,23 +114,19 @@ review-performance:
performance: performance.json
expire_in: 31d
allure-report-qa-smoke:
# Generate single report for both smoke and reliable test jobs
# Both job types are essentially the same:
# * always executed
# * always blocking
allure-report-qa-blocking:
extends:
- .allure-report-base
- .review:rules:review-qa-smoke-report
needs: ["review-qa-smoke"]
- .review:rules:review-qa-blocking-report
needs:
- review-qa-smoke
- review-qa-reliable
variables:
ALLURE_REPORT_PATH_PREFIX: gitlab-review-smoke
ALLURE_JOB_NAME: review-qa-smoke
allure-report-qa-reliable:
extends:
- .allure-report-base
- .review:rules:review-qa-reliable-report
needs: ["review-qa-reliable"]
variables:
ALLURE_REPORT_PATH_PREFIX: gitlab-review-reliable
ALLURE_JOB_NAME: review-qa-reliable
ALLURE_JOB_NAME: review-qa-blocking
allure-report-qa-all:
extends:
@ -145,18 +134,11 @@ allure-report-qa-all:
- .review:rules:review-qa-all-report
needs: ["review-qa-all"]
variables:
ALLURE_REPORT_PATH_PREFIX: gitlab-review-all
ALLURE_JOB_NAME: review-qa-all
knapsack-report:
extends:
- .review:rules:knapsack-report
image:
name: ${QA_IMAGE}
entrypoint: [""]
- .generate-knapsack-report-base
stage: post-qa
allow_failure: true
before_script:
- cd qa
script:
- bundle exec rake 'knapsack:upload[tmp/knapsack/*/*.json]'
variables:
QA_KNAPSACK_REPORT_FILE_PATTERN: $CI_PROJECT_DIR/tmp/knapsack/*/*.json

View File

@ -1583,17 +1583,12 @@
#
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76756
# Since `review-qa-smoke` isn't allowed to fail, we need to use `when: always` for `review-qa-smoke-report`.
.review:rules:review-qa-smoke-report:
rules:
- when: always
.review:rules:review-qa-reliable:
rules:
- when: on_success
# Since `review-qa-reliable` isn't allowed to fail, we need to use `when: always`for `review-qa-reliable-report`.
.review:rules:review-qa-reliable-report:
.review:rules:review-qa-blocking-report:
rules:
- when: always
@ -1613,11 +1608,6 @@
- when: on_success
- when: on_failure
.review:rules:knapsack-report:
rules:
- if: '$KNAPSACK_GENERATE_REPORT == "true"'
when: always
.review:rules:review-cleanup:
rules:
- <<: *if-not-ee

View File

@ -1 +1 @@
1.56.1
1.57.0

View File

@ -12,7 +12,17 @@ import { mapState, mapGetters } from 'vuex';
import { s__ } from '~/locale';
import highlight from '~/lib/utils/highlight';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { GROUPS_CATEGORY, PROJECTS_CATEGORY, LARGE_AVATAR_PX, SMALL_AVATAR_PX } from '../constants';
import { truncateNamespace } from '~/lib/utils/text_utility';
import {
GROUPS_CATEGORY,
PROJECTS_CATEGORY,
MERGE_REQUEST_CATEGORY,
ISSUES_CATEGORY,
RECENT_EPICS_CATEGORY,
LARGE_AVATAR_PX,
SMALL_AVATAR_PX,
} from '../constants';
export default {
name: 'HeaderSearchAutocompleteItems',
@ -40,7 +50,7 @@ export default {
},
},
computed: {
...mapState(['search', 'loading', 'autocompleteError']),
...mapState(['search', 'loading', 'autocompleteError', 'searchContext']),
...mapGetters(['autocompleteGroupedSearchOptions']),
},
watch: {
@ -53,6 +63,13 @@ export default {
},
},
methods: {
truncateNamespace(string) {
if (string.split(' / ').length > 2) {
return truncateNamespace(string);
}
return string;
},
highlightedName(val) {
return highlight(val, this.search);
},
@ -66,6 +83,35 @@ export default {
isOptionFocused(data) {
return this.currentFocusedOption?.html_id === data.html_id;
},
isProjectsCategory(data) {
return data.category === PROJECTS_CATEGORY;
},
getEntityId(data) {
switch (data.category) {
case GROUPS_CATEGORY:
case RECENT_EPICS_CATEGORY:
return data.group_id || data.id || this.searchContext?.group?.id;
case PROJECTS_CATEGORY:
case ISSUES_CATEGORY:
case MERGE_REQUEST_CATEGORY:
return data.project_id || data.id || this.searchContext?.project?.id;
default:
return data.id;
}
},
getEntitytName(data) {
switch (data.category) {
case GROUPS_CATEGORY:
case RECENT_EPICS_CATEGORY:
return data.group_name || data.value || data.label || this.searchContext?.group?.name;
case PROJECTS_CATEGORY:
case ISSUES_CATEGORY:
case MERGE_REQUEST_CATEGORY:
return data.project_name || data.value || data.label || this.searchContext?.project?.name;
default:
return data.label;
}
},
},
AVATAR_SHAPE_OPTION_RECT,
};
@ -92,12 +138,22 @@ export default {
<gl-avatar
v-if="data.avatar_url !== undefined"
:src="data.avatar_url"
:entity-id="data.id"
:entity-name="data.label"
:entity-id="getEntityId(data)"
:entity-name="getEntitytName(data)"
:size="avatarSize(data)"
:shape="$options.AVATAR_SHAPE_OPTION_RECT"
/>
<span v-safe-html="highlightedName(data.label)"></span>
<span class="gl-display-flex gl-flex-direction-column">
<span
v-safe-html="highlightedName(data.value || data.label)"
class="gl-text-gray-900"
></span>
<span
v-if="data.value"
v-safe-html="truncateNamespace(data.label)"
class="gl-font-sm gl-text-gray-500"
></span>
</span>
</div>
</gl-dropdown-item>
</div>

View File

@ -20,6 +20,12 @@ export const GROUPS_CATEGORY = 'Groups';
export const PROJECTS_CATEGORY = 'Projects';
export const ISSUES_CATEGORY = 'Recent issues';
export const MERGE_REQUEST_CATEGORY = 'Recent merge requests';
export const RECENT_EPICS_CATEGORY = 'Recent epics';
export const LARGE_AVATAR_PX = 32;
export const SMALL_AVATAR_PX = 16;

View File

@ -43,7 +43,9 @@ export default {
message: s__(
'Integrations|You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}',
),
linkUrl: helpPagePath('integration/jira_development_panel.html', { anchor: 'usage' }),
linkUrl: helpPagePath('integration/jira_development_panel.html', {
anchor: 'use-the-integration',
}),
variant: 'success',
});

View File

@ -60,7 +60,8 @@ export default {
{{ description }}
</tooltip-on-truncate>
<tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="ipAddress">
{{ __('IP Address') }} <strong>{{ ipAddress }}</strong>
<span class="gl-md-display-none gl-lg-display-inline">{{ __('IP Address') }}</span>
<strong>{{ ipAddress }}</strong>
</tooltip-on-truncate>
</div>
</template>

View File

@ -293,7 +293,7 @@ class GroupsController < Groups::ApplicationController
:setup_for_company,
:jobs_to_be_done,
:crm_enabled
]
] + [group_feature_attributes: group_feature_attributes]
end
# rubocop: disable CodeReuse/ActiveRecord
@ -396,6 +396,10 @@ class GroupsController < Groups::ApplicationController
experiment(:require_verification_for_namespace_creation, user: current_user).track(:start_create_group)
end
def group_feature_attributes
[]
end
end
GroupsController.prepend_mod_with('GroupsController')

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
class IosSpecificTemplatesExperiment < ApplicationExperiment
before_run(if: :skip_experiment) { throw(:abort) } # rubocop:disable Cop/BanCatchThrow
private
def skip_experiment
actor_not_able_to_create_pipelines? ||
project_targets_non_ios_platforms? ||
project_has_gitlab_ci? ||
project_has_pipelines?
end
def actor_not_able_to_create_pipelines?
!context.actor.is_a?(User) || !context.actor.can?(:create_pipeline, context.project)
end
def project_targets_non_ios_platforms?
context.project.project_setting.target_platforms.exclude?('ios')
end
def project_has_gitlab_ci?
context.project.has_ci? && context.project.builds_enabled?
end
def project_has_pipelines?
context.project.all_pipelines.count > 0
end
end

View File

@ -106,6 +106,12 @@ module Ci
e.candidate { data[:any_runners_available] = project.active_runners.exists?.to_s }
end
experiment(:ios_specific_templates, actor: current_user, project: project, sticky_to: project) do |e|
e.candidate do
data[:registration_token] = project.runners_token if can?(current_user, :register_project_runners, project)
end
end
data
end

View File

@ -260,6 +260,7 @@ module SearchHelper
{
category: "Groups",
id: group.id,
value: "#{search_result_sanitize(group.name)}",
label: "#{search_result_sanitize(group.full_name)}",
url: group_path(group),
avatar_url: group.avatar_url || ''
@ -311,7 +312,9 @@ module SearchHelper
id: mr.id,
label: search_result_sanitize(mr.title),
url: merge_request_path(mr),
avatar_url: mr.project.avatar_url || ''
avatar_url: mr.project.avatar_url || '',
project_id: mr.target_project_id,
project_name: mr.target_project.name
}
end
end
@ -325,7 +328,9 @@ module SearchHelper
id: i.id,
label: search_result_sanitize(i.title),
url: issue_path(i),
avatar_url: i.project.avatar_url || ''
avatar_url: i.project.avatar_url || '',
project_id: i.project_id,
project_name: i.project.name
}
end
end

View File

@ -1946,6 +1946,10 @@ class Project < ApplicationRecord
Gitlab.config.pages.enabled
end
def pages_show_onboarding?
!(pages_metadatum&.onboarding_complete || pages_metadatum&.deployed)
end
def remove_private_deploy_keys
exclude_keys_linked_to_other_projects = <<-SQL
NOT EXISTS (
@ -1961,6 +1965,10 @@ class Project < ApplicationRecord
.delete_all
end
def mark_pages_onboarding_complete
ensure_pages_metadatum.update!(onboarding_complete: true)
end
def mark_pages_as_deployed
ensure_pages_metadatum.update!(deployed: true)
end

View File

@ -476,7 +476,7 @@ class User < ApplicationRecord
scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil) }
scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
scope :by_ids_or_usernames, -> (ids, usernames) { where(username: usernames).or(where(id: ids)) }
scope :without_forbidden_states, -> { confirmed.where.not(state: FORBIDDEN_SEARCH_STATES) }
scope :without_forbidden_states, -> { where.not(state: FORBIDDEN_SEARCH_STATES) }
strip_attributes! :name
@ -1732,8 +1732,12 @@ class User < ApplicationRecord
end
def attention_requested_open_merge_requests_count(force: false)
Rails.cache.fetch(attention_request_cache_key, force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
if Feature.enabled?(:uncached_mr_attention_requests_count, self, default_enabled: :yaml)
MergeRequestsFinder.new(self, attention: self.username, state: 'opened', non_archived: true).execute.count
else
Rails.cache.fetch(attention_request_cache_key, force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
MergeRequestsFinder.new(self, attention: self.username, state: 'opened', non_archived: true).execute.count
end
end
end

View File

@ -27,5 +27,3 @@ class MergeRequestSerializer < BaseSerializer
super(merge_request, opts, entity)
end
end
MergeRequestSerializer.prepend_mod_with('MergeRequestSerializer')

View File

@ -68,7 +68,7 @@ module Jira
end
def auth_docs_link_start
auth_docs_link_url = Rails.application.routes.url_helpers.help_page_path('integration/jira', anchor: 'authentication-in-jira')
auth_docs_link_url = Rails.application.routes.url_helpers.help_page_path('integration/jira/index', anchor: 'authentication-in-jira')
'<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auth_docs_link_url }
end

View File

@ -1,16 +1,13 @@
= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-git-lfs-limits-settings'), html: { class: 'fieldset-form' } do |f|
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-git-lfs-limits-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
%h5
= _('Authenticated Git LFS request rate limit')
.form-group
.form-check
= f.check_box :throttle_authenticated_git_lfs_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_authenticated_git_lfs_checkbox' }
= f.label :throttle_authenticated_git_lfs_enabled, class: 'form-check-label gl-font-weight-bold' do
= _('Enable authenticated Git LFS request rate limit')
%span.form-text.gl-text-gray-600
= _('Helps reduce request volume (for example, from crawlers or abusive bots)')
= f.gitlab_ui_checkbox_component :throttle_authenticated_git_lfs_enabled,
_('Enable authenticated Git LFS request rate limit'),
help_text: _('Helps reduce request volume (for example, from crawlers or abusive bots)')
.form-group
= f.label :throttle_authenticated_git_lfs_requests_per_period, _('Max authenticated Git LFS requests per period per user'), class: 'gl-font-weight-bold'
= f.number_field :throttle_authenticated_git_lfs_requests_per_period, class: 'form-control gl-form-input'

View File

@ -1,4 +1,4 @@
= form_for [:admin, @application], url: @url, html: {role: 'form'} do |f|
= gitlab_ui_form_for [:admin, @application], url: @url, html: {role: 'form'} do |f|
= form_errors(application)
= content_tag :div, class: 'form-group row' do
@ -45,7 +45,7 @@
.col-sm-2.col-form-label.pt-0
= f.label :scopes
.col-sm-10
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes, f: f
.form-actions
= f.submit _('Save application'), class: "gl-button btn btn-confirm wide"

View File

@ -18,7 +18,7 @@
.form-group.row
.offset-sm-2.col-sm-10
= render 'shared/allow_request_access', form: f, bold_label: true
= render 'shared/allow_request_access', form: f
= render 'groups/group_admin_settings', f: f

View File

@ -2,14 +2,12 @@
.col-sm-2.col-form-label.pt-0
= f.label :lfs_enabled, _('Large File Storage')
.col-sm-10
.form-check
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input'
= f.label :lfs_enabled, class: 'form-check-label' do
%strong
= _('Allow projects within this group to use Git LFS')
= link_to sprite_icon('question-o'), help_page_path('topics/git/lfs/index')
%br/
%span= _('This setting can be overridden in each project.')
- label = _('Allow projects within this group to use Git LFS')
- help_link = link_to sprite_icon('question-o'), help_page_path('topics/git/lfs/index'), class: 'gl-ml-2'
= f.gitlab_ui_checkbox_component :lfs_enabled,
'%{label}%{help_link}'.html_safe % { label: label, help_link: help_link },
help_text: _('This setting can be overridden in each project.'),
checkbox_options: { checked: @group.lfs_enabled? }
.form-group.row
.col-sm-2.col-form-label
= f.label s_('ProjectCreationLevel|Allowed to create projects')
@ -26,12 +24,9 @@
.col-sm-2.col-form-label.pt-0
= f.label :require_two_factor_authentication, _('Two-factor authentication')
.col-sm-10
.form-check
= f.check_box :require_two_factor_authentication, class: 'form-check-input'
= f.label :require_two_factor_authentication, class: 'form-check-label' do
%strong
= _("Require all users in this group to set up two-factor authentication")
= link_to sprite_icon('question-o'), help_page_path('security/two_factor_authentication', anchor: 'enforce-2fa-for-all-users-in-a-group')
- label = _("Require all users in this group to set up two-factor authentication")
- help_link = link_to sprite_icon('question-o'), help_page_path('security/two_factor_authentication', anchor: 'enforce-2fa-for-all-users-in-a-group'), class: 'gl-ml-2'
= f.gitlab_ui_checkbox_component :require_two_factor_authentication, '%{label}%{help_link}'.html_safe % { label: label, help_link: help_link }
.form-group.row
.offset-sm-2.col-sm-10
.form-check

View File

@ -34,6 +34,8 @@
= render 'groups/settings/ip_restriction_registration_features_cta', f: f
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
- if Feature.enabled?(:group_wiki_settings_toggle, @group, default_enabled: :yaml)
= render_if_exists 'groups/settings/wiki', f: f, group: @group
= render 'groups/settings/lfs', f: f
= render 'groups/settings/project_creation_level', f: f, group: @group
= render 'groups/settings/subgroup_creation_level', f: f, group: @group

View File

@ -1,6 +1,3 @@
- label_class = local_assigns.fetch(:bold_label, false) ? 'font-weight-bold' : ''
= form.gitlab_ui_checkbox_component :request_access_enabled,
_('Allow users to request access (if visibility is public or internal)'),
label_options: { class: label_class },
checkbox_options: { data: { qa_selector: 'request_access_checkbox' } }

View File

@ -10,7 +10,7 @@
%p.profile-settings-content
= _("Enter the name of your application, and we'll return a unique %{type}.") % { type: type }
= form_for token, as: prefix, url: path, method: :post, html: { class: 'js-requires-input' } do |f|
= gitlab_ui_form_for token, as: prefix, url: path, method: :post, html: { class: 'js-requires-input' } do |f|
= form_errors(token)
@ -42,7 +42,7 @@
%p.text-secondary#select_scope_help_text
= s_('Tokens|Scopes set the permission levels granted to the token.')
= link_to _("Learn more."), help_path, target: '_blank', rel: 'noopener noreferrer'
= render 'shared/tokens/scopes_form', prefix: prefix, token: token, scopes: scopes
= render 'shared/tokens/scopes_form', prefix: prefix, token: token, scopes: scopes, f: f
- if prefix == :personal_access_token && Feature.enabled?(:personal_access_tokens_scoped_to_projects, current_user)
.js-access-tokens-projects

View File

@ -1,4 +1,4 @@
= form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f|
= gitlab_ui_form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f|
= form_errors(@deploy_keys.new_key)
.form-group.row
= f.label :title, class: "label-bold"
@ -13,12 +13,8 @@
= f.fields_for :deploy_keys_projects do |deploy_keys_project_form|
.form-group.row
= deploy_keys_project_form.label :can_push do
= deploy_keys_project_form.check_box :can_push
%strong= _('Grant write permissions to this key')
.form-group.row
%p.light.gl-mb-0
= _('Allow this key to push to this repository')
= deploy_keys_project_form.gitlab_ui_checkbox_component :can_push, _('Grant write permissions to this key'),
help_text: _('Allow this key to push to this repository')
.form-group.row
= f.submit _("Add key"), class: "btn gl-button btn-confirm", data: { qa_selector: "add_deploy_key_button"}

View File

@ -1,4 +1,4 @@
= form_for @application, url: url, html: { role: 'form', class: 'doorkeeper-app-form' } do |f|
= gitlab_ui_form_for @application, url: url, html: { role: 'form', class: 'doorkeeper-app-form' } do |f|
= form_errors(@application)
.form-group
@ -12,22 +12,19 @@
%span.form-text.text-muted
= _('Use one line per URI')
.form-group.form-check
= f.check_box :confidential, class: 'form-check-input'
= f.label :confidential, class: 'label-bold form-check-label'
%span.form-text.text-muted
= _('Enable only for confidential applications exclusively used by a trusted backend server that can securely store the client secret. Do not enable for native-mobile, single-page, or other JavaScript applications because they cannot keep the client secret confidential.')
.form-group
= f.gitlab_ui_checkbox_component :confidential, _('Confidential'),
help_text: _('Enable only for confidential applications exclusively used by a trusted backend server that can securely store the client secret. Do not enable for native-mobile, single-page, or other JavaScript applications because they cannot keep the client secret confidential.')
.form-group.form-check
= f.check_box :expire_access_tokens, class: 'form-check-input'
= f.label :expire_access_tokens, class: 'label-bold form-check-label'
%span.form-text.text-muted
= _('Enable access tokens to expire after 2 hours. If disabled, tokens do not expire.')
= link_to _('Learn more.'), help_page_path('integration/oauth_provider.md', anchor: 'expiring-access-tokens'), target: '_blank', rel: 'noopener noreferrer'
.form-group
- help_text = _('Enable access tokens to expire after 2 hours. If disabled, tokens do not expire.')
- help_link = link_to _('Learn more.'), help_page_path('integration/oauth_provider.md', anchor: 'expiring-access-tokens'), target: '_blank', rel: 'noopener noreferrer'
= f.gitlab_ui_checkbox_component :expire_access_tokens, _('Expire access tokens'),
help_text: '%{help_text} %{help_link}'.html_safe % { help_text: help_text, help_link: help_link }
.form-group
= f.label :scopes, class: 'label-bold'
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: @application, scopes: @scopes
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: @application, scopes: @scopes, f: f
.gl-mt-3
= f.submit _('Save application'), class: "gl-button btn btn-confirm"

View File

@ -1,9 +1,14 @@
- scopes = local_assigns.fetch(:scopes)
- prefix = local_assigns.fetch(:prefix)
- token = local_assigns.fetch(:token)
- f = local_assigns.fetch(:f)
- scopes.each do |scope|
%fieldset.form-group.form-check
= check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}", class: "form-check-input", data: { qa_selector: "#{scope}_checkbox" }
= label_tag "#{prefix}_scopes_#{scope}", scope, class: 'label-bold form-check-label'
.text-secondary= t scope, scope: scope_description(prefix)
%fieldset
- scopes.each do |scope|
- help_text = t scope, scope: scope_description(prefix)
= f.gitlab_ui_checkbox_component :scopes, scope,
help_text: help_text,
checkbox_options: { checked: token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}", multiple: true, data: { qa_selector: "#{scope}_checkbox" } },
checked_value: scope,
unchecked_value: nil,
label_options: { data: { qa_selector: "#{scope}_label" } }

View File

@ -0,0 +1,8 @@
---
name: group_wiki_settings_toggle
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82298
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/358387
milestone: '14.10'
type: development
group: group::editor
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: uncached_mr_attention_requests_count
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84145
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/357480
milestone: '14.10'
type: development
group: group::code review
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: ios_specific_templates
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84589
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/356398
milestone: "14.10"
type: experiment
group: group::activation
default_enabled: false

View File

@ -0,0 +1,21 @@
---
key_path: usage_activity_by_stage_monthly.release.users_creating_deployment_approvals
description: Users who have used the deployment approvals feature by month
product_section: ops
product_stage: release
product_group: group::release
product_category: continuous_delivery
value_type: number
status: active
milestone: "14.10"
introduced_by_url:
time_frame: 28d
data_source: database
data_category: optional
instrumentation_class: CountUsersDeploymentApprovals
performance_indicator_type: []
distribution:
- ee
tier:
- premium
- ultimate

View File

@ -0,0 +1,21 @@
---
key_path: usage_activity_by_stage.release.users_creating_deployment_approvals
description: Users who have used the deployment approvals feature by all time
product_section: ops
product_stage: release
product_group: group::release
product_category: continuous_delivery
value_type: number
status: active
milestone: "14.10"
introduced_by_url:
time_frame: all
data_source: database
data_category: optional
instrumentation_class: CountUsersDeploymentApprovals
performance_indicator_type: []
distribution:
- ee
tier:
- premium
- ultimate

View File

@ -1,16 +0,0 @@
- name: "Request a new review" # the name of the feature being removed. Avoid the words `deprecation`, `deprecate`, `removal`, and `remove` in this field because these are implied.
announcement_milestone: "15.0" # The milestone when this feature was deprecated.
announcement_date: "2022-05-22" # The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is being removed.
removal_date: "2022-05-22" # This should almost always be the 22nd of a month (YYYY-MM-DD), the date of the milestone release when this feature will be removed.
breaking_change: false # Change to true if this removal is a breaking change.
reporter: phikai # GitLab username of the person reporting the removal
body: | # Do not modify this line, instead modify the lines below.
The ability to [request a new review](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/#request-a-new-review) has been removed in GitLab 15.0. This feature is replaced with [requesting attention](https://docs.gitlab.com/ee/user/project/merge_requests/#request-attention-to-a-merge-request) to a merge request.
# The following items are not published on the docs page, but may be used in the future.
stage: Create # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
issue_url: # (optional) This is a link to the deprecation issue in GitLab
documentation_url: https://docs.gitlab.com/ee/user/project/merge_requests/reviews/#request-a-new-review # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
class AddPagesOnboardingState < Gitlab::Database::Migration[1.0]
def up
add_column :project_pages_metadata, :onboarding_complete, :boolean, default: false, null: false
end
def down
remove_column :project_pages_metadata, :onboarding_complete
end
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
class UpdatePagesOnboardingState < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
BATCH_SIZE = 75
def up
define_batchable_model(
:project_pages_metadata
).where(
deployed: true
).each_batch(
of: BATCH_SIZE,
column: :project_id
) do |batch|
batch.update_all(onboarding_complete: true)
end
end
def down
define_batchable_model(
:project_pages_metadata
).where(
onboarding_complete: true
).each_batch(
of: BATCH_SIZE,
column: :project_id
) do |batch|
batch.update_all(onboarding_complete: false)
end
end
end

View File

@ -0,0 +1 @@
d9a9d143ff553cbad5eb32a908370133549850f10b27b30eb6a1bde686054c45

View File

@ -0,0 +1 @@
94dbae9bbfb2da49a0d20b674e15f457c613c8ba0612603dd8fe9ac5699160d1

View File

@ -19304,7 +19304,8 @@ ALTER SEQUENCE project_mirror_data_id_seq OWNED BY project_mirror_data.id;
CREATE TABLE project_pages_metadata (
project_id bigint NOT NULL,
deployed boolean DEFAULT false NOT NULL,
pages_deployment_id bigint
pages_deployment_id bigint,
onboarding_complete boolean DEFAULT false NOT NULL
);
CREATE TABLE project_repositories (

View File

@ -213,7 +213,7 @@ After configuring your local PlantUML server, you're ready to enable the PlantUM
1. On the left sidebar, go to **Settings > General** and expand the **PlantUML** section.
1. Select the **Enable PlantUML** checkbox.
1. Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`,
and click **Save changes**.
and select **Save changes**.
Depending on your PlantUML and GitLab version numbers, you may also need to take
these steps:

View File

@ -305,8 +305,11 @@ Parameters:
"website_url": "",
"organization": "",
"job_title": "Operations Specialist",
"pronouns": "he/him",
"work_information": null,
"followers": 1,
"following": 1
"following": 1,
"local_time": "3:38 PM"
}
```
@ -346,6 +349,11 @@ Example Responses:
"website_url": "",
"organization": "",
"job_title": "Operations Specialist",
"pronouns": "he/him",
"work_information": null,
"followers": 1,
"following": 1,
"local_time": "3:38 PM",
"last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1,
@ -580,6 +588,13 @@ GET /user
"twitter": "",
"website_url": "",
"organization": "",
"job_title": "",
"pronouns": "he/him",
"bot": false,
"work_information": null,
"followers": 0,
"following": 0,
"local_time": "3:38 PM",
"last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1,
@ -596,10 +611,13 @@ GET /user
"can_create_project": true,
"two_factor_enabled": true,
"external": false,
"private_profile": false
"private_profile": false,
"commit_email": "admin@example.com",
}
```
Users on [GitLab Premium or higher](https://about.gitlab.com/pricing/) also see the `shared_runners_minutes_limit`, `extra_shared_runners_minutes_limit` parameters.
## List current user (for admins)
> The `namespace_id` field in the response was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82045) in GitLab 14.10.
@ -654,7 +672,8 @@ Parameters:
"commit_email": "john-codes@example.com",
"current_sign_in_ip": "196.165.1.102",
"last_sign_in_ip": "172.127.2.22",
"namespace_id": 1
"namespace_id": 1,
"note": null
}
```

View File

@ -1518,6 +1518,47 @@ The voting strategy in GitLab 13.4 and later requires the primary and secondary
voters to agree.
```
#### Deprecated features
When a feature is deprecated, add `(DEPRECATED)` to the page title or to
the heading of the section documenting the feature, immediately before
the tier badge:
```markdown
<!-- Page title example: -->
# Feature A (DEPRECATED) **(ALL TIERS)**
<!-- Doc section example: -->
## Feature B (DEPRECATED) **(PREMIUM SELF)**
```
Add the deprecation to the version history note (you can include a link
to a replacement when available):
```markdown
> - [Deprecated](<link-to-issue>) in GitLab 11.3. Replaced by [meaningful text](<link-to-appropriate-documentation>).
```
You can also describe the replacement in surrounding text, if available. If the
deprecation isn't obvious in existing text, you may want to include a warning:
```markdown
WARNING:
This feature was [deprecated](link-to-issue) in GitLab 12.3 and replaced by
[Feature name](link-to-feature-documentation).
```
If you add `(DEPRECATED)` to the page's title and the document is linked from the docs
navigation, either remove the page from the nav or update the nav item to include the
same text before the feature name:
```yaml
- doc_title: (DEPRECATED) Feature A
```
In the first major GitLab version after the feature was deprecated, be sure to
remove information about that deprecated feature.
#### End-of-life for features or products
When a feature or product enters its end-of-life, indicate its status by
@ -1604,47 +1645,6 @@ To view historical information about a feature, review GitLab
[release posts](https://about.gitlab.com/releases/), or search for the issue or
merge request where the work was done.
### Deprecated features
When a feature is deprecated, add `(DEPRECATED)` to the page title or to
the heading of the section documenting the feature, immediately before
the tier badge:
```markdown
<!-- Page title example: -->
# Feature A (DEPRECATED) **(ALL TIERS)**
<!-- Doc section example: -->
## Feature B (DEPRECATED) **(PREMIUM SELF)**
```
Add the deprecation to the version history note (you can include a link
to a replacement when available):
```markdown
> - [Deprecated](<link-to-issue>) in GitLab 11.3. Replaced by [meaningful text](<link-to-appropriate-documentation>).
```
You can also describe the replacement in surrounding text, if available. If the
deprecation isn't obvious in existing text, you may want to include a warning:
```markdown
WARNING:
This feature was [deprecated](link-to-issue) in GitLab 12.3 and replaced by
[Feature name](link-to-feature-documentation).
```
If you add `(DEPRECATED)` to the page's title and the document is linked from the docs
navigation, either remove the page from the nav or update the nav item to include the
same text before the feature name:
```yaml
- doc_title: (DEPRECATED) Feature A
```
In the first major GitLab version after the feature was deprecated, be sure to
remove information about that deprecated feature.
## Products and features
Refer to the information in this section when describing products and features

View File

@ -276,7 +276,7 @@ by changing the status. Setting the status to:
For [incidents created from alerts](alerts.md#create-an-incident-from-an-alert),
updating the incident status also updates the alert status.
## Change escalation policy **(PREMIUM)**
### Change escalation policy **(PREMIUM)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.

View File

@ -28,12 +28,6 @@ For removal reviewers (Technical Writers only):
https://about.gitlab.com/handbook/marketing/blog/release-posts/#update-the-removals-doc
-->
## 15.0
### Request a new review
The ability to [request a new review](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/#request-a-new-review) has been removed in GitLab 15.0. This feature is replaced with [requesting attention](https://docs.gitlab.com/ee/user/project/merge_requests/#request-attention-to-a-merge-request) to a merge request.
## 14.9
### Integrated error tracking disabled by default

View File

@ -185,3 +185,12 @@ The results from the query can be plugged into the command:
```shell
sudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,events,id,'[["id"]\, ["id_convert_to_bigint"]]']
```
### The `BackfillNamespaceIdForNamespaceRoute` batched migration job fails
In GitLab 14.8, the `BackfillNamespaceIdForNamespaceRoute` batched background migration job
may fail to complete. When retried, a `500 Server Error` is returned. This issue was
[resolved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82387) in GitLab 14.9.
To resolve this issue, [upgrade GitLab](../../../update/index.md) from 14.8 to 14.9.
You can ignore the failed batch migration until after you update to GitLab 14.9.

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -30,6 +30,30 @@ repository is too large, the import can timeout.
You can also [connect your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md).
## Project import history
You can view all project imports created by you. This list includes the following:
- Source (without credentials for security reasons)
- Destination
- Status
- Error details if the import failed
To view project import history:
1. Sign in to GitLab.
1. On the top bar, select **New** (**{plus}**).
1. Select **New project/repository**.
1. Select **Import project**.
1. Select **History**.
![Project import history page](img/gitlab_import_history_page_v14_10.png)
The history also includes projects created from [built-in](../working_with_projects.md#create-a-project-from-a-built-in-template)
or [custom](../working_with_projects.md#create-a-project-from-a-built-in-template)
templates. GitLab uses [import repository by URL](repo_by_url.md)
to create a new project from a template.
## LFS authentication
When importing a project that contains LFS objects, if the project has an [`.lfsconfig`](https://github.com/git-lfs/git-lfs/blob/master/docs/man/git-lfs-config.5.ronn)

View File

@ -25,7 +25,7 @@ module API
detail 'This feature was introduced in GitLab 10.6.'
end
get ':id/export/download' do
check_rate_limit! :project_download_export, scope: [current_user, user_project]
check_rate_limit! :project_download_export, scope: [current_user, user_project.namespace]
if user_project.export_file_exists?
if user_project.export_archive_exists?

View File

@ -31,14 +31,7 @@ secret_detection:
script:
- if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi
# Historic scan
- |
if [ "$SECRET_DETECTION_HISTORIC_SCAN" == "true" ]
then
echo "historic scan"
git fetch --unshallow origin $CI_COMMIT_REF_NAME
/analyzer run
exit
fi
- if [ "$SECRET_DETECTION_HISTORIC_SCAN" == "true" ]; then echo "Running Secret Detection Historic Scan"; /analyzer run; exit; fi
# Default branch scan
- if [ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit; fi
# Push event

View File

@ -1276,9 +1276,6 @@ msgid_plural "+%d more"
msgstr[0] ""
msgstr[1] ""
msgid "+%{approvers} more approvers"
msgstr ""
msgid "+%{extra} more"
msgstr ""
@ -1596,9 +1593,6 @@ msgstr ""
msgid "A member of the abuse team will review your report as soon as possible."
msgstr ""
msgid "A merge request hasn't yet been merged"
msgstr ""
msgid "A new Auto DevOps pipeline has been created, go to the Pipelines page for details"
msgstr ""
@ -3666,6 +3660,12 @@ msgstr ""
msgid "Allow \"%{group_name}\" to sign you in"
msgstr ""
msgid "Allow access only to members of this group"
msgstr ""
msgid "Allow access to everyone"
msgstr ""
msgid "Allow access to members of the following group"
msgstr ""
@ -4514,9 +4514,6 @@ msgstr ""
msgid "Applying suggestions..."
msgstr ""
msgid "Approval Status"
msgstr ""
msgid "Approval rules"
msgstr ""
@ -4708,15 +4705,6 @@ msgstr ""
msgid "ApprovalSettings|This setting is configured in %{groupName} and can only be changed in the group settings by an administrator or group owner."
msgstr ""
msgid "ApprovalStatusTooltip|Adheres to separation of duties"
msgstr ""
msgid "ApprovalStatusTooltip|At least one rule does not adhere to separation of duties"
msgstr ""
msgid "ApprovalStatusTooltip|Fails to adhere to separation of duties"
msgstr ""
msgid "Approvals are optional."
msgstr ""
@ -9259,9 +9247,6 @@ msgstr ""
msgid "Compliance report"
msgstr ""
msgid "ComplianceDashboard|created by:"
msgstr ""
msgid "ComplianceFrameworks|Add framework"
msgstr ""
@ -13145,6 +13130,9 @@ msgstr ""
msgid "Disable group runners"
msgstr ""
msgid "Disable the group-level wiki"
msgstr ""
msgid "Disable two-factor authentication"
msgstr ""
@ -15155,6 +15143,9 @@ msgstr ""
msgid "Expiration date:"
msgstr ""
msgid "Expire access tokens"
msgstr ""
msgid "Expired"
msgstr ""
@ -18550,9 +18541,6 @@ msgstr ""
msgid "Helps reduce request volume for protected paths."
msgstr ""
msgid "Here you will find recent merge request activity"
msgstr ""
msgid "Hi %{username}!"
msgstr ""
@ -37528,9 +37516,6 @@ msgstr ""
msgid "The comparison view may be inaccurate due to merge conflicts."
msgstr ""
msgid "The compliance report captures merged changes that violate compliance best practices."
msgstr ""
msgid "The compliance report shows the merge request violations merged in protected environments."
msgstr ""
@ -40474,9 +40459,6 @@ msgstr ""
msgid "Updated date"
msgstr ""
msgid "Updates"
msgstr ""
msgid "Updating"
msgstr ""
@ -43960,9 +43942,6 @@ msgid_plural "approvals"
msgstr[0] ""
msgstr[1] ""
msgid "approved by: "
msgstr ""
msgid "archived"
msgstr ""
@ -44881,9 +44860,6 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
msgid "merged %{timeAgo}"
msgstr ""
msgid "metric_id must be unique across a project"
msgstr ""
@ -45302,9 +45278,6 @@ msgstr ""
msgid "new merge request"
msgstr ""
msgid "no approvers"
msgstr ""
msgid "no expiration"
msgstr ""

View File

@ -19,7 +19,7 @@ module QA
end
base.view 'app/views/shared/tokens/_scopes_form.html.haml' do
element :api_checkbox, '#{scope}_checkbox' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
element :api_label, '#{scope}_label' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
end
base.view 'app/views/shared/access_tokens/_created_container.html.haml' do
@ -36,7 +36,7 @@ module QA
end
def check_api
check_element(:api_checkbox)
click_element(:api_label)
end
def click_create_token_button

View File

@ -31,7 +31,7 @@ module QA
end
def fabricate_via_api!
Service::DockerRun::GitlabRunner.new(name).tap do |runner|
@docker_container = Service::DockerRun::GitlabRunner.new(name).tap do |runner|
runner.pull
runner.token = @token ||= project.runners_token
runner.address = Runtime::Scenario.gitlab_address
@ -48,11 +48,20 @@ module QA
def remove_via_api!
runners = list_of_runners(tag_list: @tags)
return if runners.blank?
# If we have no runners, print the logs from the runner docker container in case they show why it isn't running.
if runners.blank?
dump_logs
return
end
this_runner = runners.find { |runner| runner[:description] == name }
# As above, but now we should have a specific runner. If not, print the logs from the runner docker container
# to see if we can find out why the runner isn't running.
unless this_runner
dump_logs
raise "Project #{project.path_with_namespace} does not have a runner with a description matching #{name} #{"or tags #{@tags}" if @tags&.any?}. Runners available: #{runners}"
end
@ -73,6 +82,10 @@ module QA
parse_body(response)
end
def reload!
super if method(:running?).super_method.call
end
def api_delete_path
"/runners/#{id}"
end
@ -86,6 +99,16 @@ module QA
def api_post_body
end
private
def dump_logs
if @docker_container.running?
@docker_container.logs { |line| QA::Runtime::Logger.debug(line) }
else
QA::Runtime::Logger.debug("No runner container found named #{name}")
end
end
end
end
end

View File

@ -11,6 +11,12 @@ module QA
@runner_network = Runtime::Scenario.attributes[:runner_network] || @network
end
def logs
shell "docker logs #{@name}" do |line|
yield " #{line.chomp}"
end
end
def network
shell "docker network inspect #{@network}"
rescue CommandError

View File

@ -43,6 +43,8 @@ module QA
#{@image} #{add_gitlab_tls_cert if @address.include? "https"} && docker exec --detach #{@name} sh -c "#{register_command}"
CMD
wait_until_running_and_configured
# Prove airgappedness
if runner_network == 'airgapped'
shell("docker exec #{@name} sh -c '#{prove_airgap}'")
@ -111,6 +113,10 @@ module QA
&& docker cp #{gitlab_tls_certificate.path} #{@name}:/etc/gitlab-runner/certs/gitlab.test.crt
CMD
end
def wait_until_running_and_configured
wait_until_shell_command_matches("docker logs #{@name}", /Configuration loaded/)
end
end
end
end

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Manage', :github, :requires_admin do
# Spec uses real github.com, which means outage of github.com can actually block deployment
# Keep spec in reliable bucket but don't run in blocking pipelines
RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin do
describe 'Project import', issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/353583' do
let!(:api_client) { Runtime::API::Client.as_admin }
let!(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } }

View File

@ -15,15 +15,11 @@ module QA
end
end
before do
sleep 5 # Runner should register within 5 seconds
end
# Removing a runner via the UI is covered by `spec/features/runners_spec.rb``
it 'removes the runner' do
runners = runner.list_of_runners(tag_list: runner_tags)
expect(runners.size).to eq(1)
runners = nil
expect { (runners = runner.list_of_runners(tag_list: runner_tags)).size }
.to eventually_eq(1).within(max_duration: 10, sleep_interval: 1)
expect(runners.first[:description]).to eq(executor)
request = Runtime::API::Request.new(api_client, "runners/#{runners.first[:id]}")

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Manage', :github, :requires_admin do
# Spec uses real github.com, which means outage of github can actually block deployment
# Keep spec in reliable bucket but don't run in blocking pipelines
RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin do
describe 'Project import' do
let(:github_repo) { 'gitlab-qa-github/import-test' }
let(:api_client) { Runtime::API::Client.as_admin }

View File

@ -22,8 +22,6 @@ module QA
Page::Project::Menu.perform(&:go_to_ci_cd_settings)
Page::Project::Settings::CiCd.perform do |settings|
sleep 5 # Runner should register within 5 seconds
settings.expand_runners_settings do |page|
expect(page).to have_content(executor)
expect(page).to have_online_runner

View File

@ -24,6 +24,7 @@ module QA
before do
allow(subject).to receive(:shell)
allow(subject).to receive(:wait_until_running_and_configured)
end
context 'defaults' do

View File

@ -128,7 +128,7 @@ function run_locally_or_in_docker() {
$cmd $args
elif hash docker 2>/dev/null
then
docker run -t -v ${PWD}:/gitlab -w /gitlab --rm registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.15-vale-2.15.0-markdownlint-0.31.0 ${cmd} ${args}
docker run -t -v ${PWD}:/gitlab -w /gitlab --rm registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.15-vale-2.15.5-markdownlint-0.31.1 ${cmd} ${args}
else
echo
echo " ✖ ERROR: '${cmd}' not found. Install '${cmd}' or Docker to proceed." >&2

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IosSpecificTemplatesExperiment do
subject do
described_class.new(actor: user, project: project) do |e|
e.candidate { true }
end.run
end
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :auto_devops_disabled) }
let!(:project_setting) { create(:project_setting, project: project, target_platforms: target_platforms) }
let(:target_platforms) { %w(ios) }
before do
stub_experiments(ios_specific_templates: :candidate)
project.add_developer(user) if user
end
it { is_expected.to be true }
describe 'skipping the experiment' do
context 'no actor' do
let_it_be(:user) { nil }
it { is_expected.to be_falsey }
end
context 'actor cannot create pipelines' do
before do
project.add_guest(user)
end
it { is_expected.to be_falsey }
end
context 'targeting a non iOS platform' do
let(:target_platforms) { [] }
it { is_expected.to be_falsey }
end
context 'project has a ci.yaml file' do
before do
allow(project).to receive(:has_ci?).and_return(true)
end
it { is_expected.to be_falsey }
end
context 'project has pipelines' do
before do
create(:ci_pipeline, project: project)
end
it { is_expected.to be_falsey }
end
end
end

View File

@ -36,14 +36,14 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
click_on "1"
# Scopes
check "api"
check "read_api"
check "read_user"
click_on "Create impersonation token"
expect(active_impersonation_tokens).to have_text(name)
expect(active_impersonation_tokens).to have_text('in')
expect(active_impersonation_tokens).to have_text('api')
expect(active_impersonation_tokens).to have_text('read_api')
expect(active_impersonation_tokens).to have_text('read_user')
expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1)
expect(created_impersonation_token).not_to be_empty

View File

@ -47,14 +47,14 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do
click_on "1"
# Scopes
check "api"
check "read_api"
check "read_user"
click_on "Create personal access token"
expect(active_personal_access_tokens).to have_text(name)
expect(active_personal_access_tokens).to have_text('in')
expect(active_personal_access_tokens).to have_text('api')
expect(active_personal_access_tokens).to have_text('read_api')
expect(active_personal_access_tokens).to have_text('read_user')
expect(created_personal_access_token).not_to be_empty
end

View File

@ -14,7 +14,7 @@ RSpec.describe UsersFinder do
it 'returns searchable users' do
users = described_class.new(user).execute
expect(users).to contain_exactly(user, normal_user, external_user, omniauth_user, internal_user, admin_user, project_bot)
expect(users).to contain_exactly(user, normal_user, external_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot)
end
it 'filters by username' do
@ -56,7 +56,7 @@ RSpec.describe UsersFinder do
it 'filters by non external users' do
users = described_class.new(user, non_external: true).execute
expect(users).to contain_exactly(user, normal_user, omniauth_user, internal_user, admin_user, project_bot)
expect(users).to contain_exactly(user, normal_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot)
end
it 'filters by created_at' do
@ -73,7 +73,7 @@ RSpec.describe UsersFinder do
it 'filters by non internal users' do
users = described_class.new(user, non_internal: true).execute
expect(users).to contain_exactly(user, normal_user, external_user, omniauth_user, admin_user, project_bot)
expect(users).to contain_exactly(user, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot)
end
it 'does not filter by custom attributes' do
@ -82,18 +82,18 @@ RSpec.describe UsersFinder do
custom_attributes: { foo: 'bar' }
).execute
expect(users).to contain_exactly(user, normal_user, external_user, omniauth_user, internal_user, admin_user, project_bot)
expect(users).to contain_exactly(user, normal_user, external_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot)
end
it 'orders returned results' do
users = described_class.new(user, sort: 'id_asc').execute
expect(users).to eq([normal_user, admin_user, external_user, omniauth_user, internal_user, project_bot, user])
expect(users).to eq([normal_user, admin_user, external_user, unconfirmed_user, omniauth_user, internal_user, project_bot, user])
end
it 'does not filter by admins' do
users = described_class.new(user, admins: true).execute
expect(users).to contain_exactly(user, normal_user, external_user, admin_user, omniauth_user, internal_user, project_bot)
expect(users).to contain_exactly(user, normal_user, external_user, admin_user, unconfirmed_user, omniauth_user, internal_user, project_bot)
end
end

View File

@ -8,6 +8,9 @@ import {
LARGE_AVATAR_PX,
PROJECTS_CATEGORY,
SMALL_AVATAR_PX,
ISSUES_CATEGORY,
MERGE_REQUEST_CATEGORY,
RECENT_EPICS_CATEGORY,
} from '~/header_search/constants';
import {
MOCK_GROUPED_AUTOCOMPLETE_OPTIONS,
@ -50,7 +53,12 @@ describe('HeaderSearchAutocompleteItems', () => {
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findGlDropdownDividers = () => wrapper.findAllComponents(GlDropdownDivider);
const findFirstDropdownItem = () => findDropdownItems().at(0);
const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => w.text());
const findDropdownItemTitles = () =>
findDropdownItems().wrappers.map((w) => w.findAll('span').at(1).text());
const findDropdownItemSubTitles = () =>
findDropdownItems()
.wrappers.filter((w) => w.findAll('span').length > 2)
.map((w) => w.findAll('span').at(2).text());
const findDropdownItemLinks = () => findDropdownItems().wrappers.map((w) => w.attributes('href'));
const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findGlAvatar = () => wrapper.findComponent(GlAvatar);
@ -95,10 +103,17 @@ describe('HeaderSearchAutocompleteItems', () => {
});
it('renders titles correctly', () => {
const expectedTitles = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.map((o) => o.label);
const expectedTitles = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.map((o) => o.value || o.label);
expect(findDropdownItemTitles()).toStrictEqual(expectedTitles);
});
it('renders sub-titles correctly', () => {
const expectedSubTitles = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.filter((o) => o.value).map(
(o) => o.label,
);
expect(findDropdownItemSubTitles()).toStrictEqual(expectedSubTitles);
});
it('renders links correctly', () => {
const expectedLinks = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.map((o) => o.url);
expect(findDropdownItemLinks()).toStrictEqual(expectedLinks);
@ -106,15 +121,30 @@ describe('HeaderSearchAutocompleteItems', () => {
});
describe.each`
item | showAvatar | avatarSize
${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)}
${{ data: [{ category: GROUPS_CATEGORY, avatar_url: '/123' }] }} | ${true} | ${String(LARGE_AVATAR_PX)}
${{ data: [{ category: 'Help', avatar_url: '' }] }} | ${true} | ${String(SMALL_AVATAR_PX)}
${{ data: [{ category: 'Settings' }] }} | ${false} | ${false}
`('GlAvatar', ({ item, showAvatar, avatarSize }) => {
item | showAvatar | avatarSize | searchContext | entityId | entityName
${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ project: { id: 29 } }} | ${'29'} | ${''}
${{ data: [{ category: GROUPS_CATEGORY, avatar_url: '/123' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ group: { id: 12 } }} | ${'12'} | ${''}
${{ data: [{ category: 'Help', avatar_url: '' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'0'} | ${''}
${{ data: [{ category: 'Settings' }] }} | ${false} | ${false} | ${null} | ${false} | ${false}
${{ data: [{ category: GROUPS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ group: { id: 1, name: 'test1' } }} | ${'1'} | ${'test1'}
${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ project: { id: 2, name: 'test2' } }} | ${'2'} | ${'test2'}
${{ data: [{ category: ISSUES_CATEGORY, avatar_url: null }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 3, name: 'test3' } }} | ${'3'} | ${'test3'}
${{ data: [{ category: MERGE_REQUEST_CATEGORY, avatar_url: null }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 4, name: 'test4' } }} | ${'4'} | ${'test4'}
${{ data: [{ category: RECENT_EPICS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ group: { id: 5, name: 'test5' } }} | ${'5'} | ${'test5'}
${{ data: [{ category: GROUPS_CATEGORY, avatar_url: null, group_id: 6, group_name: 'test6' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${null} | ${'6'} | ${'test6'}
${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null, project_id: 7, project_name: 'test7' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${null} | ${'7'} | ${'test7'}
${{ data: [{ category: ISSUES_CATEGORY, avatar_url: null, project_id: 8, project_name: 'test8' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'8'} | ${'test8'}
${{ data: [{ category: MERGE_REQUEST_CATEGORY, avatar_url: null, project_id: 9, project_name: 'test9' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'9'} | ${'test9'}
${{ data: [{ category: RECENT_EPICS_CATEGORY, avatar_url: null, group_id: 10, group_name: 'test10' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'10'} | ${'test10'}
${{ data: [{ category: GROUPS_CATEGORY, avatar_url: null, group_id: 11, group_name: 'test11' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ group: { id: 1, name: 'test1' } }} | ${'11'} | ${'test11'}
${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null, project_id: 12, project_name: 'test12' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ project: { id: 2, name: 'test2' } }} | ${'12'} | ${'test12'}
${{ data: [{ category: ISSUES_CATEGORY, avatar_url: null, project_id: 13, project_name: 'test13' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 3, name: 'test3' } }} | ${'13'} | ${'test13'}
${{ data: [{ category: MERGE_REQUEST_CATEGORY, avatar_url: null, project_id: 14, project_name: 'test14' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 4, name: 'test4' } }} | ${'14'} | ${'test14'}
${{ data: [{ category: RECENT_EPICS_CATEGORY, avatar_url: null, group_id: 15, group_name: 'test15' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ group: { id: 5, name: 'test5' } }} | ${'15'} | ${'test15'}
`('GlAvatar', ({ item, showAvatar, avatarSize, searchContext, entityId, entityName }) => {
describe(`when category is ${item.data[0].category} and avatar_url is ${item.data[0].avatar_url}`, () => {
beforeEach(() => {
createComponent({}, { autocompleteGroupedSearchOptions: () => [item] });
createComponent({ searchContext }, { autocompleteGroupedSearchOptions: () => [item] });
});
it(`should${showAvatar ? '' : ' not'} render`, () => {
@ -124,6 +154,16 @@ describe('HeaderSearchAutocompleteItems', () => {
it(`should set avatarSize to ${avatarSize}`, () => {
expect(findGlAvatar().exists() && findGlAvatar().attributes('size')).toBe(avatarSize);
});
it(`should set avatar entityId to ${entityId}`, () => {
expect(findGlAvatar().exists() && findGlAvatar().attributes('entityid')).toBe(entityId);
});
it(`should set avatar entityName to ${entityName}`, () => {
expect(findGlAvatar().exists() && findGlAvatar().attributes('entityname')).toBe(
entityName,
);
});
});
});
});

View File

@ -96,19 +96,22 @@ export const MOCK_AUTOCOMPLETE_OPTIONS_RES = [
{
category: 'Projects',
id: 1,
label: 'MockProject1',
label: 'Gitlab Org / MockProject1',
value: 'MockProject1',
url: 'project/1',
},
{
category: 'Groups',
id: 1,
label: 'MockGroup1',
label: 'Gitlab Org / MockGroup1',
value: 'MockGroup1',
url: 'group/1',
},
{
category: 'Projects',
id: 2,
label: 'MockProject2',
label: 'Gitlab Org / MockProject2',
value: 'MockProject2',
url: 'project/2',
},
{
@ -123,21 +126,24 @@ export const MOCK_AUTOCOMPLETE_OPTIONS = [
category: 'Projects',
html_id: 'autocomplete-Projects-0',
id: 1,
label: 'MockProject1',
label: 'Gitlab Org / MockProject1',
value: 'MockProject1',
url: 'project/1',
},
{
category: 'Groups',
html_id: 'autocomplete-Groups-1',
id: 1,
label: 'MockGroup1',
label: 'Gitlab Org / MockGroup1',
value: 'MockGroup1',
url: 'group/1',
},
{
category: 'Projects',
html_id: 'autocomplete-Projects-2',
id: 2,
label: 'MockProject2',
label: 'Gitlab Org / MockProject2',
value: 'MockProject2',
url: 'project/2',
},
{
@ -157,7 +163,8 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
html_id: 'autocomplete-Projects-0',
id: 1,
label: 'MockProject1',
label: 'Gitlab Org / MockProject1',
value: 'MockProject1',
url: 'project/1',
},
{
@ -165,7 +172,8 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
html_id: 'autocomplete-Projects-2',
id: 2,
label: 'MockProject2',
label: 'Gitlab Org / MockProject2',
value: 'MockProject2',
url: 'project/2',
},
],
@ -178,7 +186,8 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
html_id: 'autocomplete-Groups-1',
id: 1,
label: 'MockGroup1',
label: 'Gitlab Org / MockGroup1',
value: 'MockGroup1',
url: 'group/1',
},
],
@ -202,21 +211,24 @@ export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [
category: 'Projects',
html_id: 'autocomplete-Projects-0',
id: 1,
label: 'MockProject1',
label: 'Gitlab Org / MockProject1',
value: 'MockProject1',
url: 'project/1',
},
{
category: 'Projects',
html_id: 'autocomplete-Projects-2',
id: 2,
label: 'MockProject2',
label: 'Gitlab Org / MockProject2',
value: 'MockProject2',
url: 'project/2',
},
{
category: 'Groups',
html_id: 'autocomplete-Groups-1',
id: 1,
label: 'MockGroup1',
label: 'Gitlab Org / MockGroup1',
value: 'MockGroup1',
url: 'group/1',
},
{

View File

@ -72,7 +72,7 @@ describe('GroupsListItem', () => {
expect(addSubscriptionSpy).toHaveBeenCalledWith(mockSubscriptionPath, mockGroup1.full_path);
expect(persistAlert).toHaveBeenCalledWith({
linkUrl: '/help/integration/jira_development_panel.html#usage',
linkUrl: '/help/integration/jira_development_panel.html#use-the-integration',
message:
'You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}',
title: 'Namespace successfully linked',

View File

@ -151,5 +151,46 @@ RSpec.describe Ci::PipelinesHelper do
end
end
end
describe 'the `registration_token` attribute' do
subject { data[:registration_token] }
describe 'when the project is eligible for the `ios_specific_templates` experiment' do
let_it_be(:project) { create(:project, :auto_devops_disabled) }
let_it_be(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
project.add_developer(user)
create(:project_setting, project: project, target_platforms: %w(ios))
end
context 'when the `ios_specific_templates` experiment variant is control' do
before do
stub_experiments(ios_specific_templates: :control)
end
it { is_expected.to be_nil }
end
context 'when the `ios_specific_templates` experiment variant is candidate' do
before do
stub_experiments(ios_specific_templates: :candidate)
end
context 'when the user cannot register project runners' do
before do
allow(helper).to receive(:can?).with(user, :register_project_runners, project).and_return(false)
end
it { is_expected.to be_nil }
end
context 'when the user can register project runners' do
it { is_expected.to eq(project.runners_token) }
end
end
end
end
end
end

View File

@ -71,7 +71,7 @@ RSpec.describe SearchHelper do
create(:group).add_owner(user)
result = search_autocomplete_opts("gro").first
expect(result.keys).to match_array(%i[category id label url avatar_url])
expect(result.keys).to match_array(%i[category id value label url avatar_url])
end
it 'includes the users recently viewed issues', :aggregate_failures do

View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe UpdatePagesOnboardingState do
let(:migration) { described_class.new }
let!(:namespaces) { table(:namespaces) }
let!(:projects) { table(:projects) }
let!(:project_pages_metadata) { table(:project_pages_metadata) }
let!(:namespace1) { namespaces.create!(name: 'foo', path: 'foo') }
let!(:namespace2) { namespaces.create!(name: 'bar', path: 'bar') }
let!(:project1) { projects.create!(namespace_id: namespace1.id) }
let!(:project2) { projects.create!(namespace_id: namespace2.id) }
let!(:pages_metadata1) do
project_pages_metadata.create!(
project_id: project1.id,
deployed: true,
onboarding_complete: false
)
end
let!(:pages_metadata2) do
project_pages_metadata.create!(
project_id: project2.id,
deployed: false,
onboarding_complete: false
)
end
describe '#up' do
before do
migration.up
end
it 'sets the onboarding_complete attribute to the value of deployed' do
expect(pages_metadata1.reload.onboarding_complete).to eq(true)
expect(pages_metadata2.reload.onboarding_complete).to eq(false)
end
end
describe '#down' do
before do
migration.up
migration.down
end
it 'sets all onboarding_complete attributes to false' do
expect(pages_metadata1.reload.onboarding_complete).to eq(false)
expect(pages_metadata2.reload.onboarding_complete).to eq(false)
end
end
end

View File

@ -2293,6 +2293,44 @@ RSpec.describe Project, factory_default: :keep do
end
end
describe '#pages_show_onboarding?' do
let(:project) { create(:project) }
subject { project.pages_show_onboarding? }
context "if there is no metadata" do
it { is_expected.to be_truthy }
end
context 'if onboarding is complete' do
before do
project.pages_metadatum.update_column(:onboarding_complete, true)
end
it { is_expected.to be_falsey }
end
context 'if there is metadata, but onboarding is not complete' do
before do
project.pages_metadatum.update_column(:onboarding_complete, false)
end
it { is_expected.to be_truthy }
end
# During migration, the onboarding_complete property can still be false,
# but will be updated later. To account for that case, pages_show_onboarding?
# should return false if `deployed` is true.
context "will return false if pages is deployed even if onboarding_complete is false" do
before do
project.pages_metadatum.update_column(:onboarding_complete, false)
project.pages_metadatum.update_column(:deployed, true)
end
it { is_expected.to be_falsey }
end
end
describe '#pages_deployed?' do
let(:project) { create(:project) }
@ -6626,6 +6664,25 @@ RSpec.describe Project, factory_default: :keep do
end
end
describe '#mark_pages_onboarding_complete' do
let(:project) { create(:project) }
it "creates new record and sets onboarding_complete to true if none exists yet" do
project.mark_pages_onboarding_complete
expect(project.pages_metadatum.reload.onboarding_complete).to eq(true)
end
it "overrides an existing setting" do
pages_metadatum = project.pages_metadatum
pages_metadatum.update!(onboarding_complete: false)
expect do
project.mark_pages_onboarding_complete
end.to change { pages_metadatum.reload.onboarding_complete }.from(false).to(true)
end
end
describe '#mark_pages_as_deployed' do
let(:project) { create(:project) }

View File

@ -4988,17 +4988,36 @@ RSpec.describe User do
end
describe '#attention_requested_open_merge_requests_count' do
it 'returns number of open merge requests from non-archived projects' do
user = create(:user)
project = create(:project, :public)
archived_project = create(:project, :public, :archived)
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:archived_project) { create(:project, :public, :archived) }
before do
create(:merge_request, source_project: project, author: user, reviewers: [user])
create(:merge_request, :closed, source_project: project, author: user, reviewers: [user])
create(:merge_request, source_project: archived_project, author: user, reviewers: [user])
end
it 'returns number of open merge requests from non-archived projects' do
expect(Rails.cache).not_to receive(:fetch)
expect(user.attention_requested_open_merge_requests_count(force: true)).to eq 1
end
context 'when uncached_mr_attention_requests_count is disabled' do
before do
stub_feature_flags(uncached_mr_attention_requests_count: false)
end
it 'fetches from cache' do
expect(Rails.cache).to receive(:fetch).with(
user.attention_request_cache_key,
force: false,
expires_in: described_class::COUNT_CACHE_VALIDITY_PERIOD
).and_call_original
expect(user.attention_requested_open_merge_requests_count).to eq 1
end
end
end
describe '#assigned_open_issues_count' do
@ -6748,9 +6767,9 @@ RSpec.describe User do
let_it_be(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
let_it_be(:internal_user) { User.alert_bot.tap { |u| u.confirm } }
it 'does not return blocked, banned or unconfirmed users' do
it 'does not return blocked or banned users' do
expect(described_class.without_forbidden_states).to match_array([
normal_user, admin_user, external_user, omniauth_user, internal_user
normal_user, admin_user, external_user, unconfirmed_user, omniauth_user, internal_user
])
end
end

View File

@ -260,6 +260,29 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache do
expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
end
end
context 'applies correct scope when throttling' do
before do
stub_application_setting(project_download_export_limit: 1)
end
it 'throttles downloads within same namespaces' do
# simulate prior request to the same namespace, which increments the rate limit counter for that scope
Gitlab::ApplicationRateLimiter.throttled?(:project_download_export, scope: [user, project_finished.namespace])
get api(download_path_finished, user)
expect(response).to have_gitlab_http_status(:too_many_requests)
end
it 'allows downloads from different namespaces' do
# simulate prior request to a different namespace, which increments the rate limit counter for that scope
Gitlab::ApplicationRateLimiter.throttled?(:project_download_export,
scope: [user, create(:project, :with_export).namespace])
get api(download_path_finished, user)
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'when user is a maintainer' do

View File

@ -29,15 +29,15 @@ RSpec.shared_examples 'resource access tokens creation' do |resource_type|
click_on '1'
# Scopes
check 'api'
check 'read_api'
check 'read_repository'
click_on "Create #{resource_type} access token"
expect(active_resource_access_tokens).to have_text(name)
expect(active_resource_access_tokens).to have_text('in')
expect(active_resource_access_tokens).to have_text('api')
expect(active_resource_access_tokens).to have_text('read_api')
expect(active_resource_access_tokens).to have_text('read_repository')
expect(active_resource_access_tokens).to have_text('Maintainer')
expect(created_resource_access_token).not_to be_empty
end