From f55c9253556dd5dab700d76fa88aa04891343100 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 14 Apr 2022 18:08:29 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/docs.gitlab-ci.yml | 2 +- .gitlab/ci/review-apps/qa.gitlab-ci.yml | 62 +++++-------- .gitlab/ci/rules.gitlab-ci.yml | 12 +-- GITLAB_PAGES_VERSION | 2 +- .../header_search_autocomplete_items.vue | 66 ++++++++++++-- .../javascripts/header_search/constants.js | 6 ++ .../add_namespace_modal/groups_list_item.vue | 4 +- .../components/cells/runner_summary_cell.vue | 3 +- app/controllers/groups_controller.rb | 6 +- .../ios_specific_templates_experiment.rb | 30 +++++++ app/helpers/ci/pipelines_helper.rb | 6 ++ app/helpers/search_helper.rb | 9 +- app/models/project.rb | 8 ++ app/models/user.rb | 8 +- app/serializers/merge_request_serializer.rb | 2 - app/services/jira/requests/base.rb | 2 +- .../_git_lfs_limits.html.haml | 11 +-- app/views/admin/applications/_form.html.haml | 4 +- app/views/admin/groups/_form.html.haml | 2 +- .../groups/_group_admin_settings.html.haml | 23 ++--- .../groups/settings/_permissions.html.haml | 2 + .../shared/_allow_request_access.html.haml | 3 - .../shared/access_tokens/_form.html.haml | 4 +- .../deploy_keys/_project_group_form.html.haml | 10 +-- .../doorkeeper/applications/_form.html.haml | 23 +++-- .../shared/tokens/_scopes_form.html.haml | 15 ++-- .../group_wiki_settings_toggle.yml | 8 ++ .../uncached_mr_attention_requests_count.yml | 8 ++ .../experiment/ios_specific_templates.yml | 8 ++ ...inct_user_id_from_deployment_approvals.yml | 21 +++++ ...inct_user_id_from_deployment_approvals.yml | 21 +++++ data/removals/15_0/15-0-rerequest-review.yml | 16 ---- ...220318141037_add_pages_onboarding_state.rb | 11 +++ ...322132242_update_pages_onboarding_state.rb | 32 +++++++ db/schema_migrations/20220318141037 | 1 + db/schema_migrations/20220322132242 | 1 + db/structure.sql | 3 +- doc/administration/integration/plantuml.md | 2 +- doc/api/users.md | 25 +++++- .../documentation/styleguide/index.md | 82 +++++++++--------- .../incident_management/incidents.md | 2 +- doc/update/removals.md | 6 -- .../monitoring/background_migrations.md | 9 ++ .../img/gitlab_import_history_page_v14_10.png | Bin 0 -> 103923 bytes doc/user/project/import/index.md | 24 +++++ lib/api/project_export.rb | 2 +- .../Jobs/Secret-Detection.gitlab-ci.yml | 9 +- locale/gitlab.pot | 51 +++-------- qa/qa/page/component/access_tokens.rb | 4 +- qa/qa/resource/runner.rb | 27 +++++- qa/qa/service/docker_run/base.rb | 6 ++ qa/qa/service/docker_run/gitlab_runner.rb | 6 ++ .../api/1_manage/import_github_repo_spec.rb | 4 +- .../api/4_verify/remove_runner_spec.rb | 10 +-- .../project/import_github_repo_spec.rb | 4 +- .../4_verify/runner/register_runner_spec.rb | 2 - .../service/docker_run/gitlab_runner_spec.rb | 1 + scripts/lint-doc.sh | 2 +- .../ios_specific_templates_experiment_spec.rb | 62 +++++++++++++ .../admin_users_impersonation_tokens_spec.rb | 4 +- .../profiles/personal_access_tokens_spec.rb | 4 +- spec/finders/users_finder_spec.rb | 12 +-- .../header_search_autocomplete_items_spec.js | 58 +++++++++++-- spec/frontend/header_search/mock_data.js | 36 +++++--- .../groups_list_item_spec.js | 2 +- spec/helpers/ci/pipelines_helper_spec.rb | 41 +++++++++ spec/helpers/search_helper_spec.rb | 2 +- ...2242_update_pages_onboarding_state_spec.rb | 53 +++++++++++ spec/models/project_spec.rb | 57 ++++++++++++ spec/models/user_spec.rb | 31 +++++-- spec/requests/api/project_export_spec.rb | 23 +++++ .../features/access_tokens_shared_examples.rb | 4 +- 72 files changed, 826 insertions(+), 296 deletions(-) create mode 100644 app/experiments/ios_specific_templates_experiment.rb create mode 100644 config/feature_flags/development/group_wiki_settings_toggle.yml create mode 100644 config/feature_flags/development/uncached_mr_attention_requests_count.yml create mode 100644 config/feature_flags/experiment/ios_specific_templates.yml create mode 100644 config/metrics/counts_28d/20220407183012_count_distinct_user_id_from_deployment_approvals.yml create mode 100644 config/metrics/counts_all/20220407183012_count_distinct_user_id_from_deployment_approvals.yml delete mode 100644 data/removals/15_0/15-0-rerequest-review.yml create mode 100644 db/migrate/20220318141037_add_pages_onboarding_state.rb create mode 100644 db/post_migrate/20220322132242_update_pages_onboarding_state.rb create mode 100644 db/schema_migrations/20220318141037 create mode 100644 db/schema_migrations/20220322132242 create mode 100644 doc/user/project/import/img/gitlab_import_history_page_v14_10.png create mode 100644 spec/experiments/ios_specific_templates_experiment_spec.rb create mode 100644 spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml index 8b7691045cb..217da6506bf 100644 --- a/.gitlab/ci/docs.gitlab-ci.yml +++ b/.gitlab/ci/docs.gitlab-ci.yml @@ -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: diff --git a/.gitlab/ci/review-apps/qa.gitlab-ci.yml b/.gitlab/ci/review-apps/qa.gitlab-ci.yml index a955096992f..d2192a7511a 100644 --- a/.gitlab/ci/review-apps/qa.gitlab-ci.yml +++ b/.gitlab/ci/review-apps/qa.gitlab-ci.yml @@ -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 diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 8824c333f96..2494fea94a6 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -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 diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 43c989b5531..373aea97570 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.56.1 +1.57.0 diff --git a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue index 4e65d2d5055..025c48f355d 100644 --- a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue +++ b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue @@ -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 { - + + + + diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js index 9cb08605444..045a552efb0 100644 --- a/app/assets/javascripts/header_search/constants.js +++ b/app/assets/javascripts/header_search/constants.js @@ -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; diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item.vue b/app/assets/javascripts/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item.vue index 005c3bcd0e3..1fc40e5c0d6 100644 --- a/app/assets/javascripts/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item.vue +++ b/app/assets/javascripts/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item.vue @@ -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', }); diff --git a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue index 754908c9385..1eb383a1904 100644 --- a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue +++ b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue @@ -60,7 +60,8 @@ export default { {{ description }} - {{ __('IP Address') }} {{ ipAddress }} + {{ __('IP Address') }} + {{ ipAddress }} diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 93e56867780..e5d793b1099 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -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') diff --git a/app/experiments/ios_specific_templates_experiment.rb b/app/experiments/ios_specific_templates_experiment.rb new file mode 100644 index 00000000000..1731fa87be8 --- /dev/null +++ b/app/experiments/ios_specific_templates_experiment.rb @@ -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 diff --git a/app/helpers/ci/pipelines_helper.rb b/app/helpers/ci/pipelines_helper.rb index 8d2f83409be..70d2a4fafd1 100644 --- a/app/helpers/ci/pipelines_helper.rb +++ b/app/helpers/ci/pipelines_helper.rb @@ -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 diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 8586e93c88f..f8bfc74b344 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -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 diff --git a/app/models/project.rb b/app/models/project.rb index c15c3a65e74..6e5c694cb9c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index 4610b5a9398..68c1b1c5549 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb index e8fc18e6cf3..9fd50c8c51d 100644 --- a/app/serializers/merge_request_serializer.rb +++ b/app/serializers/merge_request_serializer.rb @@ -27,5 +27,3 @@ class MergeRequestSerializer < BaseSerializer super(merge_request, opts, entity) end end - -MergeRequestSerializer.prepend_mod_with('MergeRequestSerializer') diff --git a/app/services/jira/requests/base.rb b/app/services/jira/requests/base.rb index a16f8bbd367..3e15d47e8af 100644 --- a/app/services/jira/requests/base.rb +++ b/app/services/jira/requests/base.rb @@ -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') ''.html_safe % { url: auth_docs_link_url } end diff --git a/app/views/admin/application_settings/_git_lfs_limits.html.haml b/app/views/admin/application_settings/_git_lfs_limits.html.haml index de5a2ceaa3d..b8970a5bcf1 100644 --- a/app/views/admin/application_settings/_git_lfs_limits.html.haml +++ b/app/views/admin/application_settings/_git_lfs_limits.html.haml @@ -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' diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml index a1990ad5750..925b3681298 100644 --- a/app/views/admin/applications/_form.html.haml +++ b/app/views/admin/applications/_form.html.haml @@ -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" diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index 5f12cd048b3..8ac6f63cdfb 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -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 diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml index ab6861b5f24..785ca71b371 100644 --- a/app/views/groups/_group_admin_settings.html.haml +++ b/app/views/groups/_group_admin_settings.html.haml @@ -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 diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index dd62c9e118d..1a2f770cd59 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -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 diff --git a/app/views/shared/_allow_request_access.html.haml b/app/views/shared/_allow_request_access.html.haml index ca09fd39dc1..608a0ca37d9 100644 --- a/app/views/shared/_allow_request_access.html.haml +++ b/app/views/shared/_allow_request_access.html.haml @@ -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' } } diff --git a/app/views/shared/access_tokens/_form.html.haml b/app/views/shared/access_tokens/_form.html.haml index 0b68cfe65e5..d4106ba4e5d 100644 --- a/app/views/shared/access_tokens/_form.html.haml +++ b/app/views/shared/access_tokens/_form.html.haml @@ -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 diff --git a/app/views/shared/deploy_keys/_project_group_form.html.haml b/app/views/shared/deploy_keys/_project_group_form.html.haml index 8da48a7936a..c9edf09b350 100644 --- a/app/views/shared/deploy_keys/_project_group_form.html.haml +++ b/app/views/shared/deploy_keys/_project_group_form.html.haml @@ -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"} diff --git a/app/views/shared/doorkeeper/applications/_form.html.haml b/app/views/shared/doorkeeper/applications/_form.html.haml index adfd7ea98b7..c1650405776 100644 --- a/app/views/shared/doorkeeper/applications/_form.html.haml +++ b/app/views/shared/doorkeeper/applications/_form.html.haml @@ -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" diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml index 33e95446bd7..010376464f1 100644 --- a/app/views/shared/tokens/_scopes_form.html.haml +++ b/app/views/shared/tokens/_scopes_form.html.haml @@ -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" } } diff --git a/config/feature_flags/development/group_wiki_settings_toggle.yml b/config/feature_flags/development/group_wiki_settings_toggle.yml new file mode 100644 index 00000000000..083453a6944 --- /dev/null +++ b/config/feature_flags/development/group_wiki_settings_toggle.yml @@ -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 diff --git a/config/feature_flags/development/uncached_mr_attention_requests_count.yml b/config/feature_flags/development/uncached_mr_attention_requests_count.yml new file mode 100644 index 00000000000..239490ab1c2 --- /dev/null +++ b/config/feature_flags/development/uncached_mr_attention_requests_count.yml @@ -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 diff --git a/config/feature_flags/experiment/ios_specific_templates.yml b/config/feature_flags/experiment/ios_specific_templates.yml new file mode 100644 index 00000000000..0af80e7a5bb --- /dev/null +++ b/config/feature_flags/experiment/ios_specific_templates.yml @@ -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 diff --git a/config/metrics/counts_28d/20220407183012_count_distinct_user_id_from_deployment_approvals.yml b/config/metrics/counts_28d/20220407183012_count_distinct_user_id_from_deployment_approvals.yml new file mode 100644 index 00000000000..7b594efbf19 --- /dev/null +++ b/config/metrics/counts_28d/20220407183012_count_distinct_user_id_from_deployment_approvals.yml @@ -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 diff --git a/config/metrics/counts_all/20220407183012_count_distinct_user_id_from_deployment_approvals.yml b/config/metrics/counts_all/20220407183012_count_distinct_user_id_from_deployment_approvals.yml new file mode 100644 index 00000000000..902347d9f0b --- /dev/null +++ b/config/metrics/counts_all/20220407183012_count_distinct_user_id_from_deployment_approvals.yml @@ -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 diff --git a/data/removals/15_0/15-0-rerequest-review.yml b/data/removals/15_0/15-0-rerequest-review.yml deleted file mode 100644 index e692df05670..00000000000 --- a/data/removals/15_0/15-0-rerequest-review.yml +++ /dev/null @@ -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 diff --git a/db/migrate/20220318141037_add_pages_onboarding_state.rb b/db/migrate/20220318141037_add_pages_onboarding_state.rb new file mode 100644 index 00000000000..e320bee63c4 --- /dev/null +++ b/db/migrate/20220318141037_add_pages_onboarding_state.rb @@ -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 diff --git a/db/post_migrate/20220322132242_update_pages_onboarding_state.rb b/db/post_migrate/20220322132242_update_pages_onboarding_state.rb new file mode 100644 index 00000000000..896ab78a266 --- /dev/null +++ b/db/post_migrate/20220322132242_update_pages_onboarding_state.rb @@ -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 diff --git a/db/schema_migrations/20220318141037 b/db/schema_migrations/20220318141037 new file mode 100644 index 00000000000..e2451a42392 --- /dev/null +++ b/db/schema_migrations/20220318141037 @@ -0,0 +1 @@ +d9a9d143ff553cbad5eb32a908370133549850f10b27b30eb6a1bde686054c45 \ No newline at end of file diff --git a/db/schema_migrations/20220322132242 b/db/schema_migrations/20220322132242 new file mode 100644 index 00000000000..f21f62874a7 --- /dev/null +++ b/db/schema_migrations/20220322132242 @@ -0,0 +1 @@ +94dbae9bbfb2da49a0d20b674e15f457c613c8ba0612603dd8fe9ac5699160d1 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 9535e7deab0..771fce97e59 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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 ( diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md index 5a9699b3a39..cdf6d48ad41 100644 --- a/doc/administration/integration/plantuml.md +++ b/doc/administration/integration/plantuml.md @@ -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: diff --git a/doc/api/users.md b/doc/api/users.md index e9fe6bf7dfe..7b4962735e6 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -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 } ``` diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index 4d74dbb2616..921af68d908 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -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 + +# Feature A (DEPRECATED) **(ALL TIERS)** + + +## 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]() in GitLab 11.3. Replaced by [meaningful text](). +``` + +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 - -# Feature A (DEPRECATED) **(ALL TIERS)** - - -## 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]() in GitLab 11.3. Replaced by [meaningful text](). -``` - -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 diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md index 1a1a607e76f..b18d250f717 100644 --- a/doc/operations/incident_management/incidents.md +++ b/doc/operations/incident_management/incidents.md @@ -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. diff --git a/doc/update/removals.md b/doc/update/removals.md index 80f5006982e..7e2b4f84fa1 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -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 diff --git a/doc/user/admin_area/monitoring/background_migrations.md b/doc/user/admin_area/monitoring/background_migrations.md index 260a8515a1a..726827054da 100644 --- a/doc/user/admin_area/monitoring/background_migrations.md +++ b/doc/user/admin_area/monitoring/background_migrations.md @@ -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. diff --git a/doc/user/project/import/img/gitlab_import_history_page_v14_10.png b/doc/user/project/import/img/gitlab_import_history_page_v14_10.png new file mode 100644 index 0000000000000000000000000000000000000000..c93b5ed2b278d6db45bc08665ec3cfe6420bbcdf GIT binary patch literal 103923 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μ_FoIDc{GTDpm3lzC1AqQN)m1BBPF!_*CX=+=Nm?t18_yf4$Zg+7G2&Uc)UgXZs5r5CsA?E!AW8W~KUv(4 z#>a1S^AX0wD-Clr%DBv_?IOd^nEW9#$!d$W->`F4hfmvDQ@CjsVZ*g*79O6}+FaiI z9^7U2YeC#N^uPF?|1cSM)_<#y?X#T7MQ%Uq8f@uuPk1Acb+cNge%I;fTi;amW!Hjv zvdzaQ8od7Zt+zze<`mpow(BfAl=qWhDJ_-CC$sW{i7$G1UrDabV^}fT+tVZ`NnHYv zB$oPtkXtB<#E@!i*KgKL>Dz}8M_AsoD)L8+{%zHn;PdLn1`x-)!E^`O|`~D-W zdeSO6@WYdbxQRDms(;EB>07XuK1O&e^4BeSznYV(Z41sKvYXu*QgfFpyG)y1>W8g8 zWnX`Czjc)+`SQCDOC8VmvR+A|&&@5bA04M;Es=?KMs3RTB1@;l3@`Rvf!_?B@!K9?u3SH<=Du; z4P0kWORlZIw1))6wb5kz;rrqP%ri~?Gxx-kDYXXH#VC;s`%S+LRVocf^jn^FZ}I>S zxA)TZ!#&R)P$htn6-%N1onioU>eLIrIlTJM5HuL1Bo{Qfdloc#9E(KFdR~6!IT0`{ zfckY;R_V<0;ZFZ`6uf zcElU$pTFMX(z7#-WUlxbsCMZ2&%y-f`pe(!e}4wG)vbih<7b(drjPnci8ViK^-TVv zn^tJEN$h7q+NG)ONYu)_Tt4b0vhL}5)GDe#{UPvsf08?HCa+GK*3OWnf`KH<7{?CJ zJv(sMb%~)#0 zet((PlccUCvtwuz_I{i_We5_X9SN>|IolCrGigurt35tXz70?U^->FRO%7tKj8>Vg z-0Wi76C;n(;w)|>Fwj@x1UQ!0lAzgZd2od`hq&Y4bG)1`k#NJHs&J-TABlNb>PCu< zek}n1nIkxAK>;b>w-6Y-O4Ik-gcW7Zc=S-dJz1{-(F-gDK?=cWG8KnCiGdDtBbETJ zVAkyn7m2;aQk=|MY&Bcn?U3&>%5eW&+##FnsU98G^D~a88?msv(~RQXQ8mmrc1d2` zT%Jzfv34kibG)U0Nfx@_%X}rq-8@gnF<5NK|B@cz=#fs| zG4X>m_K79S+)CDeEEJO>lT;@RHDVE7wPV-PC1o>xRO=(!Gm_+OH2{q1LZ{?v^QdKbTjBUnWpnamu6ebr%d))v}t{~WhO^e zAhgE*SW2XebZ>MZ3~KxH-F}uQC}d_Zt0=D35$F3fw)5wmxIKK0X`>fz{kFUDNl~?` zsR-0MqmAfHhH8t$FbrS!i?y*rPd3jmqIvg8VZbWlG|8%hMPmc7y;ngqtJBoSMR)`g#I)9P75HSC=p9{J648%7%E27qHX=^f&c@|A~t5csR86_$kjfqTbmqKU>TvKlVlY z#1GG08GrHWE*Lincxdavd-C+tINJIBU%Ldl!y?U{ES@dEVzGT{jc>;Zgs0%0CS8o! zsRI}@9Bl*OZ80g6m|cn9Qp2yg+P_)E54FvfqTK4Sme`yi%Zv}?=tF^d#S%DeIB)Mg z+dTHQ!T)8rvSqK?%V{ml`m`@ay8f|HPfArr&h4gjO^URfw>xQ<(PC8OIGG=w0%PrC zFXq~|kkfrjoSAPqSp_aIcD>Ow^|l`n<@Ep*wi1|%43cRWT0PO*YmPYn@wy?%*=1z7 zO{@_S%Jq>DC=OAG0WL4u*=NrO$wZPf<nTP|u)7J#5!@Q0N0n=#Js4NBnhU}fTnuL5V@5A9v+hJ-(^Wp!iHitV9C+GWv`)@>U$CSsJEPIAM7Q%)FoMb zH;=qc#?4NDs;k}N&)R>y{T(6h+hI6u<7be4Tp!Z^>lDmnINT7^&ERvV?A`v4IC8m) zK>C*T$#Vg@Inf}HQ@7Nu!jAK~gQ^2qC5;y1Box0pNW;NsV)ApCTG-sa$aPB`g$2zS z))whGAMv2n5kuZBaw}rleR~YWKDIRuDXcnKt@=Feccbdr7?&@@?nT_(yjq$bIdfXdG4nBhpd>!iIrR1+9H*hBzXeM zq-HO@%$-?W>5bQgZ?uC|-$?v(OOwo@`sISh8z%HwZf&g3O8A+cNRUkYkRoYKMhYib zCdy4w^@I)hszbuV*kHO*3d9m)MNLq<$^FAN-^)VWq>fULOhTai3A??%Xv(^wi)bO} zaLul3$3i6NBzotJHPt_S%df>!2kxS;RXX~fS9%EfPEa+gU|KW|afBz)rIe+?#*T*9 z(=)L9>JO4U4eP(D)O@5+mOGYiKahCXwn+}?si)H1ya9rPtuLM;>8CHEwI!_`d7y#e z-2L}bTSxXS8Nv#P9`T2eV}B~RFK3?joglDroxwquK*DaRkW;9vV^)5z6m z5lRZ%f}}kNmX}-WwfQbC_5!1=;Kq{RkZI0K;mod;W$xNQb&*+dBOg-`%-LM>@D2It zr%JMYU8v&cyZhztrFgfmpP#4pqNnV0r6?!t>PYmRs^0xGvBQoRe$$t<_R;UC?LGe< ze<1v;-FU$tFt|IJ%H|qA`HuaxY7^==DQ#fF(mSAf*BVNRzPHhkK<=i9&_@}Awg8AvqiDU3f*5xY& z^f*;6BDRhCXpgW|`)6(NXwK;M9+#I?V>kYhiG0?$ZvLFnJ;nOFx!6l$m~m=VCoae- zkt0Q{z`U*|&X6A`v-nu~Xo4dpq<_+)u{rki-L%CB$H3S>=)2J8JlLZUW73R^AFU^M z3o@zbjf7sPWjAE72h2<5iub&HPB{X>w)HErh4J3#F5i@IL5pI0~X-%zOgeE?%tO!DNfUt*7lPy)~I#r@Nqe|q|N zW`Eyx4lm`()Bf}2|9|BF6~*3J4RQP)M^`s@qxWhS@W(e6Q?K`?uoXWcu&mYQ6}q~g z{W0AlBi61Yqh#86|D+!NT+irr{U*%(d#>h{&e6a3DYyZy>^B-V+i^HDT z+4`i#{?AR}53oYQu+ulf@^b3L|M08-cys6!85Z?)-ilYe`%m-z%^mEx2*KvfswgeA~X=%t&JDTEgKi#?SChApe!nEhSiIdt;f^8U)4 zE(s0k_yD(-V2_xe>>9MVwYed9_?AI!&5n_>j$md0lE*Zuf9Sb?rnQjY5jvX1{X)p+5vWiX)~+Sdj#7ZEi>O1IEcY@Bi%k!eTC?M z9u;|avg09c9C?E8F8qV3y9aEjZ$89^~ILB3U z&3yNXmWK)l2@0<`eP_N@Q_}S+nHWk^so$;~$@E(i<-PrIqAa6R&sPis z&_zzDanm@^gn8BLaAE+oVE1)2xeb|P+I32z8ADWfF7@;g?uX>aC$0kz9xXn{ncj}g z8g^;A(B%b>IhBa4HnW-u9oub&FZ)#-v5yzjN3$dyExz-rK36chY%6*o&aIgEFkZZ3 zzttXUaekY8hDV3AG1yUPK%89dRu$=3iTw_Crni`dzW#}CPZhg|S$YsSPeGvDBQ4@k zht8nR*ntF7%rVc>TGmr+4o6l7CE6&xz^r(_$xU-QRbj0W8z1|%nR*gdb=sk-VHJ%I z|8V<{`mo~O9ne`k=QS9>fMZ{EhON3uGj3O7=xu*8ARZRn*xW6s& z;{dP)8rFtKKNc%21q6be=7%QnelfF^dM&?LvADdJ@#*+UELy(WA{omZE$^q4*>j|} zwMjwGJ!MFj%k>6(Rp4zeb&Wn+K}we$(vant%Z(WftDD3uL!mk|0Nmz+o&G18nr@+ZA!|yf}nzuvP)qrLBa%wgso#E^chM zC4KrtGwBHjATNd&yw6v2Kq)lMjs;7~Z`aOl|BjA~Rk(@1JV8BYfVTsk@SxSM*G&o< zE-A9QZ_@ioJ)D<$LFcGXgA3?%t*ZWBKn;W@wGLV<+eG|>0gSswXeczS<>2x@!^{Oz zut0fUtp%y+{Z*5Fk#V@Ro{PU74MXX4D@+XV42r)H27p;Fs%C3s#vL^sQa#eXo>!;8 z@Vn(hgFC{E#YW~d#A7WjSs8n(tkqBerSRp0BE4Hx?GQDHPFxDA&I|E8XLe$e%-_a$Ah4G70sLw@RYMo|cPy?y} zOW&}ZusU%|1fH)r2+os_0<|xtP60P0axsO_?yXjy7$x3PC1jBtl?yVlM~01tXbk2Z zXE*W|bb5H?i5&85Ard~)EzClg{5&PZnJ&&H7qI=wCdR9f_KQ1uciCp{?&lOB4e^dQn3bYo-OQIA_c5E$#PrI z)gf+LS;m9GWO^&y;J4Xdt7T$>sDaLXLmRI3xb{;G zvFsP3HN_DhBSemPBP;ph|n7U<4>^M$OBc)XXE_}X9%3UIne)ac1Yky&vwvW=}F z8jS$))QgYyOf#WS7yf?6B~8{X%9R5cv8H3y0A}E6>Wv9l)i*ssyRWcR5sCzv)rcdl zu<;MsO+eiS$Kn>OHHJws%-jkdiB(hceWrQdLY@?4^49Sr+$-2y-A z9d?7@Y^Az|%_iCs(zXqu`8*GAQYTO=-9GiMD%lbzXnH+Nw0$hd=39J=#$>64(j&jp zB*?jU)~}?E(re=V2?oS)&ahftyD!$u;bg1C3HG+Lo@C^M@#YLyLCBkWI3P*EqtbW? zOO2&N2LZDmhM4Vi{Z=R~&{-kf2(KPlrp-Rp+%iM;q4o^fT`@TmGvS0Q5@%Qa)n#M2 zz~E0}@$B)P5%iK_%h%vbLZ{7~K%F5rg(18J4g z4EdWr`NL%je2s{&T1XYj@>hx|=Vv+<=as$IO1A#$w)x)YBpox)Z#iwf%aBU5@b$?@ zJsJ_`mSGIPPRqf_N3$KM6A^4s3p$I_w;{h@d#$CPeX^#VX4P#1dxRA=pb^tw3LW*1 zb(_w(FnKsMV&w59MGe*6qS>2CMhbf?{(V6lJK?fsNHbrX#QR_f5~%4PH#cm7&SB5r z;fbaGq`1CP*0>8U?w`Q8ba>(d#-+bSI;U33+cBsiuV1IBAqIW($2LEZ_AHU*{N`eGjja0 zc;*iR5Znm(uUP<`%(`jUxv1^cL`lsjeZ=O~&?h&4ht|ab^2iN;*T7}5k&D|%%C)yT{Haa#Is^4k#)(F}fy&D8~f0+cE z@>}1Sd0YR%6Qa3$n|g(2o&}S5pEX)iQr8hZ^+p^i%)-b64E4y z>B9kHLUT!a^Ft`7^mu?Vx7`AOWzQqwjs>f9j2+FdP^{`diPhMOVe^Y9Tppu|5r;-W z-|2LH2v~SZvoqn|M1HEw?#8#I-J{ML!imz@?!I3Zuy&MDm5Hmx(Dw=g%VsfFt-jqY zYc4~G76JE}?iE}5>p|>K0n&wDZ&oa(rNF8rZO(;tAQ6>oM(UiD2P(L)BQsV5)m0ELG znsgCQRXb1Vy}Q>SOKMu%5b8hs?F*T7GMOaf&h2-o!t?&SzS!h1CQaL~Hobg?#h83Q zKb5o=anu9#SV99(eRU7~5J}Rh5!4J2Z10-GUxV;;V0?!)Diu-8UUlJY$ow4ga$yH@ z8qEiLdx1JXA>zF}2tjhwiaD=Vn` z@T}GD8T>`BFMcRTHoxzTvx!jYm|U)Cg}ve#UjT`%YD?zK`fk+`h}A*;r-k9LRh8G- zW8I5E^6_W+b@vhQZUt;(+{S{nf5@+PSl3x(H?95a?s?Hsne$CA~1NQJ(>1ld~v(ws8NzH zaF#o@8BGmpLg!ktsRO`9sd`>|F4z-W>V4)gFhK@K#i)m zh45C=iy=4AoVERHe1_=J{M0DV4J3}?n?Zw3gl`pqO{izLg3{wswmmQjxTj|^E|`w> zc}Jj&YFBcpB~{=dw8Kqj%0JvhRxXV8oeqPlZQv&W1UQLEX{ zf7y{^3mt5|M;6E<-thPwEphgfcT!o(I^3iIlZeFDPi+W2gwG5S4`Q*_2wH;{t&0fHh$uE(+27 zE5&K9DJEu#U`kbh&$O9YXh+0~bmT3*rC&i>QC+uW5fJCR| z?H9E=B11@U(vtnpX=COOVm3a%DGrw+$;Ak_WXEz@ng@`Xi|^0(N(S||b-Ey{dc)+w z(q{$+NAXbzzRMJPky+I`WzXHWaSJ|=7VdSygVfzAR2WT+%x8bVeel2W6b*f!OzWGP zFU&KiwwBaAt+qbOFP2tQxhm39U9O;3gu@%`v87wE`EOs;vVRj21Z3FAzuyVQ zMl7+ZQfe^OM#8iYz+2;MZy?Gu*0Sy6o0RVDHam<-FReu4-Cxu5yB~T8$VK1R6UT-P ze_OSGF(arEmNun2BWTLaorGa@!ndp`+W}W`xOA*Xu#25_@HJaK0&P$Yu9pmVD>QvK zoD6Q9n6JaA?kp{_N-!8r<8c4Uvjab}o)7uCwhr00TikxGE@^$u7kO}un)!4YscH&l za*?2FE$yl7V9?Q+yY144nXd06U_Ck<6`1i{s&_t>kK%;_v6a(i!}+mA#6UA@FeX@{ z_Q8d-VrFg0y#GN=(8YIJY>s!{?V%&5%Q-QFw~pPS6`VH@Ho#RVZD?d2Yuq;KXhVc+ z?xS3=DWo_;RU!3s`w=_fP%E|1R^s8eXJ=W4zfG(`e)F{Ohffj~5}(So*4|=&&qL8} zRrT)qKF_7WX88js_gF801o*y7R|q9bY^KPedc-l#WyJ?V0@>2xOo zBoeUjxaDQOgKhD5L)u4-e1fac2Aj+@Cy{FM7J7C@4Mi*8A!(FDFeeg6<2aE07OY6H z|I?qJw4pJt}iKD z`!@!U@lFGlPkV+`vHKefMZ$vR(_YiP^!zKIri)5d|I`1 z1dtX|I2iqZ1Fc3I2Qi|3@4~YwXD`^NH-yf64ee2T{h7qhCbcA^xV? zZ`D|GwBJvQ<}WG;3A=?QNA)Ar>HZ=~Xa)tA95rxJl>R@<`0sabp8h6Bk$0+om#J4D z`#;Z^k|}ugbfR9*^^^Y!g=@HEagGKT>~whP41~1EAtxhmNrF zOA=uf*>#7%>#8dLaIrm))*vjaFsxba_@}-7_=Od}QibcYpns0w23mQOd|KZTi)U-&&dB8z;Qt zVWBr~o}cYSb4#?5g>tm6=v?PzQ7v{<;O*!I{Wzq#^$k0#1}RsN9FDREj>XB}t?TIj@pT^}0TKSX2B;YW=mJBKNAF#jS$2p`1 zAp{JwJ96H_`-NnLQMA*NzD$~c!&_V!gu^x$pFUtxo*_~6)0)5?M%tK6)klwGsMn`B zR|T*`XlGWBDTAc;TT7oY)odsMOapLQd|~Zqmiu3(O^YxgdALV(~92=4Cg1PktNjYC7@?jAxQxHm4r z-5Pg-JHdlnaCe94b3U87bAI2NHTV8itJbQ1W%sss@8_x7Je=nt)!c~KcdgT8DDeNm zgoz$rBX6|wDDrClijDd*vFbITsrlmcG-inO-B;43$FqvcD zPsEA9@M*VW8qLjdz>DV`0n=z5;;K>wZ?C^p-NLX)pQn(KAt1nf`AqglxV$Z1D_SRw z!}KcqrFiR9FaAV7xOyfe4r8z`MTrMi5G9wWS-FbIK6q(SMv_h@72>E|nE(gej>>dr1B+yX_K zKLS2CFXl3>C1VUaldJb%EKRi@S#1sNJS=p^ZWEr`_rmR-xw2GzsOg$2_o26`HRU%D=ZV*opTOt-yM&HKTdnX=xmO&D4$(F5A^DM{B1 z)!)BS^Td#_dtP%Y6_of*O#Li4)!X)LV5M4GYiZL?=B~8%7>me?=0Zd>IovXXiB#DC zIT|K;EDvOvR)-I%_O(E0eVc&5!}dL{Oi0~(QAPgZOrsxiz2w7GUD12~CJBGy^B3Tu zJ;z85{6Nbfixj;{dCKkA*iUXcu`iSb3hOhK-t@ZCT1L@Z zx)2hu%_2vU5&u>6W6hoz(>&&NvC3q4$*I7k*0p}pVVp?&JR|;-B=&Bkqn~ z&ix^)j^jGH@iZxeCY7%`_ai>;ly6da4-pwn%8`u;b=tWAf2xcFSSqvcMNOh@nFbok zE-?MIhZPYsg~2<2F?wQGM&dII{_@&PTP}z{lv>T@TCT7^+n;)ls$iC^drfrdUY{p^ z<}w$OayI)&bMz?Njp9I3gV^?p+#WslA2JcanX= zO6ylU!mwQQ_8d)jvHY8`Vnjl?&WgJth0ROSR#{4xJ4tIET3Yli-XX6Z@AUeNv&y#X z;V*3T*(2_6{_?pVF>$aOoPQStwVuLA)1Gc0-_woZ;1ke=+7M8| zEbz$uF@*}PHIvO;R>Xki1a+&bnoGf!3GZ2nIz%Af!FI7cRdCL3uHH`&^OTO7$KCv{ zTAF&*Wt_a*W_doLscH8Oa#gSS(IGJ<$wd}N1oGp^&I{vl#Nt&&wGpa>7t{g$X`OxX zpHD#@7Z-VRM^-4+DzW7jpG^$c;`4leJsyJ4R#3?&d&3Tnm+6k5d3hJ}TXh&hJDzX@ zjzprA9Z}dr=J@9J2iM;)m)PPBWS$m5*qTt)cdv#VH#CUS256U1_aVj{eoWZC8EZd6 zIwu@OxB?S7lr$VBvvV9YeF7zyMh@gf%7dNlE~{)i&ptaAAZ{Oe6a|^mihOl5|MA=X zSE+pjvl5eoLy{FX&U0c|M3%ZqIpNmG zh|6ZbP}&juPC9FbWzK_>nzO?$s(1EujuYcMc>etJqaF1&dm85-L-dWJTGHZc`Tk$! zU;uQuk&63sQz7_9~{bDlvII|8ElagODeG1Q;DjmBo2$VXdwzr5G^3H9|6NUXt< zp&HJw6NIkeWrOwP<}(5FWU=+D*`a~z02=Q4E~WP)B&Qhe`*&1%B{pLFT^63lDCS0*xRq$CjpTcw=JI7@kefMkG!(n+p3Rl@h za;)=9WGE^x0Ih{mgwK&hzGA-*U9P4Ws! zRA5T7Xp$yT`lY)hxOMv?D3Ity?ARYbQKb))iUZ8W95n2Rk%a+l`GwJqq_XE0aFc|b zXZ#|KOqPXXiEPXx_&%04MD^% zzlg^1eDkIGWFzHGUvmroejtg9QPKKiE{`8$oQoWnE(QiH?3+(yudtOB=H0p?qjuo0 zaDB1)HsPAjtC4sVVE}?~b$F+X!{#SU!!VqcMU??Qm~8O?mm^sU8Uu?`VC;(FL?oDZ z(q=Zo-fb92aF712@pwxr^Sqou4Xa1qCiQF@%i51W#w6O>*O6$zf|M zzWjK?)Po&-3z+7mgLkQ&^!xlNBTk*N2$#8)p5b*4ndhY~h(x(i?OUa*(;V#;g!i#3 zduK6E zj5sT(zj^!dw!mOZ%PYx%$Uam+AlrkaVJCo;r-$SCy1rQUesS-x<2Ug6&?daN1OxE{ zVW2Ibo?)4YPS2J;Pp9^bWMoCZd0HK!^^*fWmC4Z7c73>@5(0ippJ5OXbn8Ztmc)FPt75FjL?OGus zJYQvJ)Ms$@ftznt9c@z?bCt1b8Zf~Rbr?pFaG$scbG%{0`up}UA|tZj?4-=dk^wS8 zb4^yPvD~P&Tm2H!%iC4*Y9a(jv*w$)-1te1XWp2t+AC&X<7x$Nl1sC#Yx0AQ0BB0i zy&KS^gp}88C()WAN69T1o)ZMX2y~d;nZRjb2rSo*1opqt9y=gdAx){Zx8{Br6u-2* z<<>P~?qTI*oZ=w%l7qFu5&UiVy2w;U&#N0GI`{PX%``~EqS|(p_PT7+2uT3#(iq@& z&rfQk`5u)DjBxryw0GC(8|%#GJ2 zd#o_T=_qaS#kbo|G}n*+)xIepG@?Q7RE#ZE%1s!2iGiL}IG>3*=3wZ7XQEF1t&8C{ zr+O|oWbDQqW?^$B=Kf@clWK%rmdFQKnE{i^^B>!Od&(7}T;Att!_C}>Pb%nsICQ8N z!JSav*Pr_pPx|D4~atUM_Q9Kg_JO_Diw+s8;@ zD%ZgafY!#IpyDd=4|5jva$;8Z4~SGetgI_D`_g?18{qwjv0z3d|80)yc~&rJraejl z;0`wugUd0i?w%aVKeEWmbEYg^4+qc%aVwUc@$A6t`&2v=5(NSiY&I zx3I~y&vZ!ZuxX#|pffWZdDjWzxcU6{)Fyf`?>>WuU!b6uDa zO&?ME^)v6ip-cn@3z<@l_&I&@VpxeA4==$~QU#<>TnJ{(S*^}7@B#T89A#&; z5uBRRb0AOwZZArF@W2%5T#x+go98)mTxCtv_Fd)qYp1%{uRKe*<_2Sc4k{S~^Ze`< ztf7{j@-$7f&S3wi**dKTh0hD7u?4|3o(&>;1TD^m39|h(m@eJlLI&h(K<<(U)|*M% zuf#n%f+N{Ib}I{OJR@I~k<3qstVV_IYlE5kt?}Mo>)_YAx?{H#3|LOQ4^n6;S|At4 zQ7Dt+rMza%Q?iB~!M$^-wzEU43sprldxX+JtNgqP^F3XJeNS{U*Bhs?^%fMhv&88i z9VYi&(L7AaJl9au4A~}Fstd6g%j=;cR~R%m+@wa6G{(z2l?a36B>c!uEm#-k%c6@D zsLLF0u35t7STi3hu4XHD?^kf^_tT+mS_;Dk2}-y(pF}a}2w>vtXI(o4wk2Mvv#00> zQfchhe1#vE(mokdS|*&oBz=r1*nD~Ke)4hbns%d?mR^PKLvNDR$gkkj^)Q&u>x5M7 z<*@r{fh|`%=Be`;r?Jx>blbg*EX{r{QC)6tkC01t3RnC|o*rE$Jgo?`A9_R_R;5euq@rf{Hi;f8Ant+0wz zH7JWUop2@|cxXiuRODm-b59qa$A3G~>Nr`PEP)9|FH>%^sb14|Yq&-U5e{>Z!rd@$ zXa#bV7N12VZV_rUh8WzhQ=a4M$6cprwN$zum31wi8@g^`Ut6OJ=lB*&T_~e z|9#1gcR6M(IK7m`Df8NMJZj(@-V18?Dw5?s8oLgAuwlPEnE}IbtY=3l7@ntiyA=NP zT%!U>UG>}Ok3`}DRe4`ccRM(jJu#%}0=YE_+zJ9-Y0_4NFH4nYvuQQbdDPwa6A7}! z$y!^I5D&AaEpND1hX^=n96w&}WOY-dH)#>Qt8iR&rN81qq?cu7wmqL%bKcE4MLw-z z0ro}4sk3qOn2fv}6Y+TnZmkkvmlc1l%}74at5A60#)x6x+Bc$^)@RzdqX=1X7@hCY z+sMF~sC5arI9wpS>)-zl+>#n-e3&l#!VW(v4d~d!d_b*0>~sohm4#2NS`AXYR_AM8 zORvq5r7K_dESqS(YS3QmH0=YNRa0$Fa#Czz?`a(W7$iY0v5j5&!ZiDe(dk3Fp`e2a zZ@z4~;6~=~@$quLRFNn&l16R*LxOV!J|_g(o?f|m=lNa0vgaUYCWlU|n}r#!GEsfC zDAy!!Ziib%H@lK^oLN zcY%gyAS@(NywTEgD)H0;Br8L%7t1YTQ3)wMjKy*{sp!-GFgQnq!f^lQff}Bld9V&Y zK@38cP>yEQ0wS@lv8P6cxJ@=)d6>j1y0-?1Q({>sCeJNK++O>aQPwtZ@77t*P1u~I z7hx-qO&2;)5c<@3+lsvtZC>rPT~x+Dj@c_cGs^8 z-RxF+pI4|AZXqvfD|KW33qI%LIz>P>>LXAf+sJL=!(d{^(|s_m_F{+f$`)bMsyTbD zUjDb*p{c`s%Pjqc2~s^Yr^YOz9;SDRf`>lUsa_E*Kw*D&U|Eq!hB%ozAa$kN|Dp~0 zj~u2#y0Z_|ch5HnKkRQxULK52+jxPhh*}*voMglBC1tcs>0L3Ic;L3J;0Z(}&V-il76nCyFWhKD0}iqJo73NWSYf$DdPC3EWcYb()4n{UB^6yUri?*!AjB#i(=hbP%+*?IMsrD(s z%&~5fPQRU{i1-n1IwY6!;Grp&IjIOWZ=*rpfE}a%9face+K?u8J0q{zpFzW^Yju8% zU`x@u2X&Ez8(gbXioP$73Rx2m|AH6%ra;kfJuNL64nR$heCQ@X^U+ZcDTTH34{D46Ga*QDM~Bv$;lBK2MCmCo9VM!GpnS&y-ux`XV4X5=*+11G7sGHXw!AMg@+1_w=3 zjd0~%77Wy!V@5*diz2sqSxJTKoj06XeDt-JgX?jEMGI-pzTla$5@Rvn)Cd|dG0)aq z8n%F-Wf)e2M*5slz*%f<1*GLxZnO1^CGhQN!Sz|UpHN~SGKq7Orq-<)JWwK7i@+(6mHxZ7N{E@bNIHSmhrlmi24QSczOjU#xj~Vj`nTLF)lm8WK0tpS;k# ziqkf782g+B4a?%v$=lkI-hV#dP2ajIntrDfgi@Q`Fhmh6>12MdU5;XvL?0_lO2ezX zQN}38ghg!GY)rJG`DSAC&fmUJUl^*Qg-Egbpg@jp={)P41Y9cTjemEeTb_~L`3d6tQcutq%aX}^q$h4B$Zn=w zoyMawhAAoxYGtLyv9I~Zee?%(Pu`<+EFn~>9y?88I4C&(5Nrprh|HypKj0jY)lRJ` zRiuw7a~IXt+%SXTQn!l|@GZe1R2l})<_s<)0pmC02lznmN9olii@t&E+ngL*rzHQ* z(zXajuUlK}o#=hBW?<^4hv}QK+|ydLCw>j$Y9sCaiM5f=UH#;$R0DO^bkV|O&%kT> zoA*LyJiv_Ez`O$IN2BtgcxCQeX4*3xcehKTw@%_i6e?n{P%o#$-D1`MWH?vnT+=

U_&QF&mdk5$_8nCM*FC=g_X| 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%0An(-_`Rd_ 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 z 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/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 diff --git a/spec/experiments/ios_specific_templates_experiment_spec.rb b/spec/experiments/ios_specific_templates_experiment_spec.rb new file mode 100644 index 00000000000..4d02381dbde --- /dev/null +++ b/spec/experiments/ios_specific_templates_experiment_spec.rb @@ -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 diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 6643ebe82e6..15bc2318022 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -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 diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index f1e5658cd7b..8cbc0491441 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -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 diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb index bf40aea4a5d..271dce44db7 100644 --- a/spec/finders/users_finder_spec.rb +++ b/spec/finders/users_finder_spec.rb @@ -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 diff --git a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js index b6be53fec99..7952661e2d2 100644 --- a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js +++ b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js @@ -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, + ); + }); }); }); }); diff --git a/spec/frontend/header_search/mock_data.js b/spec/frontend/header_search/mock_data.js index 358c224dfa6..b6f0fdcc29d 100644 --- a/spec/frontend/header_search/mock_data.js +++ b/spec/frontend/header_search/mock_data.js @@ -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', }, { diff --git a/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js b/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js index b0d5859cd31..3d7bf7acb41 100644 --- a/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js +++ b/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js @@ -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', diff --git a/spec/helpers/ci/pipelines_helper_spec.rb b/spec/helpers/ci/pipelines_helper_spec.rb index 2b76eaa87bc..c473e1e4ab6 100644 --- a/spec/helpers/ci/pipelines_helper_spec.rb +++ b/spec/helpers/ci/pipelines_helper_spec.rb @@ -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 diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 96d8157dfde..d1be451a759 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -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 diff --git a/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb new file mode 100644 index 00000000000..fbd5fe546fa --- /dev/null +++ b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb @@ -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 diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ead7f7d0786..43088739f34 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -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) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 5f2777b12d2..09e3a9c1156 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -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 diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb index 2bc31153f2c..07efd56fef4 100644 --- a/spec/requests/api/project_export_spec.rb +++ b/spec/requests/api/project_export_spec.rb @@ -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 diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb index ae246a87bb6..215d9d3e5a8 100644 --- a/spec/support/shared_examples/features/access_tokens_shared_examples.rb +++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb @@ -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