Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/test-metadata.gitlab-ci.yml6
-rw-r--r--app/assets/javascripts/clone_panel.js42
-rw-r--r--app/assets/javascripts/pages/projects/project.js40
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue28
-rw-r--r--app/helpers/application_settings_helper.rb6
-rw-r--r--app/helpers/button_helper.rb10
-rw-r--r--app/helpers/search_helper.rb2
-rw-r--r--app/views/projects/buttons/_clone.html.haml6
-rw-r--r--app/views/projects/issues/_issue.html.haml109
-rw-r--r--app/views/projects/wikis/git_access.html.haml2
-rw-r--r--app/views/shared/_clone_panel.html.haml14
-rw-r--r--app/views/shared/_no_password.html.haml2
-rw-r--r--app/views/shared/_no_ssh.html.haml2
-rw-r--r--changelogs/unreleased/207869-fix-wiki-clone-panel.yml5
-rw-r--r--changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml5
-rw-r--r--changelogs/unreleased/232670-steal-webauthn-background-migration.yml5
-rw-r--r--changelogs/unreleased/284602-remove-issue-box-static.yml5
-rw-r--r--changelogs/unreleased/duplicate_autocomplete_suggestions.yml5
-rw-r--r--config/feature_flags/development/usage_data_design_action.yml8
-rw-r--r--data/whats_new/202011230001_13_06.yml51
-rw-r--r--db/migrate/20201117075742_change_webauthn_xid_length.rb18
-rw-r--r--db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb37
-rw-r--r--db/schema_migrations/202010261855141
-rw-r--r--db/schema_migrations/202011170757421
-rw-r--r--db/structure.sql4
-rw-r--r--doc/development/testing_guide/ci.md6
-rw-r--r--doc/user/group/saml_sso/index.md30
-rw-r--r--lib/gitlab/database/batch_count.rb15
-rw-r--r--locale/gitlab.pot16
-rw-r--r--qa/qa/page/component/legacy_clone_panel.rb4
-rwxr-xr-xscripts/api/cancel_pipeline58
-rwxr-xr-xscripts/api/download_job_artifact92
-rwxr-xr-xscripts/api/get_job_id108
-rwxr-xr-xscripts/api/play_job60
-rwxr-xr-xscripts/get-job-id43
-rw-r--r--scripts/rspec_helpers.sh62
-rw-r--r--scripts/utils.sh55
-rw-r--r--spec/features/profiles/active_sessions_spec.rb4
-rw-r--r--spec/features/projects/show/no_password_spec.rb10
-rw-r--r--spec/features/projects/wiki/user_git_access_wiki_page_spec.rb21
-rw-r--r--spec/features/projects/wikis_spec.rb1
-rw-r--r--spec/finders/ci/pipeline_schedules_finder_spec.rb2
-rw-r--r--spec/finders/feature_flags_finder_spec.rb2
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js51
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/update_alert_status_spec.rb2
-rw-r--r--spec/graphql/mutations/boards/issues/issue_move_list_spec.rb6
-rw-r--r--spec/graphql/mutations/container_expiration_policies/update_spec.rb2
-rw-r--r--spec/graphql/mutations/discussions/toggle_resolve_spec.rb2
-rw-r--r--spec/graphql/mutations/issues/create_spec.rb8
-rw-r--r--spec/graphql/mutations/labels/create_spec.rb2
-rw-r--r--spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/helpers/button_helper_spec.rb2
-rw-r--r--spec/helpers/search_helper_spec.rb31
-rw-r--r--spec/lib/gitlab/cleanup/project_uploads_spec.rb18
-rw-r--r--spec/lib/gitlab/database/batch_count_spec.rb23
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb2
-rw-r--r--spec/migrations/ensure_u2f_registrations_migrated_spec.rb41
-rw-r--r--spec/requests/api/ci/runner/jobs_artifacts_spec.rb2
-rw-r--r--spec/requests/api/go_proxy_spec.rb4
-rw-r--r--spec/services/event_create_service_spec.rb4
-rw-r--r--spec/services/projects/move_access_service_spec.rb6
-rw-r--r--spec/services/projects/move_deploy_keys_projects_service_spec.rb2
-rw-r--r--spec/services/projects/move_lfs_objects_projects_service_spec.rb2
-rw-r--r--spec/services/projects/move_notification_settings_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_authorizations_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_group_links_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_members_service_spec.rb2
-rw-r--r--spec/services/suggestions/apply_service_spec.rb2
-rw-r--r--spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb27
74 files changed, 632 insertions, 636 deletions
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index f1bd173ff6d..d3069657e88 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -38,7 +38,7 @@ review-build-cng:
- BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng
# When the job is manual, review-deploy is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them.
- - '[ -z $CI_JOB_MANUAL ] || scripts/api/play_job --job-name "review-deploy"'
+ - '[ -z $CI_JOB_MANUAL ] || play_job "review-deploy"'
.review-workflow-base:
extends:
@@ -78,8 +78,8 @@ review-deploy:
- disable_sign_ups || (delete_release && exit 1)
# When the job is manual, review-qa-smoke is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them.
- - '[ -z $CI_JOB_MANUAL ] || scripts/api/play_job --job-name "review-qa-smoke"'
- - '[ -z $CI_JOB_MANUAL ] || scripts/api/play_job --job-name "review-performance"'
+ - '[ -z $CI_JOB_MANUAL ] || play_job "review-qa-smoke"'
+ - '[ -z $CI_JOB_MANUAL ] || play_job "review-performance"'
after_script:
# Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan.
# Set DAST_RUN to true when jobs are manually scheduled.
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index aec0a1640f1..e4b7047ef71 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -1,5 +1,6 @@
.tests-metadata-state:
- image: ruby:2.7
+ variables:
+ TESTS_METADATA_S3_BUCKET: "gitlab-ce-cache"
before_script:
- source scripts/utils.sh
artifacts:
@@ -16,8 +17,7 @@ retrieve-tests-metadata:
- .test-metadata:rules:retrieve-tests-metadata
stage: prepare
script:
- - install_gitlab_gem
- - source ./scripts/rspec_helpers.sh
+ - source scripts/rspec_helpers.sh
- retrieve_tests_metadata
update-tests-metadata:
diff --git a/app/assets/javascripts/clone_panel.js b/app/assets/javascripts/clone_panel.js
new file mode 100644
index 00000000000..362e6c5c5ce
--- /dev/null
+++ b/app/assets/javascripts/clone_panel.js
@@ -0,0 +1,42 @@
+import $ from 'jquery';
+
+export default function initClonePanel() {
+ const $cloneOptions = $('ul.clone-options-dropdown');
+ if ($cloneOptions.length) {
+ const $cloneUrlField = $('#clone_url');
+ const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
+ const mobileCloneField = document.querySelector(
+ '.js-mobile-git-clone .js-clone-dropdown-label',
+ );
+
+ const selectedCloneOption = $cloneBtnLabel.text().trim();
+ if (selectedCloneOption.length > 0) {
+ $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
+ }
+
+ $('a', $cloneOptions).on('click', e => {
+ e.preventDefault();
+ const $this = $(e.currentTarget);
+ const url = $this.attr('href');
+ const cloneType = $this.data('cloneType');
+
+ $('.is-active', $cloneOptions).removeClass('is-active');
+ $(`a[data-clone-type="${cloneType}"]`).each(function switchProtocol() {
+ const $el = $(this);
+ const activeText = $el.find('.dropdown-menu-inner-title').text();
+ const $container = $el.closest('.js-git-clone-holder, .js-mobile-git-clone');
+ const $label = $container.find('.js-clone-dropdown-label');
+
+ $el.toggleClass('is-active');
+ $label.text(activeText);
+ });
+
+ if (mobileCloneField) {
+ mobileCloneField.dataset.clipboardText = url;
+ } else {
+ $cloneUrlField.val(url);
+ }
+ $('.js-git-empty .js-clone').text(url);
+ });
+ }
+}
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 5317093c4cf..8c7aa04a0b6 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -9,47 +9,11 @@ import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from '~/flash';
import projectSelect from '../../project_select';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
+import initClonePanel from '~/clone_panel';
export default class Project {
constructor() {
- const $cloneOptions = $('ul.clone-options-dropdown');
- if ($cloneOptions.length) {
- const $projectCloneField = $('#project_clone');
- const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
- const mobileCloneField = document.querySelector(
- '.js-mobile-git-clone .js-clone-dropdown-label',
- );
-
- const selectedCloneOption = $cloneBtnLabel.text().trim();
- if (selectedCloneOption.length > 0) {
- $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
- }
-
- $('a', $cloneOptions).on('click', e => {
- e.preventDefault();
- const $this = $(e.currentTarget);
- const url = $this.attr('href');
- const cloneType = $this.data('cloneType');
-
- $('.is-active', $cloneOptions).removeClass('is-active');
- $(`a[data-clone-type="${cloneType}"]`).each(function() {
- const $el = $(this);
- const activeText = $el.find('.dropdown-menu-inner-title').text();
- const $container = $el.closest('.project-clone-holder');
- const $label = $container.find('.js-clone-dropdown-label');
-
- $el.toggleClass('is-active');
- $label.text(activeText);
- });
-
- if (mobileCloneField) {
- mobileCloneField.dataset.clipboardText = url;
- } else {
- $projectCloneField.val(url);
- }
- $('.js-git-empty .js-clone').text(url);
- });
- }
+ initClonePanel();
// Ref switcher
if (document.querySelector('.js-project-refs-dropdown')) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
index ff0d065c71d..1c9909e7178 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
@@ -1,10 +1,11 @@
<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlIcon, GlTooltipDirective, GlFormCheckbox } from '@gitlab/ui';
import { SQUASH_BEFORE_MERGE } from '../../i18n';
export default {
components: {
GlIcon,
+ GlFormCheckbox,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -32,32 +33,23 @@ export default {
tooltipTitle() {
return this.isDisabled ? this.$options.i18n.tooltipTitle : null;
},
- tooltipFocusable() {
- return this.isDisabled ? '0' : null;
- },
},
};
</script>
<template>
- <div class="inline">
- <label
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-form-checkbox
v-gl-tooltip
- :class="{ 'gl-text-gray-400': isDisabled }"
- :tabindex="tooltipFocusable"
- data-testid="squashLabel"
+ :checked="value"
+ :disabled="isDisabled"
+ name="squash"
+ class="qa-squash-checkbox js-squash-checkbox gl-mb-0 gl-mr-2"
:title="tooltipTitle"
+ @change="checked => $emit('input', checked)"
>
- <input
- :checked="value"
- :disabled="isDisabled"
- type="checkbox"
- name="squash"
- class="qa-squash-checkbox js-squash-checkbox"
- @change="$emit('input', $event.target.checked)"
- />
{{ $options.i18n.checkboxLabel }}
- </label>
+ </gl-form-checkbox>
<a
v-if="helpPath"
v-gl-tooltip
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 512649b3008..9a43a4a3a15 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -49,12 +49,12 @@ module ApplicationSettingsHelper
all_protocols_enabled? || Gitlab::CurrentSettings.enabled_git_access_protocol == 'http'
end
- def enabled_project_button(project, protocol)
+ def enabled_protocol_button(container, protocol)
case protocol
when 'ssh'
- ssh_clone_button(project, append_link: false)
+ ssh_clone_button(container, append_link: false)
else
- http_clone_button(project, append_link: false)
+ http_clone_button(container, append_link: false)
end
end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index c999d1f94ad..ea24f469ffa 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -58,10 +58,10 @@ module ButtonHelper
end
end
- def http_clone_button(project, append_link: true)
+ def http_clone_button(container, append_link: true)
protocol = gitlab_config.protocol.upcase
dropdown_description = http_dropdown_description(protocol)
- append_url = project.http_url_to_repo if append_link
+ append_url = container.http_url_to_repo if append_link
dropdown_item_with_description(protocol, dropdown_description, href: append_url, data: { clone_type: 'http' })
end
@@ -74,13 +74,13 @@ module ButtonHelper
end
end
- def ssh_clone_button(project, append_link: true)
+ def ssh_clone_button(container, append_link: true)
if Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
current_user.try(:require_ssh_key?)
- dropdown_description = _("You won't be able to pull or push project code via SSH until you add an SSH key to your profile")
+ dropdown_description = s_("MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile")
end
- append_url = project.ssh_url_to_repo if append_link
+ append_url = container.ssh_url_to_repo if append_link
dropdown_item_with_description('SSH', dropdown_description, href: append_url, data: { clone_type: 'ssh' })
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index de1e0e4e05e..2d47ee89d11 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -31,7 +31,7 @@ module SearchHelper
[
resources_results,
generic_results
- ].flatten.uniq do |item|
+ ].flatten do |item|
item[:label]
end
end
diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml
index cf58cff7445..938dfc69500 100644
--- a/app/views/projects/buttons/_clone.html.haml
+++ b/app/views/projects/buttons/_clone.html.haml
@@ -2,7 +2,7 @@
- dropdown_class = local_assigns.fetch(:dropdown_class, '')
.git-clone-holder.js-git-clone-holder
- %a#clone-dropdown.gl-button.btn.btn-primary.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
+ %a#clone-dropdown.gl-button.btn.btn-info.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.gl-mr-2.js-clone-dropdown-label
= _('Clone')
= sprite_icon("chevron-down", css_class: "icon")
@@ -12,7 +12,7 @@
%label.label-bold
= _('Clone with SSH')
.input-group
- = text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control qa-ssh-clone-url", readonly: true, aria: { label: 'Project clone URL' }
+ = text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control qa-ssh-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
@@ -21,7 +21,7 @@
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
.input-group
- = text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control qa-http-clone-url", readonly: true, aria: { label: 'Project clone URL' }
+ = text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control qa-http-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
= clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index d9ad171a6cc..3ce85fb46d5 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -1,67 +1,66 @@
-# DANGER: Any changes to this file need to be reflected in issuables_list/components/issuable.vue!
%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue), data: { labels: issue.label_ids, id: issue.id, qa_selector: 'issue_container', qa_issue_title: issue.title } }
- .issue-box
+ .issuable-info-container
- if @can_bulk_update
.issue-check.hidden
= check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected-issuable"
- .issuable-info-container
- .issuable-main-info
- .issue-title.title
- %span.issue-title-text.js-onboarding-issue-item{ dir: "auto" }
- - if issue.confidential?
- %span.has-tooltip{ title: _('Confidential') }
- = confidential_icon(issue)
- = link_to issue.title, issue_path(issue)
- = render_if_exists 'projects/issues/subepic_flag', issue: issue
- - if issue.tasks?
- %span.task-status.d-none.d-sm-inline-block
- &nbsp;
- = issue.task_status
+ .issuable-main-info
+ .issue-title.title
+ %span.issue-title-text.js-onboarding-issue-item{ dir: "auto" }
+ - if issue.confidential?
+ %span.has-tooltip{ title: _('Confidential') }
+ = confidential_icon(issue)
+ = link_to issue.title, issue_path(issue)
+ = render_if_exists 'projects/issues/subepic_flag', issue: issue
+ - if issue.tasks?
+ %span.task-status.d-none.d-sm-inline-block
+ &nbsp;
+ = issue.task_status
- .issuable-info
- %span.issuable-reference
- #{issuable_reference(issue)}
- %span.issuable-authored.d-none.d-sm-inline-block
- &middot;
- opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
- by #{link_to_member(@project, issue.author, avatar: false)}
- = render_if_exists 'shared/issuable/gitlab_team_member_badge', {author: issue.author}
- - if issue.milestone
- %span.issuable-milestone.d-none.d-sm-inline-block
- &nbsp;
- = link_to project_issues_path(issue.project, milestone_title: issue.milestone.title), data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(issue.milestone) } do
- = sprite_icon('clock', css_class: 'gl-vertical-align-text-bottom')
- = issue.milestone.title
- - if issue.due_date
- %span.issuable-due-date.d-none.d-sm-inline-block.has-tooltip{ class: "#{'cred' if issue.overdue?}", title: _('Due date') }
- &nbsp;
- = sprite_icon('calendar')
- = issue.due_date.to_s(:medium)
+ .issuable-info
+ %span.issuable-reference
+ #{issuable_reference(issue)}
+ %span.issuable-authored.d-none.d-sm-inline-block
+ &middot;
+ opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
+ by #{link_to_member(@project, issue.author, avatar: false)}
+ = render_if_exists 'shared/issuable/gitlab_team_member_badge', {author: issue.author}
+ - if issue.milestone
+ %span.issuable-milestone.d-none.d-sm-inline-block
+ &nbsp;
+ = link_to project_issues_path(issue.project, milestone_title: issue.milestone.title), data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(issue.milestone) } do
+ = sprite_icon('clock', css_class: 'gl-vertical-align-text-bottom')
+ = issue.milestone.title
+ - if issue.due_date
+ %span.issuable-due-date.d-none.d-sm-inline-block.has-tooltip{ class: "#{'cred' if issue.overdue?}", title: _('Due date') }
+ &nbsp;
+ = sprite_icon('calendar')
+ = issue.due_date.to_s(:medium)
- = render_if_exists "projects/issues/issue_weight", issue: issue
- = render_if_exists "projects/issues/health_status", issue: issue
+ = render_if_exists "projects/issues/issue_weight", issue: issue
+ = render_if_exists "projects/issues/health_status", issue: issue
- - if issue.labels.any?
- &nbsp;
- - presented_labels_sorted_by_title(issue.labels, issue.project).each do |label|
- = link_to_label(label, small: true)
+ - if issue.labels.any?
+ &nbsp;
+ - presented_labels_sorted_by_title(issue.labels, issue.project).each do |label|
+ = link_to_label(label, small: true)
- = render "projects/issues/issue_estimate", issue: issue
+ = render "projects/issues/issue_estimate", issue: issue
- .issuable-meta
- %ul.controls
- - if issue.closed? && issue.moved?
- %li.issuable-status
- = _('CLOSED (MOVED)')
- - elsif issue.closed?
- %li.issuable-status
- = _('CLOSED')
- - if issue.assignees.any?
- %li.gl-display-flex
- = render 'shared/issuable/assignees', project: @project, issuable: issue
+ .issuable-meta
+ %ul.controls
+ - if issue.closed? && issue.moved?
+ %li.issuable-status
+ = _('CLOSED (MOVED)')
+ - elsif issue.closed?
+ %li.issuable-status
+ = _('CLOSED')
+ - if issue.assignees.any?
+ %li.gl-display-flex
+ = render 'shared/issuable/assignees', project: @project, issuable: issue
- = render 'shared/issuable_meta_data', issuable: issue
+ = render 'shared/issuable_meta_data', issuable: issue
- .float-right.issuable-updated-at.d-none.d-sm-inline-block
- %span
- = _('updated %{time_ago}').html_safe % { time_ago: time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago') }
+ .float-right.issuable-updated-at.d-none.d-sm-inline-block
+ %span
+ = _('updated %{time_ago}').html_safe % { time_ago: time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago') }
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index c166642bae2..2542860c742 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -11,7 +11,7 @@
%strong= @wiki.full_path
.pt-3.pt-lg-0.w-100
- = render "shared/clone_panel", project: @wiki
+ = render "shared/clone_panel", container: @wiki
.wiki-git-access
%h3= s_("WikiClone|Install Gollum")
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 9ec8d3c18cd..1ed37c7a5c4 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,11 +1,9 @@
-- project = project || @project
-
.git-clone-holder.js-git-clone-holder.input-group
.input-group-prepend
- if allowed_protocols_present?
.input-group-text.clone-dropdown-btn.btn
%span.js-clone-dropdown-label
- = enabled_project_button(project, enabled_protocol)
+ = enabled_protocol_button(container, enabled_protocol)
- else
%a#clone-dropdown.input-group-text.btn.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.js-clone-dropdown-label
@@ -13,12 +11,12 @@
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
%li
- = ssh_clone_button(project)
+ = ssh_clone_button(container)
%li
- = http_clone_button(project)
- = render_if_exists 'shared/kerberos_clone_button', project: project
+ = http_clone_button(container)
+ = render_if_exists 'shared/kerberos_clone_button', container: container
- = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Project clone URL') }
+ = text_field_tag :clone_url, default_url_to_repo(container), class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
- = clipboard_button(target: '#project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
+ = clipboard_button(target: '#clone_url', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
diff --git a/app/views/shared/_no_password.html.haml b/app/views/shared/_no_password.html.haml
index 76ae63ca5e8..9c1e5a49b44 100644
--- a/app/views/shared/_no_password.html.haml
+++ b/app/views/shared/_no_password.html.haml
@@ -5,7 +5,7 @@
= sprite_icon('close', size: 16, css_class: 'gl-icon')
.gl-alert-body
- translation_params = { protocol: gitlab_config.protocol.upcase, set_password_link: link_to_set_password }
- - set_password_message = _("You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account") % translation_params
+ - set_password_message = _("You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account") % translation_params
= set_password_message.html_safe
.gl-alert-actions
= link_to _('Remind later'), '#', class: 'hide-no-password-message btn gl-alert-action btn-info btn-md gl-button'
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index a083a772233..0a7fa2a3c1e 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -4,7 +4,7 @@
%button{ class: 'gl-alert-dismiss hide-no-ssh-message', type: 'button', 'aria-label': _('Dismiss') }
= sprite_icon('close', css_class: 'gl-icon s16')
.gl-alert-body
- = s_("MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile").html_safe
+ = s_("MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile")
.gl-alert-actions
= link_to s_('MissingSSHKeyWarningLink|Add SSH key'), profile_keys_path, class: "btn gl-alert-action btn-warning btn-md new-gl-button"
= link_to s_("MissingSSHKeyWarningLink|Don't show again"), profile_path(user: {hide_no_ssh_key: true}), method: :put, role: 'button', class: 'btn gl-alert-action btn-md btn-warning gl-button btn-warning-secondary'
diff --git a/changelogs/unreleased/207869-fix-wiki-clone-panel.yml b/changelogs/unreleased/207869-fix-wiki-clone-panel.yml
new file mode 100644
index 00000000000..8e09f0f9336
--- /dev/null
+++ b/changelogs/unreleased/207869-fix-wiki-clone-panel.yml
@@ -0,0 +1,5 @@
+---
+title: Fix repository clone panel for wikis
+merge_request: 47676
+author:
+type: fixed
diff --git a/changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml b/changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml
new file mode 100644
index 00000000000..99ad834046d
--- /dev/null
+++ b/changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml
@@ -0,0 +1,5 @@
+---
+title: Add GlFormCheckbox to squash commits
+merge_request: 48338
+author:
+type: changed
diff --git a/changelogs/unreleased/232670-steal-webauthn-background-migration.yml b/changelogs/unreleased/232670-steal-webauthn-background-migration.yml
new file mode 100644
index 00000000000..a9752c57947
--- /dev/null
+++ b/changelogs/unreleased/232670-steal-webauthn-background-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Cleanup webauthn background migration
+merge_request: 46179
+author: Jan Beckmann
+type: added
diff --git a/changelogs/unreleased/284602-remove-issue-box-static.yml b/changelogs/unreleased/284602-remove-issue-box-static.yml
new file mode 100644
index 00000000000..75cf831177f
--- /dev/null
+++ b/changelogs/unreleased/284602-remove-issue-box-static.yml
@@ -0,0 +1,5 @@
+---
+title: Remove .issue-box from static (classic) Issuable list
+merge_request: 47998
+author: Takuya Noguchi
+type: performance
diff --git a/changelogs/unreleased/duplicate_autocomplete_suggestions.yml b/changelogs/unreleased/duplicate_autocomplete_suggestions.yml
new file mode 100644
index 00000000000..5bc818862b9
--- /dev/null
+++ b/changelogs/unreleased/duplicate_autocomplete_suggestions.yml
@@ -0,0 +1,5 @@
+---
+title: Fix missing item with same name in autocomplete suggestions
+merge_request: 48410
+author: Paul Ungureanu @ungps
+type: fixed
diff --git a/config/feature_flags/development/usage_data_design_action.yml b/config/feature_flags/development/usage_data_design_action.yml
deleted file mode 100644
index e013237ecca..00000000000
--- a/config/feature_flags/development/usage_data_design_action.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_design_action
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46626
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/287630
-milestone: '13.7'
-type: development
-group: group::knowledge
-default_enabled: true
diff --git a/data/whats_new/202011230001_13_06.yml b/data/whats_new/202011230001_13_06.yml
new file mode 100644
index 00000000000..faa4cf62ea9
--- /dev/null
+++ b/data/whats_new/202011230001_13_06.yml
@@ -0,0 +1,51 @@
+---
+- title: Auto Deploy to EC2
+ body: Auto DevOps has been expanded to support deployments to Amazon Web Services. You can now deploy to AWS Cloud Compute (EC2) and take advantage of Auto DevOps, even without Kubernetes. To enable this workflow, you must enable Auto DevOps and define the AWS-typed environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_DEFAULT_REGION. This allows you to provision your own infrastructure by leveraging the AWS CloudFormation API. Then, you can push your previously built artifact to an AWS S3 bucket, and deploy the content to an AWS EC2 instance. Your EC2 deployment then automatically builds you a complete, automatic delivery pipeline without extra manual steps.
+ stage: Release
+ self-managed: true
+ gitlab-com: true
+ packages: [core, starter, premium, ultimate]
+ url: https://docs.gitlab.com/ee/ci/cloud_deployment/#custom-build-job-for-auto-devops
+ image_url: https://img.youtube.com/vi/4B-qSwKnacA/hqdefault.jpg
+ published_at: 2020-11-22
+ release: 13.6
+- title: Display Code Quality severity ratings
+ body: The Code Quality feature in GitLab is great at showing what quality violations exist in a project or are changing in the Merge Request. However, understanding which of those violations is the most important is not clear in the GitLab interface today. With the Full Code Quality Report and Merge Request Widget, now you can see the severity rating. This makes it easy for you to understand which code quality violations are most important to resolve before merging and reduces the technical debt in your project.
+ stage: Verify
+ self-managed: true
+ gitlab-com: true
+ packages: [core, starter, premium, ultimate]
+ url: https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html#code-quality-widget
+ image_url: https://about.gitlab.com/images/13_6/code_quality_severity.png
+ published_at: 2020-11-22
+ release: 13.6
+- title: Display code coverage data for selected projects
+ body: In 13.4, we released the first iteration of Code Coverage data for Groups that enables you to compare the coverage of multiple projects and download the data in a single file from a single screen. However, to analyze the data, you had to open the file to check it manually, and probably imported it into a spreadsheet for further analysis. In GitLab 13.6, you can now select specific projects in a group to see their latest coverage values directly in GitLab itself without needing to download a file or waste development time accessing code coverage data.
+ stage: Verify
+ self-managed: true
+ gitlab-com: true
+ packages: [premium, ultimate]
+ url: https://docs.gitlab.com/ee/user/group/repositories_analytics/index.html#latest-project-test-coverage-list
+ image_url: https://about.gitlab.com/images/13_6/display_selected_coverage_projects_example.png
+ published_at: 2020-11-22
+ release: 13.6
+- title: Group-level management of project integrations
+ body: In GitLab 13.3, we added the ability to enable an integration across an entire instance. With GitLab 13.6, that feature is being expanded to allow integrations to be managed at the group level as well! Group owners can now add an integration to a group, and that integration will be inherited by all projects under that group. This has the potential for saving massive amounts of time, as many organizations have specific integrations that they want rolled out to every project they create. A great example of this is using our Jira integration. If you're using Jira, it's almost always across the whole company. Some of these companies have _thousands of projects_ and therefore had to configure each and every one of those integrations individually.With group-level management of project integrations, you can add the integration at each parent group, reducing the amount of configuration required by orders of magnitude!
+ stage: Create
+ self-managed: true
+ gitlab-com: true
+ packages: [core, starter, premium, ultimate]
+ url: https://docs.gitlab.com/ee/user/admin_area/settings/project_integration_management.html
+ image_url: https://about.gitlab.com/images/13_6/project-integration-inheriting-settings.png
+ published_at: 2020-11-22
+ release: 13.6
+- title: Milestone Burnup Charts and historically accurate reporting
+ body: A milestone or iteration burndown chart helps track completion progress of total scope, but it doesn't provide insight into how the scope changed during the course of the timebox. Neither has it previously retained historical accuracy regarding how much of the initial committed scope of the milestone or iteration was actually completed. To solve these problems and help teams have better insights into scope creep, milestones and iterations now show a burnup chart that tracks the daily total count and weight of issues added to and completed within a given timebox. This change only applies to milestones and iterations created in GitLab 13.6 or later. Milestones created prior to 13.6 will still have access to legacy burndown charts.
+ stage: Plan
+ self-managed: true
+ gitlab-com: true
+ packages: [starter, premium, ultimate]
+ url: https://docs.gitlab.com/ee/user/project/milestones/burndown_and_burnup_charts.html#burnup-charts
+ image_url: https://about.gitlab.com/images/13_6/burnup-chart.png
+ published_at: 2020-11-22
+ release: 13.6
diff --git a/db/migrate/20201117075742_change_webauthn_xid_length.rb b/db/migrate/20201117075742_change_webauthn_xid_length.rb
new file mode 100644
index 00000000000..2d836662e01
--- /dev/null
+++ b/db/migrate/20201117075742_change_webauthn_xid_length.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class ChangeWebauthnXidLength < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :webauthn_registrations, :credential_xid, 340, constraint_name: check_constraint_name(:webauthn_registrations, :credential_xid, 'max_length_v2')
+ remove_text_limit :webauthn_registrations, :credential_xid, constraint_name: check_constraint_name(:webauthn_registrations, :credential_xid, 'max_length')
+ end
+
+ def down
+ # no-op: Danger of failling if there are records with length(credential_xid) > 255
+ end
+end
diff --git a/db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb b/db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb
new file mode 100644
index 00000000000..121b9fee623
--- /dev/null
+++ b/db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+class EnsureU2fRegistrationsMigrated < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ BACKGROUND_MIGRATION_CLASS = 'MigrateU2fWebauthn'
+ BATCH_SIZE = 100
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class U2fRegistration < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'u2f_registrations'
+ end
+
+ def up
+ Gitlab::BackgroundMigration.steal(BACKGROUND_MIGRATION_CLASS)
+
+ # Do a manual update in case we lost BG jobs. The expected record count should be 0 or very low.
+ U2fRegistration
+ .joins("LEFT JOIN webauthn_registrations ON webauthn_registrations.u2f_registration_id = u2f_registrations.id")
+ .where("webauthn_registrations.u2f_registration_id IS NULL")
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ batch.each do |record|
+ Gitlab::BackgroundMigration::MigrateU2fWebauthn.new.perform(record.id, record.id)
+ rescue => e
+ Gitlab::ErrorTracking.track_exception(e, u2f_registration_id: record.id)
+ end
+ end
+ end
+
+ def down
+ # no-op (we can't "unsteal" migrations)
+ end
+end
diff --git a/db/schema_migrations/20201026185514 b/db/schema_migrations/20201026185514
new file mode 100644
index 00000000000..f6bdd06e501
--- /dev/null
+++ b/db/schema_migrations/20201026185514
@@ -0,0 +1 @@
+a9ae0161c40b9c72371d6eb992bd0da8c3698e7784357faac0821e3f513e48d2 \ No newline at end of file
diff --git a/db/schema_migrations/20201117075742 b/db/schema_migrations/20201117075742
new file mode 100644
index 00000000000..b3efeee7e0b
--- /dev/null
+++ b/db/schema_migrations/20201117075742
@@ -0,0 +1 @@
+a39bad8b213833c84370cf64188a3ce444fd8aeeff239c29f5f2f633d94ac6bb \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 97e669029b7..5cb98ddbd19 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -17571,8 +17571,8 @@ CREATE TABLE webauthn_registrations (
name text NOT NULL,
public_key text NOT NULL,
u2f_registration_id integer,
- CONSTRAINT check_242f0cc65c CHECK ((char_length(credential_xid) <= 255)),
- CONSTRAINT check_2f02e74321 CHECK ((char_length(name) <= 255))
+ CONSTRAINT check_2f02e74321 CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_e54008d9ce CHECK ((char_length(credential_xid) <= 340))
);
CREATE SEQUENCE webauthn_registrations_id_seq
diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md
index e7d67593a09..618f9010b4d 100644
--- a/doc/development/testing_guide/ci.md
+++ b/doc/development/testing_guide/ci.md
@@ -12,8 +12,8 @@ Our current CI parallelization setup is as follows:
1. The `retrieve-tests-metadata` job in the `prepare` stage ensures we have a
`knapsack/report-master.json` file:
- - The `knapsack/report-master.json` file is fetched from the latest `master` pipeline which runs `update-tests-metadata`
- (for now it's the 2-hourly scheduled master pipeline), if it's not here we initialize the file with `{}`.
+ - The `knapsack/report-master.json` file is fetched from S3, if it's not here
+ we initialize the file with `{}`.
1. Each `[rspec|rspec-ee] [unit|integration|system|geo] n m` job are run with
`knapsack rspec` and should have an evenly distributed share of tests:
- It works because the jobs have access to the `knapsack/report-master.json`
@@ -25,7 +25,7 @@ Our current CI parallelization setup is as follows:
1. The `update-tests-metadata` job (which only runs on scheduled pipelines for
[the canonical project](https://gitlab.com/gitlab-org/gitlab) takes all the
`knapsack/rspec*_pg_*.json` files and merge them all together into a single
- `knapsack/report-master.json` file that is saved as artifact.
+ `knapsack/report-master.json` file that is then uploaded to S3.
After that, the next pipeline will use the up-to-date `knapsack/report-master.json` file.
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 1ad6298caeb..49b444bd871 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -38,13 +38,15 @@ GitLab.com uses the SAML NameID to identify users. The NameID element:
- Must be unique to each user.
- Must be a persistent value that will never change, such as a randomly generated unique user ID.
- Is case sensitive. The NameID must match exactly on subsequent login attempts, so should not rely on user input that could change between upper and lower case.
-- Should not be an email address or username. We strongly recommend against these as it is hard to guarantee they will never change, for example when a person's name changes. Email addresses are also case-insensitive, which can result in users being unable to sign in.
+- Should not be an email address or username. We strongly recommend against these as it's hard to
+ guarantee it doesn't ever change, for example, when a person's name changes. Email addresses are
+ also case-insensitive, which can result in users being unable to sign in.
The relevant field name and recommended value for supported providers are in the [provider specific notes](#providers).
appropriate corresponding field.
CAUTION: **Warning:**
-Once users have signed into GitLab using the SSO SAML setup, changing the `NameID` will break the configuration and potentially lock users out of the GitLab group.
+Once users have signed into GitLab using the SSO SAML setup, changing the `NameID` breaks the configuration and potentially locks users out of the GitLab group.
#### NameID Format
@@ -56,11 +58,11 @@ GitLab provides metadata XML that can be used to configure your Identity Provide
1. Navigate to the group and click **Settings > SAML SSO**.
1. Copy the provided **GitLab metadata URL**.
-1. Follow your Identity Provider's documentation and paste the metadata URL when it is requested.
+1. Follow your Identity Provider's documentation and paste the metadata URL when it's requested.
## Configuring GitLab
-Once you've set up your identity provider to work with GitLab, you'll need to configure GitLab to use it for authentication:
+After you set up your identity provider to work with GitLab, you must configure GitLab to use it for authentication:
1. Navigate to the group's **Settings > SAML SSO**.
1. Find the SSO URL from your Identity Provider and enter it the **Identity provider single sign-on URL** field.
@@ -79,14 +81,14 @@ Please note that the certificate [fingerprint algorithm](#additional-providers-a
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5291) in GitLab 11.8.
- [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/9255) in GitLab 11.11 with ongoing enforcement in the GitLab UI.
-With this option enabled, users must go through your group's GitLab single sign-on URL. They may also be added via SCIM, if configured. Users cannot be added manually, and may only access project/group resources via the UI by signing in through the SSO URL.
+With this option enabled, users must go through your group's GitLab single sign-on URL. They may also be added via SCIM, if configured. Users can't be added manually, and may only access project/group resources via the UI by signing in through the SSO URL.
-However, users will not be prompted to sign in through SSO on each visit. GitLab will check whether a user has authenticated through SSO, and will only prompt the user to sign in via SSO if the session has expired.
+However, users are not prompted to sign in through SSO on each visit. GitLab checks whether a user has authenticated through SSO, and only prompts the user to sign in via SSO if the session has expired.
You can see more information about how long a session is valid in our [user profile documentation](../../profile/#why-do-i-keep-getting-signed-out).
We intend to add a similar SSO requirement for [Git and API activity](https://gitlab.com/gitlab-org/gitlab/-/issues/9152).
-When SSO enforcement is enabled for a group, users cannot share a project in the group outside the top-level group, even if the project is forked.
+When SSO enforcement is enabled for a group, users can't share a project in the group outside the top-level group, even if the project is forked.
## Providers
@@ -192,7 +194,7 @@ If the information you need isn't listed above you may wish to check our [troubl
Once Group SSO is configured and enabled, users can access the GitLab.com group through the identity provider's dashboard. If [SCIM](scim_setup.md) is configured, please see the [user access and linking setup section on the SCIM page](scim_setup.md#user-access-and-linking-setup).
-When a user tries to sign in with Group SSO, they will need an account that's configured with one of the following:
+When a user tries to sign in with Group SSO, they need an account that's configured with one of the following:
- [SCIM](scim_setup.md).
- [Group-managed accounts](group_managed_accounts.md).
@@ -203,18 +205,18 @@ When a user tries to sign in with Group SSO, they will need an account that's co
To link SAML to your existing GitLab.com account:
1. Sign in to your GitLab.com account.
-1. Locate and visit the **GitLab single sign-on URL** for the group you are signing in to. A group Admin can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the Identity Provider.
+1. Locate and visit the **GitLab single sign-on URL** for the group you're signing in to. A group Admin can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the Identity Provider.
1. Click **Authorize**.
1. Enter your credentials on the Identity Provider if prompted.
-1. You will be redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com.
+1. You are then redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com.
-On subsequent visits, you should be able to go [sign in to GitLab.com with SAML](#signing-in-to-gitlabcom-with-saml) or by visiting links directly. If the **enforce SSO** option is turned on, you will be redirected to sign in through the identity provider.
+On subsequent visits, you should be able to go [sign in to GitLab.com with SAML](#signing-in-to-gitlabcom-with-saml) or by visiting links directly. If the **enforce SSO** option is turned on, you are then redirected to sign in through the identity provider.
### Signing in to GitLab.com with SAML
1. Sign in to your identity provider.
1. From the list of apps, click on the "GitLab.com" app (The name is set by the administrator of the identity provider).
-1. You will be signed in to GitLab.com and redirected to the group.
+1. You are then signed in to GitLab.com and redirected to the group.
### Role
@@ -238,7 +240,7 @@ Users can unlink SAML for a group from their profile page. This can be helpful i
- You no longer want a group to be able to sign you in to GitLab.com.
- Your SAML NameID has changed and so GitLab can no longer find your user.
-For example, to unlink the `MyOrg` account, the following **Disconnect** button will be available under **Profile > Accounts**:
+For example, to unlink the `MyOrg` account, the following **Disconnect** button is available under **Profile > Accounts**:
![Unlink Group SAML](img/unlink_group_saml.png)
@@ -286,7 +288,7 @@ access.
| Service Provider | SAML considers GitLab to be a service provider. |
| Assertion | A piece of information about a user's identity, such as their name or role. Also know as claims or attributes. |
| SSO | Single Sign On. |
-| Assertion consumer service URL | The callback on GitLab where users will be redirected after successfully authenticating with the identity provider. |
+| Assertion consumer service URL | The callback on GitLab where users are redirected after successfully authenticating with the identity provider. |
| Issuer | How GitLab identifies itself to the identity provider. Also known as a "Relying party trust identifier". |
| Certificate fingerprint | Used to confirm that communications over SAML are secure by checking that the server is signing communications with the correct certificate. Also known as a certificate thumbprint. |
diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb
index 6f79e965cd5..5a506da0d05 100644
--- a/lib/gitlab/database/batch_count.rb
+++ b/lib/gitlab/database/batch_count.rb
@@ -49,6 +49,8 @@ module Gitlab
MAX_ALLOWED_LOOPS = 10_000
SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
ALLOWED_MODES = [:itself, :distinct].freeze
+ FALLBACK_FINISH = 0
+ OFFSET_BY_ONE = 1
# Each query should take < 500ms https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705
DEFAULT_DISTINCT_BATCH_SIZE = 10_000
@@ -65,7 +67,7 @@ module Gitlab
(@operation == :count && batch_size <= MIN_REQUIRED_BATCH_SIZE) ||
(@operation == :sum && batch_size < DEFAULT_SUM_BATCH_SIZE) ||
(finish - start) / batch_size >= MAX_ALLOWED_LOOPS ||
- start > finish
+ start >= finish
end
def count(batch_size: nil, mode: :itself, start: nil, finish: nil)
@@ -85,11 +87,13 @@ module Gitlab
results = nil
batch_start = start
- while batch_start <= finish
- batch_relation = build_relation_batch(batch_start, batch_start + batch_size, mode)
+ while batch_start < finish
+ batch_end = [batch_start + batch_size, finish].min
+ batch_relation = build_relation_batch(batch_start, batch_end, mode)
+
begin
results = merge_results(results, batch_relation.send(@operation, *@operation_args)) # rubocop:disable GitlabSecurity/PublicSend
- batch_start += batch_size
+ batch_start = batch_end
rescue ActiveRecord::QueryCanceled => error
# retry with a safe batch size & warmer cache
if batch_size >= 2 * MIN_REQUIRED_BATCH_SIZE
@@ -99,6 +103,7 @@ module Gitlab
return FALLBACK
end
end
+
sleep(SLEEP_TIME_IN_SECONDS)
end
@@ -138,7 +143,7 @@ module Gitlab
end
def actual_finish(finish)
- finish || @relation.unscope(:group, :having).maximum(@column) || 0
+ (finish || @relation.unscope(:group, :having).maximum(@column) || FALLBACK_FINISH) + OFFSET_BY_ONE
end
def check_mode!(mode)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8fc93e4810d..7ec22748d11 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -17794,7 +17794,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -21111,9 +21111,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -23088,6 +23085,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -24252,6 +24252,9 @@ msgstr ""
msgid "SecurityReports|Scan details"
msgstr ""
+msgid "SecurityReports|Scanner"
+msgstr ""
+
msgid "SecurityReports|Security Dashboard"
msgstr ""
@@ -31430,10 +31433,7 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
diff --git a/qa/qa/page/component/legacy_clone_panel.rb b/qa/qa/page/component/legacy_clone_panel.rb
index ebab9fd708c..f15d159a712 100644
--- a/qa/qa/page/component/legacy_clone_panel.rb
+++ b/qa/qa/page/component/legacy_clone_panel.rb
@@ -12,7 +12,7 @@ module QA
base.view 'app/views/shared/_clone_panel.html.haml' do
element :clone_dropdown
element :clone_options_dropdown, '.clone-options-dropdown' # rubocop:disable QA/ElementWithPattern
- element :project_repository_location, 'text_field_tag :project_clone' # rubocop:disable QA/ElementWithPattern
+ element :clone_url, 'text_field_tag :clone_url' # rubocop:disable QA/ElementWithPattern
end
end
@@ -28,7 +28,7 @@ module QA
end
def repository_location
- Git::Location.new(find('#project_clone').value)
+ Git::Location.new(find('#clone_url').value)
end
private
diff --git a/scripts/api/cancel_pipeline b/scripts/api/cancel_pipeline
deleted file mode 100755
index 0965877a69a..00000000000
--- a/scripts/api/cancel_pipeline
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'rubygems'
-require 'gitlab'
-require 'optparse'
-require_relative 'get_job_id'
-
-class CancelPipeline
- DEFAULT_OPTIONS = {
- project: ENV['CI_PROJECT_ID'],
- pipeline_id: ENV['CI_PIPELINE_ID'],
- api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
- }.freeze
-
- def initialize(options)
- @project = options.delete(:project)
- @pipeline_id = options.delete(:pipeline_id)
-
- Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
- config.private_token = options.delete(:api_token)
- end
- end
-
- def execute
- Gitlab.cancel_pipeline(project, pipeline_id)
- end
-
- private
-
- attr_reader :project, :pipeline_id
-end
-
-if $0 == __FILE__
- options = CancelPipeline::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
- options[:project] = value
- end
-
- opts.on("-i", "--pipeline-id PIPELINE_ID", String, "A pipeline ID (defaults to $CI_PIPELINE_ID)") do |value|
- options[:pipeline_id] = value
- end
-
- opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
- options[:api_token] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- CancelPipeline.new(options).execute
-end
diff --git a/scripts/api/download_job_artifact b/scripts/api/download_job_artifact
deleted file mode 100755
index 9ac24ff624d..00000000000
--- a/scripts/api/download_job_artifact
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'rubygems'
-require 'optparse'
-require 'fileutils'
-require 'uri'
-require 'cgi'
-require 'net/http'
-
-class ArtifactFinder
- DEFAULT_OPTIONS = {
- project: ENV['CI_PROJECT_ID'],
- api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
- }.freeze
-
- def initialize(options)
- @project = options.delete(:project)
- @job_id = options.delete(:job_id)
- @api_token = options.delete(:api_token)
- @artifact_path = options.delete(:artifact_path)
- end
-
- def execute
- url = "https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/jobs/#{job_id}/artifacts"
-
- if artifact_path
- FileUtils.mkdir_p(File.dirname(artifact_path))
- url += "/#{artifact_path}"
- end
-
- fetch(url)
- end
-
- private
-
- attr_reader :project, :job_id, :api_token, :artifact_path
-
- def fetch(uri_str, limit = 10)
- raise 'Too many HTTP redirects' if limit == 0
-
- uri = URI(uri_str)
- request = Net::HTTP::Get.new(uri)
- request['Private-Token'] = api_token
-
- Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
- http.request(request) do |response|
- case response
- when Net::HTTPSuccess then
- File.open(artifact_path || 'artifacts.zip', 'w') do |file|
- response.read_body(&file.method(:write))
- end
- when Net::HTTPRedirection then
- location = response['location']
- warn "Redirected (#{limit - 1} redirections remaining)."
- fetch(location, limit - 1)
- else
- raise "Unexpected response: #{response.value}"
- end
- end
- end
- end
-end
-
-if $0 == __FILE__
- options = ArtifactFinder::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
- options[:project] = value
- end
-
- opts.on("-j", "--job-id JOB_ID", String, "A job ID") do |value|
- options[:job_id] = value
- end
-
- opts.on("-a", "--artifact-path ARTIFACT_PATH", String, "A valid artifact path") do |value|
- options[:artifact_path] = value
- end
-
- opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
- options[:api_token] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- ArtifactFinder.new(options).execute
-end
diff --git a/scripts/api/get_job_id b/scripts/api/get_job_id
deleted file mode 100755
index 2324f6ca9d3..00000000000
--- a/scripts/api/get_job_id
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'rubygems'
-require 'gitlab'
-require 'optparse'
-
-class JobFinder
- DEFAULT_OPTIONS = {
- project: ENV['CI_PROJECT_ID'],
- pipeline_id: ENV['CI_PIPELINE_ID'],
- pipeline_query: {},
- job_query: {},
- api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
- }.freeze
-
- def initialize(options)
- @project = options.delete(:project)
- @pipeline_query = options.delete(:pipeline_query)
- @job_query = options.delete(:job_query)
- @pipeline_id = options.delete(:pipeline_id)
- @job_name = options.delete(:job_name)
-
- Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
- config.private_token = options.delete(:api_token)
- end
- end
-
- def execute
- find_job_with_filtered_pipelines || find_job_in_pipeline
- end
-
- private
-
- attr_reader :project, :pipeline_query, :job_query, :pipeline_id, :job_name
-
- def find_job_with_filtered_pipelines
- return if pipeline_query.empty?
-
- Gitlab.pipelines(project, pipeline_query_params).auto_paginate do |pipeline|
- Gitlab.pipeline_jobs(project, pipeline.id, job_query_params).auto_paginate do |job|
- return job if job.name == job_name # rubocop:disable Cop/AvoidReturnFromBlocks
- end
- end
-
- raise 'Job not found!'
- end
-
- def find_job_in_pipeline
- return unless pipeline_id
-
- Gitlab.pipeline_jobs(project, pipeline_id, job_query_params).auto_paginate do |job|
- return job if job.name == job_name # rubocop:disable Cop/AvoidReturnFromBlocks
- end
-
- raise 'Job not found!'
- end
-
- def pipeline_query_params
- @pipeline_query_params ||= { per_page: 100, **pipeline_query }
- end
-
- def job_query_params
- @job_query_params ||= { per_page: 100, **job_query }
- end
-end
-
-if $0 == __FILE__
- options = JobFinder::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
- options[:project] = value
- end
-
- opts.on("-i", "--pipeline-id pipeline_id", String, "A pipeline ID (defaults to $CI_PIPELINE_ID)") do |value|
- options[:pipeline_id] = value
- end
-
- opts.on("-q", "--pipeline-query pipeline_query", String, "Query to pass to the Pipeline API request") do |value|
- options[:pipeline_query].merge!(Hash[*value.split('=')])
- end
-
- opts.on("-Q", "--job-query job_query", String, "Query to pass to the Job API request") do |value|
- options[:job_query].merge!(Hash[*value.split('=')])
- end
-
- opts.on("-j", "--job-name job_name", String, "A job name that needs to exist in the found pipeline") do |value|
- options[:job_name] = value
- end
-
- opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
- options[:api_token] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- job = JobFinder.new(options).execute
-
- return if job.nil?
-
- puts job.id
-end
diff --git a/scripts/api/play_job b/scripts/api/play_job
deleted file mode 100755
index 199f7e65633..00000000000
--- a/scripts/api/play_job
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'rubygems'
-require 'gitlab'
-require 'optparse'
-require_relative 'get_job_id'
-
-class PlayJob
- DEFAULT_OPTIONS = {
- project: ENV['CI_PROJECT_ID'],
- pipeline_id: ENV['CI_PIPELINE_ID'],
- api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
- }.freeze
-
- def initialize(options)
- @project = options.delete(:project)
- @options = options
-
- Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
- config.private_token = options.fetch(:api_token)
- end
- end
-
- def execute
- job = JobFinder.new(project, options.slice(:api_token, :pipeline_id, :job_name).merge(scope: 'manual')).execute
-
- Gitlab.job_play(project, job.id)
- end
-
- private
-
- attr_reader :project, :options
-end
-
-if $0 == __FILE__
- options = PlayJob::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
- options[:project] = value
- end
-
- opts.on("-j", "--job-name JOB_NAME", String, "A job name that needs to exist in the found pipeline") do |value|
- options[:job_name] = value
- end
-
- opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
- options[:api_token] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- PlayJob.new(options).execute
-end
diff --git a/scripts/get-job-id b/scripts/get-job-id
new file mode 100755
index 00000000000..a5d34dc545b
--- /dev/null
+++ b/scripts/get-job-id
@@ -0,0 +1,43 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'gitlab'
+require 'optparse'
+
+#
+# Configure credentials to be used with gitlab gem
+#
+Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+ config.private_token = ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+end
+
+options = {}
+OptionParser.new do |opts|
+ opts.on("-s", "--scope=SCOPE", "Find job with matching scope") do |scope|
+ options[:scope] = scope
+ end
+end.parse!
+
+class PipelineJobFinder
+ def initialize(project_id, pipeline_id, job_name, options)
+ @project_id = project_id
+ @pipeline_id = pipeline_id
+ @job_name = job_name
+ @options = options
+ end
+
+ def execute
+ Gitlab.pipeline_jobs(@project_id, @pipeline_id, @options).auto_paginate do |job|
+ break job if job.name == @job_name
+ end
+ end
+end
+
+project_id, pipeline_id, job_name = ARGV
+
+job = PipelineJobFinder.new(project_id, pipeline_id, job_name, options).execute
+
+return if job.nil?
+
+puts job.id
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
index 0f14b702de2..5f003d032b7 100644
--- a/scripts/rspec_helpers.sh
+++ b/scripts/rspec_helpers.sh
@@ -1,39 +1,44 @@
#!/usr/bin/env bash
function retrieve_tests_metadata() {
- mkdir -p crystalball/ knapsack/ rspec_flaky/ rspec_profiling/
-
- local project_path="gitlab-org/gitlab"
- local test_metadata_job_id
-
- # Ruby
- test_metadata_job_id=$(scripts/api/get_job_id --project "${project_path}" -q "status=success" -q "ref=master" -q "username=gitlab-bot" -Q "scope=success" --job-name "update-tests-metadata")
+ mkdir -p knapsack/ rspec_flaky/ rspec_profiling/
if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then
- scripts/api/download_job_artifact --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
+ wget -O "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
fi
if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then
- scripts/api/download_job_artifact --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+ wget -O "${FLAKY_RSPEC_SUITE_REPORT_PATH}" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
fi
-
- # FIXME: We will need to find a pipeline where the $RSPEC_PACKED_TESTS_MAPPING_PATH.gz actually exists (Crystalball only runs every two-hours, but the `update-tests-metadata` runs for all `master` pipelines...).
- # if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then
- # (scripts/api/download_job_artifact --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
- # fi
- #
- # scripts/unpack-test-mapping "${RSPEC_PACKED_TESTS_MAPPING_PATH}" "${RSPEC_TESTS_MAPPING_PATH}"
}
function update_tests_metadata() {
echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
scripts/merge-reports "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" knapsack/rspec*.json
+ if [[ -n "${TESTS_METADATA_S3_BUCKET}" ]]; then
+ if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
+ scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
+ else
+ echo "Not uplaoding report to S3 as the pipeline is not a scheduled one."
+ fi
+ fi
+
rm -f knapsack/rspec*.json
- export FLAKY_RSPEC_GENERATE_REPORT="true"
scripts/merge-reports "${FLAKY_RSPEC_SUITE_REPORT_PATH}" rspec_flaky/all_*.json
+
+ export FLAKY_RSPEC_GENERATE_REPORT="true"
scripts/flaky_examples/prune-old-flaky-examples "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+
+ if [[ -n ${TESTS_METADATA_S3_BUCKET} ]]; then
+ if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
+ scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+ else
+ echo "Not uploading report to S3 as the pipeline is not a scheduled one."
+ fi
+ fi
+
rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json
if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
@@ -43,6 +48,16 @@ function update_tests_metadata() {
fi
}
+function retrieve_tests_mapping() {
+ mkdir -p crystalball/
+
+ if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then
+ (wget -O "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
+ fi
+
+ scripts/unpack-test-mapping "${RSPEC_PACKED_TESTS_MAPPING_PATH}" "${RSPEC_TESTS_MAPPING_PATH}"
+}
+
function update_tests_mapping() {
if ! crystalball_rspec_data_exists; then
echo "No crystalball rspec data found."
@@ -50,9 +65,20 @@ function update_tests_mapping() {
fi
scripts/generate-test-mapping "${RSPEC_TESTS_MAPPING_PATH}" crystalball/rspec*.yml
+
scripts/pack-test-mapping "${RSPEC_TESTS_MAPPING_PATH}" "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
+
gzip "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
- rm -f crystalball/rspec*.yml "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
+
+ if [[ -n "${TESTS_METADATA_S3_BUCKET}" ]]; then
+ if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
+ scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz"
+ else
+ echo "Not uploading report to S3 as the pipeline is not a scheduled one."
+ fi
+ fi
+
+ rm -f crystalball/rspec*.yml
}
function crystalball_rspec_data_exists() {
diff --git a/scripts/utils.sh b/scripts/utils.sh
index 4d6088e94a8..3829bcdf24e 100644
--- a/scripts/utils.sh
+++ b/scripts/utils.sh
@@ -87,14 +87,65 @@ function echosuccess() {
fi
}
+function get_job_id() {
+ local job_name="${1}"
+ local query_string="${2:+&${2}}"
+ local api_token="${API_TOKEN-${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}}"
+ if [ -z "${api_token}" ]; then
+ echoerr "Please provide an API token with \$API_TOKEN or \$GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN."
+ return
+ fi
+
+ local max_page=3
+ local page=1
+
+ while true; do
+ local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/jobs?per_page=100&page=${page}${query_string}"
+ echoinfo "GET ${url}"
+
+ local job_id
+ job_id=$(curl --silent --show-error --header "PRIVATE-TOKEN: ${api_token}" "${url}" | jq "map(select(.name == \"${job_name}\")) | map(.id) | last")
+ [[ "${job_id}" == "null" && "${page}" -lt "$max_page" ]] || break
+
+ let "page++"
+ done
+
+ if [[ "${job_id}" == "null" ]]; then # jq prints "null" for non-existent attribute
+ echoerr "The '${job_name}' job ID couldn't be retrieved!"
+ else
+ echoinfo "The '${job_name}' job ID is ${job_id}"
+ echo "${job_id}"
+ fi
+}
+
+function play_job() {
+ local job_name="${1}"
+ local job_id
+ job_id=$(get_job_id "${job_name}" "scope=manual");
+ if [ -z "${job_id}" ]; then return; fi
+
+ local api_token="${API_TOKEN-${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}}"
+ if [ -z "${api_token}" ]; then
+ echoerr "Please provide an API token with \$API_TOKEN or \$GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN."
+ return
+ fi
+
+ local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/jobs/${job_id}/play"
+ echoinfo "POST ${url}"
+
+ local job_url
+ job_url=$(curl --silent --show-error --request POST --header "PRIVATE-TOKEN: ${api_token}" "${url}" | jq ".web_url")
+ echoinfo "Manual job '${job_name}' started at: ${job_url}"
+}
+
function fail_pipeline_early() {
local dont_interrupt_me_job_id
- dont_interrupt_me_job_id=$(scripts/api/get_job_id --job-query "scope=success" --job-name "dont-interrupt-me")
+ dont_interrupt_me_job_id=$(get_job_id 'dont-interrupt-me' 'scope=success')
if [[ -n "${dont_interrupt_me_job_id}" ]]; then
echoinfo "This pipeline cannot be interrupted due to \`dont-interrupt-me\` job ${dont_interrupt_me_job_id}"
else
echoinfo "Failing pipeline early for fast feedback due to test failures in rspec fail-fast."
- scripts/api/cancel_pipeline
+ curl --request POST --header "PRIVATE-TOKEN: ${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}" "https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/cancel"
fi
}
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
index 637ac72eca2..ff969a7786d 100644
--- a/spec/features/profiles/active_sessions_spec.rb
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -56,8 +56,8 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
visit profile_active_sessions_path
expect(page).to(
- have_selector('ul.list-group li.list-group-item', { text: 'Signed in on',
- count: 2 }))
+ have_selector('ul.list-group li.list-group-item', text: 'Signed in on',
+ count: 2))
expect(page).to have_content(
'127.0.0.1 ' \
diff --git a/spec/features/projects/show/no_password_spec.rb b/spec/features/projects/show/no_password_spec.rb
index 79cd65e5406..d18ff75b324 100644
--- a/spec/features/projects/show/no_password_spec.rb
+++ b/spec/features/projects/show/no_password_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'No Password Alert' do
let(:user) { create(:user) }
it 'shows no alert' do
- expect(page).not_to have_content "You won't be able to pull or push project code via HTTP until you set a password on your account"
+ expect(page).not_to have_content "You won't be able to pull or push repositories via HTTP until you set a password on your account"
end
end
@@ -23,7 +23,7 @@ RSpec.describe 'No Password Alert' do
let(:user) { create(:user, password_automatically_set: true) }
it 'shows a password alert' do
- expect(page).to have_content "You won't be able to pull or push project code via HTTP until you set a password on your account"
+ expect(page).to have_content "You won't be able to pull or push repositories via HTTP until you set a password on your account"
end
end
end
@@ -41,7 +41,7 @@ RSpec.describe 'No Password Alert' do
gitlab_sign_in_via('saml', user, 'my-uid')
visit project_path(project)
- expect(page).to have_content "You won't be able to pull or push project code via HTTP until you create a personal access token on your account"
+ expect(page).to have_content "You won't be able to pull or push repositories via HTTP until you create a personal access token on your account"
end
end
@@ -51,7 +51,7 @@ RSpec.describe 'No Password Alert' do
gitlab_sign_in_via('saml', user, 'my-uid')
visit project_path(project)
- expect(page).not_to have_content "You won't be able to pull or push project code via HTTP until you create a personal access token on your account"
+ expect(page).not_to have_content "You won't be able to pull or push repositories via HTTP until you create a personal access token on your account"
end
end
end
@@ -65,7 +65,7 @@ RSpec.describe 'No Password Alert' do
end
it 'shows no alert' do
- expect(page).not_to have_content "You won't be able to pull or push project code via HTTP until you"
+ expect(page).not_to have_content "You won't be able to pull or push repositories via HTTP until you"
end
end
end
diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
deleted file mode 100644
index 83679c6bd1d..00000000000
--- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Projects > Wiki > User views Git access wiki page' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, :public) }
- let(:wiki_page) { create(:wiki_page, wiki: project.wiki, title: 'home', content: '[some link](other-page)') }
-
- before do
- sign_in(user)
- end
-
- it 'Visit Wiki Page Current Commit' do
- visit project_wiki_path(project, wiki_page)
-
- click_link 'Clone repository'
- expect(page).to have_text("Clone repository #{project.wiki.full_path}")
- expect(page).to have_text(project.wiki.http_url_to_repo)
- end
-end
diff --git a/spec/features/projects/wikis_spec.rb b/spec/features/projects/wikis_spec.rb
index 1c66ad81145..621f8c71b20 100644
--- a/spec/features/projects/wikis_spec.rb
+++ b/spec/features/projects/wikis_spec.rb
@@ -17,4 +17,5 @@ RSpec.describe 'Project wikis' do
it_behaves_like 'User views a wiki page'
it_behaves_like 'User views wiki pages'
it_behaves_like 'User views wiki sidebar'
+ it_behaves_like 'User views Git access wiki page'
end
diff --git a/spec/finders/ci/pipeline_schedules_finder_spec.rb b/spec/finders/ci/pipeline_schedules_finder_spec.rb
index 57842bbecd7..535c684289e 100644
--- a/spec/finders/ci/pipeline_schedules_finder_spec.rb
+++ b/spec/finders/ci/pipeline_schedules_finder_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Ci::PipelineSchedulesFinder do
let!(:active_schedule) { create(:ci_pipeline_schedule, project: project) }
let!(:inactive_schedule) { create(:ci_pipeline_schedule, :inactive, project: project) }
- subject { described_class.new(project).execute(params) }
+ subject { described_class.new(project).execute(**params) }
describe "#execute" do
context 'when the scope is nil' do
diff --git a/spec/finders/feature_flags_finder_spec.rb b/spec/finders/feature_flags_finder_spec.rb
index 870447a1286..cab1094672b 100644
--- a/spec/finders/feature_flags_finder_spec.rb
+++ b/spec/finders/feature_flags_finder_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe FeatureFlagsFinder do
end
describe '#execute' do
- subject { finder.execute(args) }
+ subject { finder.execute(**args) }
let!(:feature_flag_1) { create(:operations_feature_flag, name: 'flag-a', project: project) }
let!(:feature_flag_2) { create(:operations_feature_flag, name: 'flag-b', project: project) }
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
index 5326d63cb8a..f9490ac77ff 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
@@ -1,4 +1,5 @@
import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlFormCheckbox } from '@gitlab/ui';
import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue';
import { SQUASH_BEFORE_MERGE } from '~/vue_merge_request_widget/i18n';
@@ -20,17 +21,15 @@ describe('Squash before merge component', () => {
wrapper.destroy();
});
- const findLabel = () => wrapper.find('[data-testid="squashLabel"]');
+ const findCheckbox = () => wrapper.find(GlFormCheckbox);
describe('checkbox', () => {
- const findCheckbox = () => wrapper.find('.js-squash-checkbox');
-
it('is unchecked if passed value prop is false', () => {
createComponent({
value: false,
});
- expect(findCheckbox().element.checked).toBeFalsy();
+ expect(findCheckbox().vm.$attrs.checked).toBe(false);
});
it('is checked if passed value prop is true', () => {
@@ -38,22 +37,7 @@ describe('Squash before merge component', () => {
value: true,
});
- expect(findCheckbox().element.checked).toBeTruthy();
- });
-
- it('changes value on click', done => {
- createComponent({
- value: false,
- });
-
- findCheckbox().element.checked = true;
-
- findCheckbox().trigger('change');
-
- wrapper.vm.$nextTick(() => {
- expect(findCheckbox().element.checked).toBeTruthy();
- done();
- });
+ expect(findCheckbox().vm.$attrs.checked).toBe(true);
});
it('is disabled if isDisabled prop is true', () => {
@@ -62,31 +46,12 @@ describe('Squash before merge component', () => {
isDisabled: true,
});
- expect(findCheckbox().attributes('disabled')).toBeTruthy();
- });
- });
-
- describe('label', () => {
- describe.each`
- isDisabled | expectation
- ${true} | ${'grays out text if it is true'}
- ${false} | ${'does not gray out text if it is false'}
- `('isDisabled prop', ({ isDisabled, expectation }) => {
- beforeEach(() => {
- createComponent({
- value: false,
- isDisabled,
- });
- });
-
- it(expectation, () => {
- expect(findLabel().classes('gl-text-gray-400')).toBe(isDisabled);
- });
+ expect(findCheckbox().vm.$attrs.disabled).toBe(true);
});
});
describe('tooltip', () => {
- const tooltipTitle = () => findLabel().attributes('title');
+ const tooltipTitle = () => findCheckbox().attributes('title');
it('does not render when isDisabled is false', () => {
createComponent({
@@ -114,7 +79,7 @@ describe('Squash before merge component', () => {
const aboutLink = wrapper.find('a');
- expect(aboutLink.exists()).toBeFalsy();
+ expect(aboutLink.exists()).toBe(false);
});
it('is rendered if help path is passed', () => {
@@ -125,7 +90,7 @@ describe('Squash before merge component', () => {
const aboutLink = wrapper.find('a');
- expect(aboutLink.exists()).toBeTruthy();
+ expect(aboutLink.exists()).toBe(true);
});
it('should have a correct help path if passed', () => {
diff --git a/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb b/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
index f74f9186743..acd7070d0d3 100644
--- a/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::AlertManagement::HttpIntegration::Destroy do
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has access to project' do
before do
diff --git a/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb b/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
index d3ffb2abb47..96974c2aa6f 100644
--- a/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::AlertManagement::HttpIntegration::ResetToken do
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has sufficient access to project' do
before do
diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
index 45d92695e06..ddf23909035 100644
--- a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
+++ b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::AlertManagement::PrometheusIntegration::ResetToken do
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has sufficient access to project' do
before do
diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
index 08761ce64c2..8465393f299 100644
--- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
+++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do
specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alert) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has access to project' do
before do
diff --git a/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb b/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
index 71c43ed826c..ded5776b3cb 100644
--- a/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
+++ b/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
@@ -34,18 +34,18 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
end
subject do
- mutation.resolve(params.merge(move_params))
+ mutation.resolve(**params.merge(move_params))
end
describe '#ready?' do
it 'raises an error if required arguments are missing' do
- expect { mutation.ready?(params) }
+ expect { mutation.ready?(**params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, "At least one of the arguments " \
"fromListId, toListId, afterId or beforeId is required")
end
it 'raises an error if only one of fromListId and toListId is present' do
- expect { mutation.ready?(params.merge(from_list_id: list1.id)) }
+ expect { mutation.ready?(**params.merge(from_list_id: list1.id)) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError,
'Both fromListId and toListId must be present'
)
diff --git a/spec/graphql/mutations/container_expiration_policies/update_spec.rb b/spec/graphql/mutations/container_expiration_policies/update_spec.rb
index 9c6016e0af4..e22fb951172 100644
--- a/spec/graphql/mutations/container_expiration_policies/update_spec.rb
+++ b/spec/graphql/mutations/container_expiration_policies/update_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Mutations::ContainerExpirationPolicies::Update do
specify { expect(described_class).to require_graphql_authorizations(:destroy_container_image) }
describe '#resolve' do
- subject { described_class.new(object: project, context: { current_user: user }, field: nil).resolve(params) }
+ subject { described_class.new(object: project, context: { current_user: user }, field: nil).resolve(**params) }
RSpec.shared_examples 'returning a success' do
it 'returns the container expiration policy with no errors' do
diff --git a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
index 2e5d41a8f1e..162b1249ab5 100644
--- a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
+++ b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::Discussions::ToggleResolve do
describe '#resolve' do
subject do
- mutation.resolve({ id: id_arg, resolve: resolve_arg })
+ mutation.resolve(id: id_arg, resolve: resolve_arg)
end
let(:id_arg) { discussion.to_global_id.to_s }
diff --git a/spec/graphql/mutations/issues/create_spec.rb b/spec/graphql/mutations/issues/create_spec.rb
index 57658f6b358..422ad40a9cb 100644
--- a/spec/graphql/mutations/issues/create_spec.rb
+++ b/spec/graphql/mutations/issues/create_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Mutations::Issues::Create do
project.add_guest(assignee2)
end
- subject { mutation.resolve(mutation_params) }
+ subject { mutation.resolve(**mutation_params) }
context 'when the user does not have permission to create an issue' do
it 'raises an error' do
@@ -117,7 +117,7 @@ RSpec.describe Mutations::Issues::Create do
end
it 'raises exception when mutually exclusive params are given' do
- expect { mutation.ready?(mutation_params) }
+ expect { mutation.ready?(**mutation_params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/)
end
end
@@ -128,7 +128,7 @@ RSpec.describe Mutations::Issues::Create do
end
it 'raises exception when mutually exclusive params are given' do
- expect { mutation.ready?(mutation_params) }
+ expect { mutation.ready?(**mutation_params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /to resolve a discussion please also provide `merge_request_to_resolve_discussions_of` parameter/)
end
end
@@ -139,7 +139,7 @@ RSpec.describe Mutations::Issues::Create do
end
it 'raises exception when mutually exclusive params are given' do
- expect { mutation.ready?(mutation_params) }.not_to raise_error
+ expect { mutation.ready?(**mutation_params) }.not_to raise_error
end
end
end
diff --git a/spec/graphql/mutations/labels/create_spec.rb b/spec/graphql/mutations/labels/create_spec.rb
index 8b284816d63..b2dd94f31bb 100644
--- a/spec/graphql/mutations/labels/create_spec.rb
+++ b/spec/graphql/mutations/labels/create_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe Mutations::Labels::Create do
end
describe '#ready?' do
- subject { mutation.ready?(attributes.merge(extra_params)) }
+ subject { mutation.ready?(**attributes.merge(extra_params)) }
context 'when passing both project_path and group_path' do
let(:extra_params) { { project_path: 'foo', group_path: 'bar' } }
diff --git a/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb b/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb
index 8c22e1a1cb6..d88b196cbff 100644
--- a/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb
+++ b/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Mutations::Notes::RepositionImageDiffNote do
describe '#resolve' do
subject do
- mutation.resolve({ note: note, position: new_position })
+ mutation.resolve(note: note, position: new_position)
end
let_it_be(:noteable) { create(:merge_request) }
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index a557e9e04da..c7470f31ad8 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -116,9 +116,9 @@ RSpec.describe ApplicationHelper do
Time.use_zone('UTC') { example.run }
end
- def element(*arguments)
+ def element(**arguments)
@time = Time.zone.parse('2015-07-02 08:23')
- element = helper.time_ago_with_tooltip(@time, *arguments)
+ element = helper.time_ago_with_tooltip(@time, **arguments)
Nokogiri::HTML::DocumentFragment.parse(element).first_element_child
end
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index 6a5cb73281e..ecb9c98b1bf 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -89,7 +89,7 @@ RSpec.describe ButtonHelper do
it 'shows a warning on the dropdown description' do
description = element.search('.dropdown-menu-inner-content').first
- expect(description.inner_text).to eq "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+ expect(description.inner_text).to eq "You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
end
end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 34af3ce7e5e..208fba4a7f9 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -104,6 +104,37 @@ RSpec.describe SearchHelper do
})
end
+ it 'includes the users recently viewed issues with the exact same name', :aggregate_failures do
+ recent_issues = instance_double(::Gitlab::Search::RecentIssues)
+ expect(::Gitlab::Search::RecentIssues).to receive(:new).with(user: user).and_return(recent_issues)
+ project1 = create(:project, namespace: user.namespace)
+ project2 = create(:project, namespace: user.namespace)
+ issue1 = create(:issue, title: 'issue same_name', project: project1)
+ issue2 = create(:issue, title: 'issue same_name', project: project2)
+
+ expect(recent_issues).to receive(:search).with('the search term').and_return(Issue.id_in_ordered([issue1.id, issue2.id]))
+
+ results = search_autocomplete_opts("the search term")
+
+ expect(results.count).to eq(2)
+
+ expect(results[0]).to include({
+ category: 'Recent issues',
+ id: issue1.id,
+ label: 'issue same_name',
+ url: Gitlab::Routing.url_helpers.project_issue_path(issue1.project, issue1),
+ avatar_url: '' # This project didn't have an avatar so set this to ''
+ })
+
+ expect(results[1]).to include({
+ category: 'Recent issues',
+ id: issue2.id,
+ label: 'issue same_name',
+ url: Gitlab::Routing.url_helpers.project_issue_path(issue2.project, issue2),
+ avatar_url: '' # This project didn't have an avatar so set this to ''
+ })
+ end
+
it 'includes the users recently viewed merge requests', :aggregate_failures do
recent_merge_requests = instance_double(::Gitlab::Search::RecentMergeRequests)
expect(::Gitlab::Search::RecentMergeRequests).to receive(:new).with(user: user).and_return(recent_merge_requests)
diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
index 05d744d95e2..a99bdcc9a0f 100644
--- a/spec/lib/gitlab/cleanup/project_uploads_spec.rb
+++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
@@ -15,10 +15,10 @@ RSpec.describe Gitlab::Cleanup::ProjectUploads do
describe '#run!' do
shared_examples_for 'moves the file' do
shared_examples_for 'a real run' do
- let(:args) { [dry_run: false] }
+ let(:args) { { dry_run: false } }
it 'moves the file to its proper location' do
- subject.run!(*args)
+ subject.run!(**args)
expect(File.exist?(path)).to be_falsey
expect(File.exist?(new_path)).to be_truthy
@@ -28,13 +28,13 @@ RSpec.describe Gitlab::Cleanup::ProjectUploads do
expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up...")
expect(logger).to receive(:info).with("Did #{action}")
- subject.run!(*args)
+ subject.run!(**args)
end
end
shared_examples_for 'a dry run' do
it 'does not move the file' do
- subject.run!(*args)
+ subject.run!(**args)
expect(File.exist?(path)).to be_truthy
expect(File.exist?(new_path)).to be_falsey
@@ -44,30 +44,30 @@ RSpec.describe Gitlab::Cleanup::ProjectUploads do
expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up. Dry run...")
expect(logger).to receive(:info).with("Can #{action}")
- subject.run!(*args)
+ subject.run!(**args)
end
end
context 'when dry_run is false' do
- let(:args) { [dry_run: false] }
+ let(:args) { { dry_run: false } }
it_behaves_like 'a real run'
end
context 'when dry_run is nil' do
- let(:args) { [dry_run: nil] }
+ let(:args) { { dry_run: nil } }
it_behaves_like 'a real run'
end
context 'when dry_run is true' do
- let(:args) { [dry_run: true] }
+ let(:args) { { dry_run: true } }
it_behaves_like 'a dry run'
end
context 'with dry_run not specified' do
- let(:args) { [] }
+ let(:args) { {} }
it_behaves_like 'a dry run'
end
diff --git a/spec/lib/gitlab/database/batch_count_spec.rb b/spec/lib/gitlab/database/batch_count_spec.rb
index a1cc759e011..29688b18e94 100644
--- a/spec/lib/gitlab/database/batch_count_spec.rb
+++ b/spec/lib/gitlab/database/batch_count_spec.rb
@@ -130,6 +130,16 @@ RSpec.describe Gitlab::Database::BatchCount do
expect(described_class.batch_count(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq(5)
end
+ it 'stops counting when finish value is reached' do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ expect(described_class.batch_count(model,
+ start: model.minimum(:id),
+ finish: model.maximum(:id) - 1, # Do not count the last record
+ batch_size: model.count - 2 # Ensure there are multiple batches
+ )).to eq(model.count - 1)
+ end
+
it "defaults the batch size to #{Gitlab::Database::BatchCounter::DEFAULT_BATCH_SIZE}" do
min_id = model.minimum(:id)
relation = instance_double(ActiveRecord::Relation)
@@ -242,6 +252,19 @@ RSpec.describe Gitlab::Database::BatchCount do
expect(described_class.batch_distinct_count(model, column, start: model.minimum(column), finish: model.maximum(column))).to eq(2)
end
+ it 'stops counting when finish value is reached' do
+ # Create a new unique author that should not be counted
+ create(:issue)
+
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ expect(described_class.batch_distinct_count(model, column,
+ start: User.minimum(:id),
+ finish: User.maximum(:id) - 1, # Do not count the newly created issue
+ batch_size: model.count - 2 # Ensure there are multiple batches
+ )).to eq(2)
+ end
+
it 'counts with User min and max as start and finish' do
expect(described_class.batch_distinct_count(model, column, start: User.minimum(:id), finish: User.maximum(:id))).to eq(2)
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index b09bd9dff1b..36f7d89dd0f 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -357,7 +357,7 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
end
it 'sends an RPC request with the correct payload' do
- expect(client.commits_by_message(query, options)).to match_array(wrap_commits(commits))
+ expect(client.commits_by_message(query, **options)).to match_array(wrap_commits(commits))
end
end
diff --git a/spec/migrations/ensure_u2f_registrations_migrated_spec.rb b/spec/migrations/ensure_u2f_registrations_migrated_spec.rb
new file mode 100644
index 00000000000..77eab3b829a
--- /dev/null
+++ b/spec/migrations/ensure_u2f_registrations_migrated_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20201026185514_ensure_u2f_registrations_migrated.rb')
+
+RSpec.describe EnsureU2fRegistrationsMigrated, schema: 20201022144501 do
+ let(:u2f_registrations) { table(:u2f_registrations) }
+ let(:webauthn_registrations) { table(:webauthn_registrations) }
+ let(:users) { table(:users) }
+
+ let(:user) { users.create!(email: 'email@email.com', name: 'foo', username: 'foo', projects_limit: 0) }
+
+ before do
+ create_u2f_registration(1, 'reg1')
+ create_u2f_registration(2, 'reg2')
+ webauthn_registrations.create!({ name: 'reg1', u2f_registration_id: 1, credential_xid: '', public_key: '', user_id: user.id })
+ end
+
+ it 'correctly migrates u2f registrations previously not migrated' do
+ expect { migrate! }.to change { webauthn_registrations.count }.from(1).to(2)
+ end
+
+ it 'migrates all valid u2f registrations depite errors' do
+ create_u2f_registration(3, 'reg3', 'invalid!')
+ create_u2f_registration(4, 'reg4')
+
+ expect { migrate! }.to change { webauthn_registrations.count }.from(1).to(3)
+ end
+
+ def create_u2f_registration(id, name, public_key = nil)
+ device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5), { key_handle: SecureRandom.random_bytes(255) })
+ public_key ||= Base64.strict_encode64(device.origin_public_key_raw)
+ u2f_registrations.create!({ id: id,
+ certificate: Base64.strict_encode64(device.cert_raw),
+ key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
+ public_key: public_key,
+ counter: 5,
+ name: name,
+ user_id: user.id })
+ end
+end
diff --git a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
index 71be0c30f5a..4d8da50f8f0 100644
--- a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
@@ -242,7 +242,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
}
expect { authorize_artifacts_with_token_in_headers(artifact_type: :lsif) }
- .to change { Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(tracking_params) }
+ .to change { Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**tracking_params) }
.by(1)
end
end
diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb
index 9d422ebbce2..d45e24241b2 100644
--- a/spec/requests/api/go_proxy_spec.rb
+++ b/spec/requests/api/go_proxy_spec.rb
@@ -428,7 +428,7 @@ RSpec.describe API::GoProxy do
context 'with a non-existent project' do
def get_resource(user = nil, **params)
- get api("/projects/not%2fa%2fproject/packages/go/#{base}/@v/list", user, params)
+ get api("/projects/not%2fa%2fproject/packages/go/#{base}/@v/list", user, **params)
end
describe 'GET /projects/:id/packages/go/*module_name/@v/list' do
@@ -465,7 +465,7 @@ RSpec.describe API::GoProxy do
end
def get_resource(user = nil, headers: {}, **params)
- get api("/projects/#{project.id}/packages/go/#{module_name}/@v/#{resource}", user, params), headers: headers
+ get api("/projects/#{project.id}/packages/go/#{module_name}/@v/#{resource}", user, **params), headers: headers
end
def fmt_pseudo_version(prefix, commit)
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index 3c67c15f10a..17b2c7b38e1 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe EventCreateService do
tracking_params = { event_action: event_action, date_from: Date.yesterday, date_to: Date.today }
expect { subject }
- .to change { Gitlab::UsageDataCounters::TrackUniqueEvents.count_unique_events(tracking_params) }
+ .to change { Gitlab::UsageDataCounters::TrackUniqueEvents.count_unique_events(**tracking_params) }
.by(1)
end
end
@@ -386,7 +386,7 @@ RSpec.describe EventCreateService do
counter_class = Gitlab::UsageDataCounters::TrackUniqueEvents
tracking_params = { event_action: event_action, date_from: Date.yesterday, date_to: Date.today }
- expect { subject }.not_to change { counter_class.count_unique_events(tracking_params) }
+ expect { subject }.not_to change { counter_class.count_unique_events(**tracking_params) }
end
end
end
diff --git a/spec/services/projects/move_access_service_spec.rb b/spec/services/projects/move_access_service_spec.rb
index 02f80988dd1..90167ffebed 100644
--- a/spec/services/projects/move_access_service_spec.rb
+++ b/spec/services/projects/move_access_service_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe Projects::MoveAccessService do
it 'does not remove remaining memberships' do
target_project.add_maintainer(maintainer_user)
- subject.execute(project_with_access, options)
+ subject.execute(project_with_access, **options)
expect(project_with_access.project_members.count).not_to eq 0
end
@@ -99,7 +99,7 @@ RSpec.describe Projects::MoveAccessService do
it 'does not remove remaining group links' do
target_project.project_group_links.create!(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
- subject.execute(project_with_access, options)
+ subject.execute(project_with_access, **options)
expect(project_with_access.project_group_links.count).not_to eq 0
end
@@ -107,7 +107,7 @@ RSpec.describe Projects::MoveAccessService do
it 'does not remove remaining authorizations' do
target_project.add_developer(developer_user)
- subject.execute(project_with_access, options)
+ subject.execute(project_with_access, **options)
expect(project_with_access.project_authorizations.count).not_to eq 0
end
diff --git a/spec/services/projects/move_deploy_keys_projects_service_spec.rb b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
index e69b4dd4fc7..bd93b80f712 100644
--- a/spec/services/projects/move_deploy_keys_projects_service_spec.rb
+++ b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Projects::MoveDeployKeysProjectsService do
it 'does not remove remaining deploy keys projects' do
target_project.deploy_keys << project_with_deploy_keys.deploy_keys.first
- subject.execute(project_with_deploy_keys, options)
+ subject.execute(project_with_deploy_keys, **options)
expect(project_with_deploy_keys.deploy_keys_projects.count).not_to eq 0
end
diff --git a/spec/services/projects/move_lfs_objects_projects_service_spec.rb b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
index b73286fba9a..e3df5fed9cf 100644
--- a/spec/services/projects/move_lfs_objects_projects_service_spec.rb
+++ b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Projects::MoveLfsObjectsProjectsService do
it 'does not remove remaining lfs objects' do
target_project.lfs_objects << project_with_lfs_objects.lfs_objects.first(2)
- subject.execute(project_with_lfs_objects, options)
+ subject.execute(project_with_lfs_objects, **options)
expect(project_with_lfs_objects.lfs_objects.count).not_to eq 0
end
diff --git a/spec/services/projects/move_notification_settings_service_spec.rb b/spec/services/projects/move_notification_settings_service_spec.rb
index 7c9f1dd30d2..e381ae7590f 100644
--- a/spec/services/projects/move_notification_settings_service_spec.rb
+++ b/spec/services/projects/move_notification_settings_service_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe Projects::MoveNotificationSettingsService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining notification settings' do
- subject.execute(project_with_notifications, options)
+ subject.execute(project_with_notifications, **options)
expect(project_with_notifications.notification_settings.count).not_to eq 0
end
diff --git a/spec/services/projects/move_project_authorizations_service_spec.rb b/spec/services/projects/move_project_authorizations_service_spec.rb
index a37b4d807a0..d47b13ca939 100644
--- a/spec/services/projects/move_project_authorizations_service_spec.rb
+++ b/spec/services/projects/move_project_authorizations_service_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe Projects::MoveProjectAuthorizationsService do
target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
- subject.execute(project_with_users, options)
+ subject.execute(project_with_users, **options)
expect(project_with_users.project_authorizations.count).not_to eq 0
end
diff --git a/spec/services/projects/move_project_group_links_service_spec.rb b/spec/services/projects/move_project_group_links_service_spec.rb
index 6304eded8d3..1fca96a0367 100644
--- a/spec/services/projects/move_project_group_links_service_spec.rb
+++ b/spec/services/projects/move_project_group_links_service_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe Projects::MoveProjectGroupLinksService do
target_project.project_group_links.create!(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
target_project.project_group_links.create!(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
- subject.execute(project_with_groups, options)
+ subject.execute(project_with_groups, **options)
expect(project_with_groups.project_group_links.count).not_to eq 0
end
diff --git a/spec/services/projects/move_project_members_service_spec.rb b/spec/services/projects/move_project_members_service_spec.rb
index f14f00e3866..8fbd0ba3270 100644
--- a/spec/services/projects/move_project_members_service_spec.rb
+++ b/spec/services/projects/move_project_members_service_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe Projects::MoveProjectMembersService do
target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
- subject.execute(project_with_users, options)
+ subject.execute(project_with_users, **options)
expect(project_with_users.project_members.count).not_to eq 0
end
diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb
index d8ade0fbbda..3e7594bd30f 100644
--- a/spec/services/suggestions/apply_service_spec.rb
+++ b/spec/services/suggestions/apply_service_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Suggestions::ApplyService do
position_args = args.slice(:old_path, :new_path, :old_line, :new_line)
content_args = args.slice(:from_content, :to_content)
- position = build_position(position_args)
+ position = build_position(**position_args)
diff_note = create(:diff_note_on_merge_request,
noteable: merge_request,
diff --git a/spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb
new file mode 100644
index 00000000000..d3d2a36147d
--- /dev/null
+++ b/spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'User views Git access wiki page' do
+ let(:wiki_page) { create(:wiki_page, wiki: wiki) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'shows the correct clone URLs', :js do
+ visit wiki_page_path(wiki, wiki_page)
+ click_link 'Clone repository'
+
+ expect(page).to have_text("Clone repository #{wiki.full_path}")
+
+ within('.git-clone-holder') do
+ expect(page).to have_css('#clone-dropdown', text: 'HTTP')
+ expect(page).to have_field('clone_url', with: wiki.http_url_to_repo)
+
+ click_link 'HTTP' # open the dropdown
+ click_link 'SSH' # select the dropdown item
+
+ expect(page).to have_css('#clone-dropdown', text: 'SSH')
+ expect(page).to have_field('clone_url', with: wiki.ssh_url_to_repo)
+ end
+ end
+end