Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9b762f50fe
commit
f55c925355
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.56.1
|
||||
1.57.0
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -27,5 +27,3 @@ class MergeRequestSerializer < BaseSerializer
|
|||
super(merge_request, opts, entity)
|
||||
end
|
||||
end
|
||||
|
||||
MergeRequestSerializer.prepend_mod_with('MergeRequestSerializer')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' } }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" } }
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
d9a9d143ff553cbad5eb32a908370133549850f10b27b30eb6a1bde686054c45
|
|
@ -0,0 +1 @@
|
|||
94dbae9bbfb2da49a0d20b674e15f457c613c8ba0612603dd8fe9ac5699160d1
|
|
@ -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 (
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 |
|
@ -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)
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 } }
|
||||
|
|
|
@ -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]}")
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue