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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-11-05 21:12:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-11-05 21:12:21 +0300
commit25307dda309ede41ea2e67f16f6de25d0ec1c40e (patch)
tree8684e28ace0cb742e45a041710dc833fb0b0eadc
parent81a37f05815a4c731a2d2c93302ddc554444b637 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/Problem Validation.md8
-rw-r--r--CHANGELOG.md9
-rw-r--r--app/assets/javascripts/clusters/agents/components/show.vue1
-rw-r--r--app/assets/javascripts/clusters/agents/index.js2
-rw-r--r--app/assets/javascripts/google_cloud/components/app.vue50
-rw-r--r--app/assets/javascripts/google_cloud/components/incubation_banner.vue44
-rw-r--r--app/assets/javascripts/google_cloud/components/service_accounts.vue65
-rw-r--r--app/assets/javascripts/google_cloud/index.js19
-rw-r--r--app/assets/javascripts/pages/projects/google_cloud/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/project.js8
-rw-r--r--app/assets/javascripts/projects/components/shared/delete_button.vue11
-rw-r--r--app/assets/javascripts/terms/index.js2
-rw-r--r--app/controllers/projects/cluster_agents_controller.rb6
-rw-r--r--app/helpers/projects_helper.rb33
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/models/merge_request_diff_commit.rb1
-rw-r--r--app/models/user.rb13
-rw-r--r--app/views/shared/_no_password.html.haml16
-rw-r--r--app/views/shared/_no_ssh.html.haml14
-rw-r--r--config/feature_flags/development/cluster_vulnerabilities.yml8
-rw-r--r--config/feature_flags/development/terms_of_service_vue.yml2
-rw-r--r--doc/administration/feature_flags.md6
-rw-r--r--doc/administration/sidekiq.md14
-rw-r--r--doc/user/project/badges.md17
-rw-r--r--doc/user/project/wiki/group.md17
-rw-r--r--lib/bulk_imports/groups/graphql/get_milestones_query.rb56
-rw-r--r--lib/gitlab/background_migration/fix_merge_request_diff_commit_users.rb53
-rw-r--r--locale/gitlab.pot66
-rw-r--r--package.json4
-rw-r--r--spec/features/projects/show/no_password_spec.rb11
-rw-r--r--spec/features/projects_spec.rb2
-rw-r--r--spec/frontend/clusters/agents/components/show_spec.js63
-rw-r--r--spec/frontend/google_cloud/components/app_spec.js66
-rw-r--r--spec/frontend/google_cloud/components/incubation_banner_spec.js60
-rw-r--r--spec/frontend/google_cloud/components/service_accounts_spec.js79
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap1
-rw-r--r--spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap4
-rw-r--r--spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap4
-rw-r--r--spec/helpers/projects_helper_spec.rb14
-rw-r--r--spec/lib/bulk_imports/groups/graphql/get_milestones_query_spec.rb35
-rw-r--r--spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb30
-rw-r--r--spec/models/commit_status_spec.rb16
-rw-r--r--spec/models/user_spec.rb33
-rw-r--r--yarn.lock16
44 files changed, 778 insertions, 206 deletions
diff --git a/.gitlab/issue_templates/Problem Validation.md b/.gitlab/issue_templates/Problem Validation.md
index 5d417c5a26d..3f92510b6af 100644
--- a/.gitlab/issue_templates/Problem Validation.md
+++ b/.gitlab/issue_templates/Problem Validation.md
@@ -1,3 +1,7 @@
+<!-- This template is used as a starting point for understing and articulating a customer problem.
+Learn more about it in the handbook: https://about.gitlab.com/handbook/product-development-flow/#validation-phase-2-problem-validation
+-->
+
## Problem Statement
<!-- What is the problem we hope to validate? Reference how to write a real customer problem statement at https://productcoalition.com/how-to-write-a-good-customer-problem-statement-a815f80189ba for guidance. -->
@@ -45,4 +49,8 @@ For example, if the solution will take a product manager, designer, and engineer
- [ ] The problem is well described and detailed with necessary requirements for product design to understand the problem
- [ ] The problem is well described and detailed with necessary requirements for engineering to understand the problem
+## Research Issue
+
+<!-- Link to the Problem Validation Research issue that will be executed by the UX Researcher. https://gitlab.com/gitlab-org/ux-research/ -->
+
/label ~"workflow::validation backlog" ~devops:: ~category: ~group::
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c670421d88..6b8363ed543 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2293,6 +2293,15 @@ entry.
- [Remove diffs gradual load feature flag](gitlab-org/gitlab@027d7c4327b5b6205a84281239027273517bf81b) ([merge request](gitlab-org/gitlab!55478))
- [Remove partial index for Hashed Storage migration](gitlab-org/gitlab@3ed017a1023d7b0941a7606b69e6caee8d22f15c) ([merge request](gitlab-org/gitlab!62920))
+## 14.0.12 (2021-11-05)
+
+### Changed (4 changes)
+
+- [Geo: Alternate redownload and normal design sync attempts](gitlab-org/gitlab@ed34172e5c7425316480efb732a9429e93e81017) ([merge request](gitlab-org/gitlab!73722)) **GitLab Enterprise Edition**
+- [Geo: Alternate redownload and normal SSF sync attempts](gitlab-org/gitlab@c0f2f40b98c4b9fc72c21c474a2224e045263ea2) ([merge request](gitlab-org/gitlab!73722)) **GitLab Enterprise Edition**
+- [Geo: Alternate redownload and normal project syncs](gitlab-org/gitlab@6370a7258719d5eb0ad83c54383ecb7f4fd54fc2) ([merge request](gitlab-org/gitlab!73722)) **GitLab Enterprise Edition**
+- [Geo: Reduce frequency of redownload attempts](gitlab-org/gitlab@1bcd41f28733b01286a42689857f6530c0805186) ([merge request](gitlab-org/gitlab!73722)) **GitLab Enterprise Edition**
+
## 14.0.11 (2021-09-23)
### Fixed (1 change)
diff --git a/app/assets/javascripts/clusters/agents/components/show.vue b/app/assets/javascripts/clusters/agents/components/show.vue
index 5c672d288c5..afbba9d1f7c 100644
--- a/app/assets/javascripts/clusters/agents/components/show.vue
+++ b/app/assets/javascripts/clusters/agents/components/show.vue
@@ -128,6 +128,7 @@ export default {
</p>
<gl-tabs>
+ <slot name="ee-security-tab"></slot>
<gl-tab>
<template #title>
<span data-testid="cluster-agent-token-count">
diff --git a/app/assets/javascripts/clusters/agents/index.js b/app/assets/javascripts/clusters/agents/index.js
index bcb5b271203..426d8d83847 100644
--- a/app/assets/javascripts/clusters/agents/index.js
+++ b/app/assets/javascripts/clusters/agents/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
-import AgentShowPage from './components/show.vue';
+import AgentShowPage from 'ee_else_ce/clusters/agents/components/show.vue';
Vue.use(VueApollo);
diff --git a/app/assets/javascripts/google_cloud/components/app.vue b/app/assets/javascripts/google_cloud/components/app.vue
new file mode 100644
index 00000000000..1e5be9df019
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/components/app.vue
@@ -0,0 +1,50 @@
+<script>
+import { GlTab, GlTabs } from '@gitlab/ui';
+import IncubationBanner from './incubation_banner.vue';
+import ServiceAccounts from './service_accounts.vue';
+
+export default {
+ components: { GlTab, GlTabs, IncubationBanner, ServiceAccounts },
+ props: {
+ serviceAccounts: {
+ type: Array,
+ required: true,
+ },
+ createServiceAccountUrl: {
+ type: String,
+ required: true,
+ },
+ emptyIllustrationUrl: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ feedbackUrl(template) {
+ return `https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/meta/-/issues/new?issuable_template=${template}`;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <incubation-banner
+ :share-feedback-url="feedbackUrl('general_feedback')"
+ :report-bug-url="feedbackUrl('report_bug')"
+ :feature-request-url="feedbackUrl('feature_request')"
+ />
+ <gl-tabs>
+ <gl-tab :title="__('Configuration')">
+ <service-accounts
+ class="gl-mx-3"
+ :list="serviceAccounts"
+ :create-url="createServiceAccountUrl"
+ :empty-illustration-url="emptyIllustrationUrl"
+ />
+ </gl-tab>
+ <gl-tab :title="__('Deployments')" disabled />
+ <gl-tab :title="__('Services')" disabled />
+ </gl-tabs>
+ </div>
+</template>
diff --git a/app/assets/javascripts/google_cloud/components/incubation_banner.vue b/app/assets/javascripts/google_cloud/components/incubation_banner.vue
new file mode 100644
index 00000000000..652b8c1aecb
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/components/incubation_banner.vue
@@ -0,0 +1,44 @@
+<script>
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
+
+export default {
+ components: { GlAlert, GlLink, GlSprintf },
+ props: {
+ shareFeedbackUrl: {
+ required: true,
+ type: String,
+ },
+ reportBugUrl: {
+ required: true,
+ type: String,
+ },
+ featureRequestUrl: {
+ required: true,
+ type: String,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-alert :dismissible="false" variant="info">
+ {{ __('This is an experimental feature developed by GitLab Incubation Engineering.') }}
+ <gl-sprintf
+ :message="
+ __(
+ 'We invite you to %{featureLinkStart}request a feature%{featureLinkEnd}, %{bugLinkStart}report a bug%{bugLinkEnd} or %{feedbackLinkStart}share feedback%{feedbackLinkEnd}',
+ )
+ "
+ >
+ <template #featureLink="{ content }">
+ <gl-link :href="featureRequestUrl">{{ content }}</gl-link>
+ </template>
+ <template #bugLink="{ content }">
+ <gl-link :href="reportBugUrl">{{ content }}</gl-link>
+ </template>
+ <template #feedbackLink="{ content }">
+ <gl-link :href="shareFeedbackUrl">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-alert>
+</template>
diff --git a/app/assets/javascripts/google_cloud/components/service_accounts.vue b/app/assets/javascripts/google_cloud/components/service_accounts.vue
new file mode 100644
index 00000000000..b70b25a5dc3
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/components/service_accounts.vue
@@ -0,0 +1,65 @@
+<script>
+import { GlButton, GlEmptyState, GlTable } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ components: { GlButton, GlEmptyState, GlTable },
+ props: {
+ list: {
+ type: Array,
+ required: true,
+ },
+ createUrl: {
+ type: String,
+ required: true,
+ },
+ emptyIllustrationUrl: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ tableFields: [
+ { key: 'environment', label: __('Environment'), sortable: true },
+ { key: 'gcp_project', label: __('Google Cloud Project'), sortable: true },
+ { key: 'service_account_exists', label: __('Service Account'), sortable: true },
+ { key: 'service_account_key_exists', label: __('Service Account Key'), sortable: true },
+ ],
+ };
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-empty-state
+ v-if="list.length === 0"
+ :title="__('No service accounts')"
+ :description="
+ __('Service Accounts keys authorize GitLab to deploy your Google Cloud project')
+ "
+ :primary-button-link="createUrl"
+ :primary-button-text="__('Create service account')"
+ :svg-path="emptyIllustrationUrl"
+ />
+
+ <div v-else>
+ <h2 class="gl-font-size-h2">{{ __('Service Accounts') }}</h2>
+ <p>{{ __('Service Accounts keys authorize GitLab to deploy your Google Cloud project') }}</p>
+
+ <gl-table :items="list" :fields="tableFields">
+ <template #cell(service_account_exists)="{ value }">
+ {{ value ? '✔' : __('Not found') }}
+ </template>
+ <template #cell(service_account_key_exists)="{ value }">
+ {{ value ? '✔' : __('Not found') }}
+ </template>
+ </gl-table>
+
+ <gl-button :href="createUrl" category="primary" variant="info">
+ {{ __('Create service account') }}
+ </gl-button>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/google_cloud/index.js b/app/assets/javascripts/google_cloud/index.js
new file mode 100644
index 00000000000..a2838605219
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/index.js
@@ -0,0 +1,19 @@
+import Vue from 'vue';
+import App from './components/app.vue';
+
+const elementRenderer = (element, props = {}) => (createElement) =>
+ createElement(element, { props });
+
+export default () => {
+ const root = document.querySelector('#js-google-cloud');
+
+ // uncomment this once backend is ready
+ // const dataset = JSON.parse(root.getAttribute('data'));
+ const mockDataset = {
+ createServiceAccountUrl: '#create-url',
+ serviceAccounts: [],
+ emptyIllustrationUrl:
+ 'https://gitlab.com/gitlab-org/gitlab-svgs/-/raw/main/illustrations/pipelines_empty.svg',
+ };
+ return new Vue({ el: root, render: elementRenderer(App, mockDataset) });
+};
diff --git a/app/assets/javascripts/pages/projects/google_cloud/index.js b/app/assets/javascripts/pages/projects/google_cloud/index.js
new file mode 100644
index 00000000000..4506ea8efd1
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/google_cloud/index.js
@@ -0,0 +1,3 @@
+import initGoogleCloud from '~/google_cloud/index';
+
+initGoogleCloud();
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 9e93f709937..a26aeeb6db4 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -23,14 +23,14 @@ export default class Project {
});
}
- $('.hide-no-ssh-message').on('click', function (e) {
+ $('.js-hide-no-ssh-message').on('click', function (e) {
Cookies.set('hide_no_ssh_message', 'false');
- $(this).parents('.no-ssh-key-message').remove();
+ $(this).parents('.js-no-ssh-key-message').remove();
return e.preventDefault();
});
- $('.hide-no-password-message').on('click', function (e) {
+ $('.js-hide-no-password-message').on('click', function (e) {
Cookies.set('hide_no_password_message', 'false');
- $(this).parents('.no-password-message').remove();
+ $(this).parents('.js-no-password-message').remove();
return e.preventDefault();
});
$('.hide-auto-devops-implicitly-enabled-banner').on('click', function (e) {
diff --git a/app/assets/javascripts/projects/components/shared/delete_button.vue b/app/assets/javascripts/projects/components/shared/delete_button.vue
index 525ea462847..2e46f437ace 100644
--- a/app/assets/javascripts/projects/components/shared/delete_button.vue
+++ b/app/assets/javascripts/projects/components/shared/delete_button.vue
@@ -1,5 +1,5 @@
<script>
-import { GlModal, GlModalDirective, GlFormTextarea, GlButton } from '@gitlab/ui';
+import { GlModal, GlModalDirective, GlFormInput, GlButton } from '@gitlab/ui';
import { uniqueId } from 'lodash';
import csrf from '~/lib/utils/csrf';
import { __ } from '~/locale';
@@ -7,7 +7,7 @@ import { __ } from '~/locale';
export default {
components: {
GlModal,
- GlFormTextarea,
+ GlFormInput,
GlButton,
},
directives: {
@@ -88,7 +88,12 @@ export default {
<p>
<code class="gl-white-space-pre-wrap">{{ confirmPhrase }}</code>
</p>
- <gl-form-textarea id="confirm_name_input" v-model="userInput" name="confirm_name_input" />
+ <gl-form-input
+ id="confirm_name_input"
+ v-model="userInput"
+ name="confirm_name_input"
+ type="text"
+ />
<slot name="modal-footer"></slot>
</div>
</gl-modal>
diff --git a/app/assets/javascripts/terms/index.js b/app/assets/javascripts/terms/index.js
index dc4f1190eb8..9d60fdfb50a 100644
--- a/app/assets/javascripts/terms/index.js
+++ b/app/assets/javascripts/terms/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
+import TermsApp from 'jh_else_ce/terms/components/app.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import TermsApp from './components/app.vue';
export const initTermsApp = () => {
const el = document.getElementById('js-terms-of-service');
diff --git a/app/controllers/projects/cluster_agents_controller.rb b/app/controllers/projects/cluster_agents_controller.rb
index e7fbe93131d..404d3907128 100644
--- a/app/controllers/projects/cluster_agents_controller.rb
+++ b/app/controllers/projects/cluster_agents_controller.rb
@@ -3,6 +3,10 @@
class Projects::ClusterAgentsController < Projects::ApplicationController
before_action :authorize_can_read_cluster_agent!
+ before_action do
+ push_frontend_feature_flag(:cluster_vulnerabilities, project, default_enabled: :yaml)
+ end
+
feature_category :kubernetes_management
def show
@@ -17,3 +21,5 @@ class Projects::ClusterAgentsController < Projects::ApplicationController
access_denied!
end
end
+
+Projects::ClusterAgentsController.prepend_mod_with('Projects::ClusterAgentsController')
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index bee25e2a569..8366b25d2bc 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -196,12 +196,26 @@ module ProjectsHelper
cookies["hide_auto_devops_implicitly_enabled_banner_#{project.id}".to_sym].blank?
end
- def link_to_set_password
- if current_user.require_password_creation_for_git?
- link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path
- else
- link_to s_('CreateTokenToCloneLink|create a personal access token'), profile_personal_access_tokens_path
- end
+ def no_password_message
+ push_pull_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('gitlab-basics/start-using-git', anchor: 'pull-and-push') }
+ clone_with_https_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('gitlab-basics/start-using-git', anchor: 'clone-with-https') }
+ set_password_link_start = '<a href="%{url}">'.html_safe % { url: edit_profile_password_path }
+ set_up_pat_link_start = '<a href="%{url}">'.html_safe % { url: profile_personal_access_tokens_path }
+
+ message = if current_user.require_password_creation_for_git?
+ _('Your account is authenticated with SSO or SAML. To %{push_pull_link_start}push and pull%{link_end} over %{protocol} with Git using this account, you must %{set_password_link_start}set a password%{link_end} or %{set_up_pat_link_start}set up a Personal Access Token%{link_end} to use instead of a password. For more information, see %{clone_with_https_link_start}Clone with HTTPS%{link_end}.')
+ else
+ _('Your account is authenticated with SSO or SAML. To %{push_pull_link_start}push and pull%{link_end} over %{protocol} with Git using this account, you must %{set_up_pat_link_start}set up a Personal Access Token%{link_end} to use instead of a password. For more information, see %{clone_with_https_link_start}Clone with HTTPS%{link_end}.')
+ end
+
+ html_escape(message) % {
+ push_pull_link_start: push_pull_link_start,
+ protocol: gitlab_config.protocol.upcase,
+ clone_with_https_link_start: clone_with_https_link_start,
+ set_password_link_start: set_password_link_start,
+ set_up_pat_link_start: set_up_pat_link_start,
+ link_end: '</a>'.html_safe
+ }
end
# Returns true if any projects are present.
@@ -384,12 +398,11 @@ module ProjectsHelper
# Returns the confirm phrase the user needs to type in order to delete the project
#
- # Occasionally a user will delete one project, believing it is a different (similar) one.
- # Specifically, a user might delete an original project, believing it is a fork.
- # Thus the phrase should be the project full name to include the namespace.
+ # Thus the phrase should include the namespace to make it very clear to the
+ # user which project is subject to deletion.
# Relevant issue: https://gitlab.com/gitlab-org/gitlab/-/issues/343591
def delete_confirm_phrase(project)
- s_('DeleteProject|Delete %{name}') % { name: project.full_name }
+ project.path_with_namespace
end
private
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 43427e2ebc7..d75f7984e2c 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -48,7 +48,7 @@ class CommitStatus < Ci::ApplicationRecord
scope :ordered, -> { order(:name) }
scope :ordered_by_stage, -> { order(stage_idx: :asc) }
scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) }
- scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
+ scope :retried_ordered, -> { retried.order(name: :asc, id: :desc).includes(project: :namespace) }
scope :ordered_by_pipeline, -> { order(pipeline_id: :asc) }
scope :before_stage, -> (index) { where('stage_idx < ?', index) }
scope :for_stage, -> (index) { where(stage_idx: index) }
diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb
index f776ac4256c..b1cae0d1e49 100644
--- a/app/models/merge_request_diff_commit.rb
+++ b/app/models/merge_request_diff_commit.rb
@@ -7,6 +7,7 @@ class MergeRequestDiffCommit < ApplicationRecord
include ShaAttribute
include CachedCommit
include IgnorableColumns
+ include FromUnion
ignore_column %i[author_name author_email committer_name committer_email],
remove_with: '14.6',
diff --git a/app/models/user.rb b/app/models/user.rb
index 886c30bd28e..2ca7909ebcb 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -473,7 +473,11 @@ class User < ApplicationRecord
end
def active_for_authentication?
- super && can?(:log_in)
+ return false unless super
+
+ check_ldap_if_ldap_blocked!
+
+ can?(:log_in)
end
# The messages for these keys are defined in `devise.en.yml`
@@ -2167,6 +2171,13 @@ class User < ApplicationRecord
def ci_job_token_scope_cache_key
"users:#{id}:ci:job_token_scope"
end
+
+ # An `ldap_blocked` user will be unblocked if LDAP indicates they are allowed.
+ def check_ldap_if_ldap_blocked!
+ return unless ::Gitlab::Auth::Ldap::Config.enabled? && ldap_blocked?
+
+ ::Gitlab::Auth::Ldap::Access.allowed?(self)
+ end
end
User.prepend_mod_with('User')
diff --git a/app/views/shared/_no_password.html.haml b/app/views/shared/_no_password.html.haml
index 9c1e5a49b44..d1e1a8a819d 100644
--- a/app/views/shared/_no_password.html.haml
+++ b/app/views/shared/_no_password.html.haml
@@ -1,12 +1,10 @@
- if show_no_password_message?
- .no-password-message.gl-alert.gl-alert-warning
- = sprite_icon('warning', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label': _('Dismiss') }
- = sprite_icon('close', size: 16, css_class: 'gl-icon')
+ = render 'shared/global_alert',
+ variant: :warning,
+ alert_class: 'js-no-password-message',
+ close_button_class: 'js-hide-no-password-message' do
.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 repositories via %{protocol} until you %{set_password_link} on your account") % translation_params
- = set_password_message.html_safe
+ = no_password_message
.gl-alert-actions
- = link_to _('Remind later'), '#', class: 'hide-no-password-message btn gl-alert-action btn-info btn-md gl-button'
- = link_to _("Don't show again"), profile_path(user: {hide_no_password: true}), method: :put, role: 'button', class: 'btn gl-alert-action btn-md btn-default gl-button btn-default-secondary'
+ = link_to _('Remind later'), '#', class: 'js-hide-no-password-message gl-alert-action btn btn-confirm btn-md gl-button'
+ = link_to _("Don't show again"), profile_path(user: { hide_no_password: true }), method: :put, role: 'button', class: 'gl-alert-action btn btn-default btn-md gl-button'
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index 2c6ceb58654..20dc1b41970 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -1,10 +1,10 @@
- if show_no_ssh_key_message?
- %div{ class: 'no-ssh-key-message gl-alert gl-alert-warning', role: 'alert' }
- = sprite_icon('warning', css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title')
- %button{ class: 'gl-alert-dismiss hide-no-ssh-message', type: 'button', 'aria-label': _('Dismiss') }
- = sprite_icon('close', css_class: 'gl-icon s16')
+ = render 'shared/global_alert',
+ variant: :warning,
+ alert_class: 'js-no-ssh-message',
+ close_button_class: 'js-hide-no-ssh-message' do
.gl-alert-body
- = s_("MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile")
+ = s_("MissingSSHKeyWarningLink|You can't push or pull repositories using 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 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'
+ = link_to s_('MissingSSHKeyWarningLink|Add SSH key'), profile_keys_path, class: "gl-alert-action btn btn-confirm btn-md gl-button"
+ = link_to s_("MissingSSHKeyWarningLink|Don't show again"), profile_path(user: { hide_no_ssh_key: true }), method: :put, role: 'button', class: 'gl-alert-action btn btn-default btn-md gl-button'
diff --git a/config/feature_flags/development/cluster_vulnerabilities.yml b/config/feature_flags/development/cluster_vulnerabilities.yml
new file mode 100644
index 00000000000..919cdc1d009
--- /dev/null
+++ b/config/feature_flags/development/cluster_vulnerabilities.yml
@@ -0,0 +1,8 @@
+---
+name: cluster_vulnerabilities
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73321
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343917
+milestone: '14.5'
+type: development
+group: group::container security
+default_enabled: false
diff --git a/config/feature_flags/development/terms_of_service_vue.yml b/config/feature_flags/development/terms_of_service_vue.yml
index d80af2ab716..01bf3613127 100644
--- a/config/feature_flags/development/terms_of_service_vue.yml
+++ b/config/feature_flags/development/terms_of_service_vue.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343046
milestone: '14.5'
type: development
group: group::access
-default_enabled: false
+default_enabled: true
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index f2067e7a2d1..afbf0759452 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -42,11 +42,15 @@ GitLab to an earlier version, the feature flag status may change.
Features that are disabled by default may change or be removed without notice in a future version of GitLab.
-Data corruption, stability degradation, or performance degradation might occur if
+Data corruption, stability degradation, performance degradation, or security issues might occur if
you enable a feature that's disabled by default. Problems caused by using a default
disabled feature aren't covered by GitLab support, unless you were directed by GitLab
to enable the feature.
+Security issues found in features that are disabled by default are patched in regular releases
+and do not follow our regular [maintenance policy](../policy/maintenance.md#security-releases)
+with regards to backporting the fix.
+
## Risks when disabling released features
In most cases, the feature flag code is removed in a future version of GitLab.
diff --git a/doc/administration/sidekiq.md b/doc/administration/sidekiq.md
index 4aee88ed9cb..8f1119f6868 100644
--- a/doc/administration/sidekiq.md
+++ b/doc/administration/sidekiq.md
@@ -74,6 +74,20 @@ you want using steps 1 and 2 from the GitLab downloads page.
postgresql['trust_auth_cidr_addresses'] = %w(127.0.0.1/32 10.10.1.30/32 10.10.1.31/32 10.10.1.32/32 10.10.1.33/32 10.10.1.38/32)
```
+1. If you run multiple Sidekiq nodes with a shared file storage, such as NFS, you must
+ specify the UIDs and GIDs to ensure they match between servers. Specifying the UIDs
+ and GIDs prevents permissions issues in the file system. This advice is similar to the
+ [advice for Geo setups](geo/replication/multiple_servers.md#step-4-configure-the-frontend-application-nodes-on-the-geo-secondary-site):
+
+ ```ruby
+ user['uid'] = 9000
+ user['gid'] = 9000
+ web_server['uid'] = 9001
+ web_server['gid'] = 9001
+ registry['uid'] = 9002
+ registry['gid'] = 9002
+ ```
+
1. Disable other services:
```ruby
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index d54edc7e6d3..9ca11d43864 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -56,11 +56,18 @@ To add this badge to a project:
## Group badges
-Badges can be added to a group and are visible on every project's
-overview page that's under that group. In this case, they cannot be edited or
-deleted on the project level. If you need to have individual badges for each
-project, consider adding them on the [project level](#project-badges) or use
-[placeholders](#placeholders).
+By adding a badge to a group, you add and enforce a project-level badge
+for all projects in the group. The group badge is visible on the **Overview**
+page of any project that belongs to the group.
+
+NOTE:
+While these badges appear as project-level badges in the codebase, they
+cannot be edited or deleted at the project level.
+
+If you need individual badges for each project, either:
+
+- Add the badge at the [project level](#project-badges).
+- Use [placeholders](#placeholders).
To add a new badge to a group:
diff --git a/doc/user/project/wiki/group.md b/doc/user/project/wiki/group.md
index 6e364a688d3..731fed2595a 100644
--- a/doc/user/project/wiki/group.md
+++ b/doc/user/project/wiki/group.md
@@ -44,7 +44,22 @@ Users with the [Owner role](../../permissions.md) in a group can
[import and export group wikis](../../group/settings/import_export.md) when importing
or exporting a group.
-Content created in a group wiki is not deleted when an account is downgraded or a GitLab trial ends.
+Content created in a group wiki is not deleted when an account is downgraded or a
+GitLab trial ends. The group wiki data is exported whenever the group owner of
+the wiki is exported.
+
+To access the group wiki data from the export file if the feature is no longer
+available, you have to:
+
+1. Extract the [export file tarball](../../group/settings/import_export.md) with
+ this command, replacing `FILENAME` with your file's name:
+ `tar -xvzf FILENAME.tar.gz`
+1. Browse to the `repositories` directory. This directory contains a
+ [Git bundle](https://git-scm.com/docs/git-bundle) with the extension `.wiki.bundle`.
+1. Clone the Git bundle into a new repository, replacing `FILENAME` with
+ your bundle's name: `git clone FILENAME.wiki.bundle`
+
+All files in the wiki are available in this Git repository.
## Related topics
diff --git a/lib/bulk_imports/groups/graphql/get_milestones_query.rb b/lib/bulk_imports/groups/graphql/get_milestones_query.rb
deleted file mode 100644
index 5dd5b31cf0e..00000000000
--- a/lib/bulk_imports/groups/graphql/get_milestones_query.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# frozen_string_literal: true
-
-module BulkImports
- module Groups
- module Graphql
- module GetMilestonesQuery
- extend self
-
- def to_s
- <<-'GRAPHQL'
- query ($full_path: ID!, $cursor: String, $per_page: Int) {
- group(fullPath: $full_path) {
- milestones(first: $per_page, after: $cursor, includeDescendants: false) {
- page_info: pageInfo {
- next_page: endCursor
- has_next_page: hasNextPage
- }
- nodes {
- iid
- title
- description
- state
- start_date: startDate
- due_date: dueDate
- created_at: createdAt
- updated_at: updatedAt
- }
- }
- }
- }
- GRAPHQL
- end
-
- def variables(context)
- {
- full_path: context.entity.source_full_path,
- cursor: context.tracker.next_page,
- per_page: ::BulkImports::Tracker::DEFAULT_PAGE_SIZE
- }
- end
-
- def base_path
- %w[data group milestones]
- end
-
- def data_path
- base_path << 'nodes'
- end
-
- def page_info_path
- base_path << 'page_info'
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/background_migration/fix_merge_request_diff_commit_users.rb b/lib/gitlab/background_migration/fix_merge_request_diff_commit_users.rb
index 1c04b5b025e..ea3e56cb14a 100644
--- a/lib/gitlab/background_migration/fix_merge_request_diff_commit_users.rb
+++ b/lib/gitlab/background_migration/fix_merge_request_diff_commit_users.rb
@@ -10,7 +10,10 @@ module Gitlab
# this process needs Git/Gitaly access, and duplicating all that code is far
# too much, this migration relies on global models such as Project,
# MergeRequest, etc.
+ # rubocop: disable Metrics/ClassLength
class FixMergeRequestDiffCommitUsers
+ BATCH_SIZE = 100
+
def initialize
@commits = {}
@users = {}
@@ -33,24 +36,47 @@ module Gitlab
# Loading everything using one big query may result in timeouts (e.g.
# for projects the size of gitlab-org/gitlab). So instead we query
# data on a per merge request basis.
- project.merge_requests.each_batch do |mrs|
- ::MergeRequestDiffCommit
- .select([
- :merge_request_diff_id,
- :relative_order,
- :sha,
- :committer_id,
- :commit_author_id
- ])
- .joins(merge_request_diff: :merge_request)
- .where(merge_requests: { id: mrs.select(:id) })
- .where('commit_author_id IS NULL OR committer_id IS NULL')
- .each do |commit|
+ project.merge_requests.each_batch(column: :iid) do |mrs|
+ mrs.ids.each do |mr_id|
+ each_row_to_check(mr_id) do |commit|
update_commit(project, commit)
end
+ end
end
end
+ def each_row_to_check(merge_request_id, &block)
+ columns = %w[merge_request_diff_id relative_order].map do |col|
+ Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: col,
+ order_expression: MergeRequestDiffCommit.arel_table[col.to_sym].asc,
+ nullable: :not_nullable,
+ distinct: false
+ )
+ end
+
+ order = Pagination::Keyset::Order.build(columns)
+ scope = MergeRequestDiffCommit
+ .joins(:merge_request_diff)
+ .where(merge_request_diffs: { merge_request_id: merge_request_id })
+ .where('commit_author_id IS NULL OR committer_id IS NULL')
+ .order(order)
+
+ Pagination::Keyset::Iterator
+ .new(scope: scope, use_union_optimization: true)
+ .each_batch(of: BATCH_SIZE) do |rows|
+ rows
+ .select([
+ :merge_request_diff_id,
+ :relative_order,
+ :sha,
+ :committer_id,
+ :commit_author_id
+ ])
+ .each(&block)
+ end
+ end
+
# rubocop: disable Metrics/AbcSize
def update_commit(project, row)
commit = find_commit(project, row.sha)
@@ -125,5 +151,6 @@ module Gitlab
MergeRequestDiffCommit.arel_table
end
end
+ # rubocop: enable Metrics/ClassLength
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b932dc5e249..60e93714371 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2914,6 +2914,9 @@ msgstr ""
msgid "Advanced export options"
msgstr ""
+msgid "AdvancedSearch|Reindex required"
+msgstr ""
+
msgid "After a successful password update you will be redirected to login screen."
msgstr ""
@@ -7400,6 +7403,9 @@ msgstr ""
msgid "ClusterAgents|Registration token"
msgstr ""
+msgid "ClusterAgents|Security"
+msgstr ""
+
msgid "ClusterAgents|Select an Agent"
msgstr ""
@@ -9742,6 +9748,9 @@ msgstr ""
msgid "Create requirement"
msgstr ""
+msgid "Create service account"
+msgstr ""
+
msgid "Create snippet"
msgstr ""
@@ -9775,9 +9784,6 @@ msgstr ""
msgid "CreateTag|Tag"
msgstr ""
-msgid "CreateTokenToCloneLink|create a personal access token"
-msgstr ""
-
msgid "CreateValueStreamForm|%{name} (default)"
msgstr ""
@@ -11107,9 +11113,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -16034,6 +16037,9 @@ msgstr ""
msgid "Google Cloud"
msgstr ""
+msgid "Google Cloud Project"
+msgstr ""
+
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -18647,6 +18653,9 @@ msgstr ""
msgid "Interval Pattern"
msgstr ""
+msgid "Introduced in GitLab 13.1, before using %{reindexing_link_start}zero-downtime reindexing%{link_end} and %{migrations_link_start}Advanced Search migrations%{link_end}, you need to %{recreate_link_start}recreate your index%{link_end}."
+msgstr ""
+
msgid "Introducing Your DevOps Reports"
msgstr ""
@@ -22387,6 +22396,9 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
+msgid "MissingSSHKeyWarningLink|You can't push or pull repositories using SSH until you add an SSH key to your profile."
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
@@ -23366,6 +23378,9 @@ msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No service accounts"
+msgstr ""
+
msgid "No severity matches the provided parameter"
msgstr ""
@@ -23461,6 +23476,9 @@ msgstr ""
msgid "Not confidential"
msgstr ""
+msgid "Not found"
+msgstr ""
+
msgid "Not found."
msgstr ""
@@ -31282,6 +31300,18 @@ msgstr ""
msgid "Service"
msgstr ""
+msgid "Service Account"
+msgstr ""
+
+msgid "Service Account Key"
+msgstr ""
+
+msgid "Service Accounts"
+msgstr ""
+
+msgid "Service Accounts keys authorize GitLab to deploy your Google Cloud project"
+msgstr ""
+
msgid "Service Desk"
msgstr ""
@@ -31336,6 +31366,9 @@ msgstr ""
msgid "ServicePing|Turn on service ping to review instance-level analytics."
msgstr ""
+msgid "Services"
+msgstr ""
+
msgid "Session ID"
msgstr ""
@@ -31474,9 +31507,6 @@ msgstr ""
msgid "Set what should be replicated by this secondary site."
msgstr ""
-msgid "SetPasswordToCloneLink|set a password"
-msgstr ""
-
msgid "SetStatusModal|Add status emoji"
msgstr ""
@@ -35110,6 +35140,9 @@ msgstr ""
msgid "This is a self-managed instance of GitLab."
msgstr ""
+msgid "This is an experimental feature developed by GitLab Incubation Engineering."
+msgstr ""
+
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
@@ -37021,6 +37054,9 @@ msgstr ""
msgid "UsageQuota|Learn more about usage quotas"
msgstr ""
+msgid "UsageQuota|No CI minutes usage data available."
+msgstr ""
+
msgid "UsageQuota|Packages"
msgstr ""
@@ -38356,6 +38392,9 @@ msgstr ""
msgid "We heard back from your device. You have been authenticated."
msgstr ""
+msgid "We invite you to %{featureLinkStart}request a feature%{featureLinkEnd}, %{bugLinkStart}report a bug%{bugLinkEnd} or %{feedbackLinkStart}share feedback%{feedbackLinkEnd}"
+msgstr ""
+
msgid "We recommend cloud-based mobile authenticator apps such as Authy, Duo Mobile, and LastPass. They can restore access if you lose your hardware device."
msgstr ""
@@ -39552,9 +39591,6 @@ 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 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."
msgstr ""
@@ -39705,6 +39741,12 @@ msgstr ""
msgid "Your account has been deactivated. You will not be able to: "
msgstr ""
+msgid "Your account is authenticated with SSO or SAML. To %{push_pull_link_start}push and pull%{link_end} over %{protocol} with Git using this account, you must %{set_password_link_start}set a password%{link_end} or %{set_up_pat_link_start}set up a Personal Access Token%{link_end} to use instead of a password. For more information, see %{clone_with_https_link_start}Clone with HTTPS%{link_end}."
+msgstr ""
+
+msgid "Your account is authenticated with SSO or SAML. To %{push_pull_link_start}push and pull%{link_end} over %{protocol} with Git using this account, you must %{set_up_pat_link_start}set up a Personal Access Token%{link_end} to use instead of a password. For more information, see %{clone_with_https_link_start}Clone with HTTPS%{link_end}."
+msgstr ""
+
msgid "Your account is locked."
msgstr ""
diff --git a/package.json b/package.json
index 9bf46e477c2..88141c019af 100644
--- a/package.json
+++ b/package.json
@@ -55,9 +55,9 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "1.219.0",
+ "@gitlab/svgs": "1.220.0",
"@gitlab/tributejs": "1.0.0",
- "@gitlab/ui": "32.31.0",
+ "@gitlab/ui": "32.33.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "6.1.4-1",
"@rails/ujs": "6.1.4-1",
diff --git a/spec/features/projects/show/no_password_spec.rb b/spec/features/projects/show/no_password_spec.rb
index d18ff75b324..ed06f4e14d3 100644
--- a/spec/features/projects/show/no_password_spec.rb
+++ b/spec/features/projects/show/no_password_spec.rb
@@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.describe 'No Password Alert' do
+ let_it_be(:message_password_auth_enabled) { 'Your account is authenticated with SSO or SAML. To push and pull over HTTP with Git using this account, you must set a password or set up a Personal Access Token to use instead of a password. For more information, see Clone with HTTPS.' }
+ let_it_be(:message_password_auth_disabled) { 'Your account is authenticated with SSO or SAML. To push and pull over HTTP with Git using this account, you must set up a Personal Access Token to use instead of a password. For more information, see Clone with HTTPS.' }
+
let(:project) { create(:project, :repository, namespace: user.namespace) }
context 'with internal auth enabled' do
@@ -15,7 +18,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 repositories via HTTP until you set a password on your account"
+ expect(page).not_to have_content message_password_auth_enabled
end
end
@@ -23,7 +26,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 repositories via HTTP until you set a password on your account"
+ expect(page).to have_content message_password_auth_enabled
end
end
end
@@ -41,7 +44,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 repositories via HTTP until you create a personal access token on your account"
+ expect(page).to have_content message_password_auth_disabled
end
end
@@ -51,7 +54,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 repositories via HTTP until you create a personal access token on your account"
+ expect(page).not_to have_content message_password_auth_disabled
end
end
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index fec2873e4fd..c4619b5498e 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -257,7 +257,7 @@ RSpec.describe 'Project' do
end
it 'deletes a project', :sidekiq_inline do
- expect { remove_with_confirm('Delete project', "Delete #{project.full_name}", 'Yes, delete project') }.to change { Project.count }.by(-1)
+ expect { remove_with_confirm('Delete project', project.path_with_namespace, 'Yes, delete project') }.to change { Project.count }.by(-1)
expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
expect(Project.all.count).to be_zero
expect(project.issues).to be_empty
diff --git a/spec/frontend/clusters/agents/components/show_spec.js b/spec/frontend/clusters/agents/components/show_spec.js
index fd04ff8b3e7..c502e7d813e 100644
--- a/spec/frontend/clusters/agents/components/show_spec.js
+++ b/spec/frontend/clusters/agents/components/show_spec.js
@@ -1,6 +1,8 @@
import { GlAlert, GlKeysetPagination, GlLoadingIcon, GlSprintf, GlTab } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ClusterAgentShow from '~/clusters/agents/components/show.vue';
import TokenTable from '~/clusters/agents/components/token_table.vue';
import getAgentQuery from '~/clusters/agents/graphql/queries/get_cluster_agent.query.graphql';
@@ -40,28 +42,34 @@ describe('ClusterAgentShow', () => {
queryResponse || jest.fn().mockResolvedValue({ data: { project: { clusterAgent } } });
const apolloProvider = createMockApollo([[getAgentQuery, agentQueryResponse]]);
- wrapper = shallowMount(ClusterAgentShow, {
- localVue,
- apolloProvider,
- propsData,
- stubs: { GlSprintf, TimeAgoTooltip, GlTab },
- });
+ wrapper = extendedWrapper(
+ shallowMount(ClusterAgentShow, {
+ localVue,
+ apolloProvider,
+ propsData,
+ stubs: { GlSprintf, TimeAgoTooltip, GlTab },
+ }),
+ );
};
- const createWrapperWithoutApollo = ({ clusterAgent, loading = false }) => {
+ const createWrapperWithoutApollo = ({ clusterAgent, loading = false, slots = {} }) => {
const $apollo = { queries: { clusterAgent: { loading } } };
- wrapper = shallowMount(ClusterAgentShow, {
- propsData,
- mocks: { $apollo, clusterAgent },
- stubs: { GlTab },
- });
+ wrapper = extendedWrapper(
+ shallowMount(ClusterAgentShow, {
+ propsData,
+ mocks: { $apollo, clusterAgent },
+ slots,
+ stubs: { GlTab },
+ }),
+ );
};
- const findCreatedText = () => wrapper.find('[data-testid="cluster-agent-create-info"]').text();
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
- const findPaginationButtons = () => wrapper.find(GlKeysetPagination);
- const findTokenCount = () => wrapper.find('[data-testid="cluster-agent-token-count"]').text();
+ const findCreatedText = () => wrapper.findByTestId('cluster-agent-create-info').text();
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findPaginationButtons = () => wrapper.findComponent(GlKeysetPagination);
+ const findTokenCount = () => wrapper.findByTestId('cluster-agent-token-count').text();
+ const findEESecurityTabSlot = () => wrapper.findByTestId('ee-security-tab');
afterEach(() => {
wrapper.destroy();
@@ -87,7 +95,7 @@ describe('ClusterAgentShow', () => {
});
it('renders token table', () => {
- expect(wrapper.find(TokenTable).exists()).toBe(true);
+ expect(wrapper.findComponent(TokenTable).exists()).toBe(true);
});
it('should not render pagination buttons when there are no additional pages', () => {
@@ -188,8 +196,27 @@ describe('ClusterAgentShow', () => {
});
it('displays an alert message', () => {
- expect(wrapper.find(GlAlert).exists()).toBe(true);
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(true);
expect(wrapper.text()).toContain(ClusterAgentShow.i18n.loadingError);
});
});
+
+ describe('ee-security-tab slot', () => {
+ it('does not display when a slot is not passed in', async () => {
+ createWrapperWithoutApollo({ clusterAgent: defaultClusterAgent });
+ await nextTick();
+ expect(findEESecurityTabSlot().exists()).toBe(false);
+ });
+
+ it('does display when a slot is passed in', async () => {
+ createWrapperWithoutApollo({
+ clusterAgent: defaultClusterAgent,
+ slots: {
+ 'ee-security-tab': `<gl-tab data-testid="ee-security-tab">Security Tab!</gl-tab>`,
+ },
+ });
+ await nextTick();
+ expect(findEESecurityTabSlot().exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/google_cloud/components/app_spec.js b/spec/frontend/google_cloud/components/app_spec.js
new file mode 100644
index 00000000000..bb86eb5c22e
--- /dev/null
+++ b/spec/frontend/google_cloud/components/app_spec.js
@@ -0,0 +1,66 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlTab, GlTabs } from '@gitlab/ui';
+import App from '~/google_cloud/components/app.vue';
+import IncubationBanner from '~/google_cloud/components/incubation_banner.vue';
+import ServiceAccounts from '~/google_cloud/components/service_accounts.vue';
+
+describe('google_cloud App component', () => {
+ let wrapper;
+
+ const findIncubationBanner = () => wrapper.findComponent(IncubationBanner);
+ const findTabs = () => wrapper.findComponent(GlTabs);
+ const findTabItems = () => findTabs().findAllComponents(GlTab);
+ const findConfigurationTab = () => findTabItems().at(0);
+ const findDeploymentTab = () => findTabItems().at(1);
+ const findServicesTab = () => findTabItems().at(2);
+ const findServiceAccounts = () => findConfigurationTab().findComponent(ServiceAccounts);
+
+ beforeEach(() => {
+ const propsData = {
+ serviceAccounts: [{}, {}],
+ createServiceAccountUrl: '#url-create-service-account',
+ emptyIllustrationUrl: '#url-empty-illustration',
+ };
+ wrapper = shallowMount(App, { propsData });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('should contain incubation banner', () => {
+ expect(findIncubationBanner().exists()).toBe(true);
+ });
+
+ describe('google_cloud App tabs', () => {
+ it('should contain tabs', () => {
+ expect(findTabs().exists()).toBe(true);
+ });
+
+ it('should contain three tab items', () => {
+ expect(findTabItems().length).toBe(3);
+ });
+
+ describe('configuration tab', () => {
+ it('should exist', () => {
+ expect(findConfigurationTab().exists()).toBe(true);
+ });
+
+ it('should contain service accounts component', () => {
+ expect(findServiceAccounts().exists()).toBe(true);
+ });
+ });
+
+ describe('deployments tab', () => {
+ it('should exist', () => {
+ expect(findDeploymentTab().exists()).toBe(true);
+ });
+ });
+
+ describe('services tab', () => {
+ it('should exist', () => {
+ expect(findServicesTab().exists()).toBe(true);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/google_cloud/components/incubation_banner_spec.js b/spec/frontend/google_cloud/components/incubation_banner_spec.js
new file mode 100644
index 00000000000..89517be4ef1
--- /dev/null
+++ b/spec/frontend/google_cloud/components/incubation_banner_spec.js
@@ -0,0 +1,60 @@
+import { mount } from '@vue/test-utils';
+import { GlAlert, GlLink } from '@gitlab/ui';
+import IncubationBanner from '~/google_cloud/components/incubation_banner.vue';
+
+describe('IncubationBanner component', () => {
+ let wrapper;
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findLinks = () => wrapper.findAllComponents(GlLink);
+ const findFeatureRequestLink = () => findLinks().at(0);
+ const findReportBugLink = () => findLinks().at(1);
+ const findShareFeedbackLink = () => findLinks().at(2);
+
+ beforeEach(() => {
+ const propsData = {
+ shareFeedbackUrl: 'url_general_feedback',
+ reportBugUrl: 'url_report_bug',
+ featureRequestUrl: 'url_feature_request',
+ };
+ wrapper = mount(IncubationBanner, { propsData });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('contains alert', () => {
+ expect(findAlert().exists()).toBe(true);
+ });
+
+ it('contains relevant text', () => {
+ expect(findAlert().text()).toContain(
+ 'This is an experimental feature developed by GitLab Incubation Engineering.',
+ );
+ });
+
+ describe('has relevant gl-links', () => {
+ it('three in total', () => {
+ expect(findLinks().length).toBe(3);
+ });
+
+ it('contains feature request link', () => {
+ const link = findFeatureRequestLink();
+ expect(link.text()).toBe('request a feature');
+ expect(link.attributes('href')).toBe('url_feature_request');
+ });
+
+ it('contains report bug link', () => {
+ const link = findReportBugLink();
+ expect(link.text()).toBe('report a bug');
+ expect(link.attributes('href')).toBe('url_report_bug');
+ });
+
+ it('contains share feedback link', () => {
+ const link = findShareFeedbackLink();
+ expect(link.text()).toBe('share feedback');
+ expect(link.attributes('href')).toBe('url_general_feedback');
+ });
+ });
+});
diff --git a/spec/frontend/google_cloud/components/service_accounts_spec.js b/spec/frontend/google_cloud/components/service_accounts_spec.js
new file mode 100644
index 00000000000..3d097078f03
--- /dev/null
+++ b/spec/frontend/google_cloud/components/service_accounts_spec.js
@@ -0,0 +1,79 @@
+import { mount } from '@vue/test-utils';
+import { GlButton, GlEmptyState, GlTable } from '@gitlab/ui';
+import ServiceAccounts from '~/google_cloud/components/service_accounts.vue';
+
+describe('ServiceAccounts component', () => {
+ describe('when the project does not have any service accounts', () => {
+ let wrapper;
+
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findButtonInEmptyState = () => findEmptyState().findComponent(GlButton);
+
+ beforeEach(() => {
+ const propsData = {
+ list: [],
+ createUrl: '#create-url',
+ emptyIllustrationUrl: '#empty-illustration-url',
+ };
+ wrapper = mount(ServiceAccounts, { propsData });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows the empty state component', () => {
+ expect(findEmptyState().exists()).toBe(true);
+ });
+ it('shows the link to create new service accounts', () => {
+ const button = findButtonInEmptyState();
+ expect(button.exists()).toBe(true);
+ expect(button.text()).toBe('Create service account');
+ expect(button.attributes('href')).toBe('#create-url');
+ });
+ });
+
+ describe('when three service accounts are passed via props', () => {
+ let wrapper;
+
+ const findTitle = () => wrapper.find('h2');
+ const findDescription = () => wrapper.find('p');
+ const findTable = () => wrapper.findComponent(GlTable);
+ const findRows = () => findTable().findAll('tr');
+ const findButton = () => wrapper.findComponent(GlButton);
+
+ beforeEach(() => {
+ const propsData = {
+ list: [{}, {}, {}],
+ createUrl: '#create-url',
+ emptyIllustrationUrl: '#empty-illustration-url',
+ };
+ wrapper = mount(ServiceAccounts, { propsData });
+ });
+
+ it('shows the title', () => {
+ expect(findTitle().text()).toBe('Service Accounts');
+ });
+
+ it('shows the description', () => {
+ expect(findDescription().text()).toBe(
+ 'Service Accounts keys authorize GitLab to deploy your Google Cloud project',
+ );
+ });
+
+ it('shows the table', () => {
+ expect(findTable().exists()).toBe(true);
+ });
+
+ it('table must have three rows + header row', () => {
+ expect(findRows().length).toBe(4);
+ });
+
+ it('shows the link to create new service accounts', () => {
+ const button = findButton();
+ expect(button.exists()).toBe(true);
+ expect(button.text()).toBe('Create service account');
+ expect(button.attributes('href')).toBe('#create-url');
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap
index 8f69f943112..c95538546c1 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap
@@ -27,6 +27,7 @@ exports[`VersionRow renders 1`] = `
>
<span
class="gl-truncate"
+ data-testid="truncate-end-container"
title="@gitlab-org/package-15"
>
<span
diff --git a/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap b/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap
index 492a40458e2..c255fcce321 100644
--- a/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap
+++ b/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap
@@ -71,10 +71,10 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
</code>
</p>
- <gl-form-textarea-stub
+ <gl-form-input-stub
id="confirm_name_input"
name="confirm_name_input"
- noresize="true"
+ type="text"
/>
</div>
diff --git a/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap b/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap
index a34507e9199..dd54db7dc0a 100644
--- a/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap
+++ b/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap
@@ -51,10 +51,10 @@ exports[`Project remove modal intialized matches the snapshot 1`] = `
</code>
</p>
- <gl-form-textarea-stub
+ <gl-form-input-stub
id="confirm_name_input"
name="confirm_name_input"
- noresize="true"
+ type="text"
/>
</div>
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 661d9ac2821..5d2af567549 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -268,7 +268,7 @@ RSpec.describe ProjectsHelper do
end
end
- describe '#link_to_set_password' do
+ describe '#no_password_message' do
let(:user) { create(:user, password_automatically_set: true) }
before do
@@ -276,18 +276,18 @@ RSpec.describe ProjectsHelper do
end
context 'password authentication is enabled for Git' do
- it 'returns link to set a password' do
+ it 'returns message prompting user to set password or set up a PAT' do
stub_application_setting(password_authentication_enabled_for_git?: true)
- expect(helper.link_to_set_password).to match %r{<a href="#{edit_profile_password_path}">set a password</a>}
+ expect(helper.no_password_message).to eq('Your account is authenticated with SSO or SAML. To <a href="/help/gitlab-basics/start-using-git#pull-and-push" target="_blank" rel="noopener noreferrer">push and pull</a> over HTTP with Git using this account, you must <a href="/-/profile/password/edit">set a password</a> or <a href="/-/profile/personal_access_tokens">set up a Personal Access Token</a> to use instead of a password. For more information, see <a href="/help/gitlab-basics/start-using-git#clone-with-https" target="_blank" rel="noopener noreferrer">Clone with HTTPS</a>.')
end
end
context 'password authentication is disabled for Git' do
- it 'returns link to create a personal access token' do
+ it 'returns message prompting user to set up a PAT' do
stub_application_setting(password_authentication_enabled_for_git?: false)
- expect(helper.link_to_set_password).to match %r{<a href="#{profile_personal_access_tokens_path}">create a personal access token</a>}
+ expect(helper.no_password_message).to eq('Your account is authenticated with SSO or SAML. To <a href="/help/gitlab-basics/start-using-git#pull-and-push" target="_blank" rel="noopener noreferrer">push and pull</a> over HTTP with Git using this account, you must <a href="/-/profile/personal_access_tokens">set up a Personal Access Token</a> to use instead of a password. For more information, see <a href="/help/gitlab-basics/start-using-git#clone-with-https" target="_blank" rel="noopener noreferrer">Clone with HTTPS</a>.')
end
end
end
@@ -987,8 +987,8 @@ RSpec.describe ProjectsHelper do
describe "#delete_confirm_phrase" do
subject { helper.delete_confirm_phrase(project) }
- it 'includes the project full name' do
- expect(subject).to eq("Delete #{project.full_name}")
+ it 'includes the project path with namespace' do
+ expect(subject).to eq(project.path_with_namespace)
end
end
end
diff --git a/spec/lib/bulk_imports/groups/graphql/get_milestones_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_milestones_query_spec.rb
deleted file mode 100644
index 7a0f964c5f3..00000000000
--- a/spec/lib/bulk_imports/groups/graphql/get_milestones_query_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Groups::Graphql::GetMilestonesQuery do
- it 'has a valid query' do
- tracker = create(:bulk_import_tracker)
- context = BulkImports::Pipeline::Context.new(tracker)
-
- query = GraphQL::Query.new(
- GitlabSchema,
- described_class.to_s,
- variables: described_class.variables(context)
- )
- result = GitlabSchema.static_validator.validate(query)
-
- expect(result[:errors]).to be_empty
- end
-
- describe '#data_path' do
- it 'returns data path' do
- expected = %w[data group milestones nodes]
-
- expect(described_class.data_path).to eq(expected)
- end
- end
-
- describe '#page_info_path' do
- it 'returns pagination information path' do
- expected = %w[data group milestones page_info]
-
- expect(described_class.page_info_path).to eq(expected)
- end
- end
-end
diff --git a/spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb b/spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb
index ffed0b517f9..c343ee438b8 100644
--- a/spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb
+++ b/spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb
@@ -49,6 +49,36 @@ RSpec.describe Gitlab::BackgroundMigration::FixMergeRequestDiffCommitUsers do
end
end
+ describe '#process' do
+ it 'processes the merge requests of the project' do
+ project = create(:project, :repository)
+ commit = project.commit
+ mr = create(
+ :merge_request_with_diffs,
+ source_project: project,
+ target_project: project
+ )
+
+ diff = mr.merge_request_diffs.first
+
+ create(
+ :merge_request_diff_commit,
+ merge_request_diff: diff,
+ sha: commit.sha,
+ relative_order: 9000
+ )
+
+ migration.process(project)
+
+ updated = diff
+ .merge_request_diff_commits
+ .find_by(sha: commit.sha, relative_order: 9000)
+
+ expect(updated.commit_author_id).not_to be_nil
+ expect(updated.committer_id).not_to be_nil
+ end
+ end
+
describe '#update_commit' do
let(:project) { create(:project, :repository) }
let(:mr) do
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 20afddd8470..59d14574c02 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -379,6 +379,22 @@ RSpec.describe CommitStatus do
end
end
+ describe '.retried_ordered' do
+ subject { described_class.retried_ordered.to_a }
+
+ let!(:statuses) do
+ [create_status(name: 'aa', ref: 'bb', status: 'running', retried: true),
+ create_status(name: 'cc', ref: 'cc', status: 'pending', retried: true),
+ create_status(name: 'aa', ref: 'cc', status: 'success', retried: true),
+ create_status(name: 'cc', ref: 'bb', status: 'success'),
+ create_status(name: 'aa', ref: 'bb', status: 'success')]
+ end
+
+ it 'returns retried statuses in order' do
+ is_expected.to eq(statuses.values_at(2, 0, 1))
+ end
+ end
+
describe '.running_or_pending' do
subject { described_class.running_or_pending.order(:id) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index bd357858dcf..2921d94c343 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe User do
include ProjectForksHelper
include TermsHelper
include ExclusiveLeaseHelpers
+ include LdapHelpers
it_behaves_like 'having unique enum values'
@@ -5808,7 +5809,7 @@ RSpec.describe User do
end
describe '#active_for_authentication?' do
- subject { user.active_for_authentication? }
+ subject(:active_for_authentication?) { user.active_for_authentication? }
let(:user) { create(:user) }
@@ -5818,6 +5819,14 @@ RSpec.describe User do
end
it { is_expected.to be false }
+
+ it 'does not check if LDAP is allowed' do
+ stub_ldap_setting(enabled: true)
+
+ expect(Gitlab::Auth::Ldap::Access).not_to receive(:allowed?)
+
+ active_for_authentication?
+ end
end
context 'when user is a ghost user' do
@@ -5828,6 +5837,28 @@ RSpec.describe User do
it { is_expected.to be false }
end
+ context 'when user is ldap_blocked' do
+ before do
+ user.ldap_block
+ end
+
+ it 'rechecks if LDAP is allowed when LDAP is enabled' do
+ stub_ldap_setting(enabled: true)
+
+ expect(Gitlab::Auth::Ldap::Access).to receive(:allowed?)
+
+ active_for_authentication?
+ end
+
+ it 'does not check if LDAP is allowed when LDAP is not enabled' do
+ stub_ldap_setting(enabled: false)
+
+ expect(Gitlab::Auth::Ldap::Access).not_to receive(:allowed?)
+
+ active_for_authentication?
+ end
+ end
+
context 'based on user type' do
using RSpec::Parameterized::TableSyntax
diff --git a/yarn.lock b/yarn.lock
index 54b1473a65b..82be75259dc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -904,20 +904,20 @@
stylelint-declaration-strict-value "1.7.7"
stylelint-scss "3.18.0"
-"@gitlab/svgs@1.219.0":
- version "1.219.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.219.0.tgz#82735876b98bd3a46f42b4a424b45086ed48e7ac"
- integrity sha512-UOy0+6A6PTbjNHLFBc70ATYztsiQfWHPORgPGnzugYJz2F7ewMr4p6R8d9avFqMNtVB5mIHSnbrsr0pp0XVMGA==
+"@gitlab/svgs@1.220.0":
+ version "1.220.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.220.0.tgz#188bdefe86cdbf8be1faa7a92dbac31c728066c7"
+ integrity sha512-9QRXQG6IrQoviU86g2Y4l19yE81UyEg/iMoGetMfUdQ64NW6unLN7uNbUaO1ws1J0p7uG0dKwR6ohD7tEUPLFA==
"@gitlab/tributejs@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
-"@gitlab/ui@32.31.0":
- version "32.31.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.31.0.tgz#e379f79f0797c98d67e121739add8dec8281a5d4"
- integrity sha512-a/03Jgh3TJx0W1lJjsYZiAKbRQHGvomrGhzDvBpxKve2FXrYdo4G6gbwlIKJGiooB5YmZ5OIWhgnhQ8FSy15Aw==
+"@gitlab/ui@32.33.0":
+ version "32.33.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.33.0.tgz#387c0c0fc515a44b8d115a1da1903e84233fbaaf"
+ integrity sha512-wmyfRMEQ4ZQLCR4FS7fkCY1FCNX6amPyZYYzCZTV52NMtKlgaxczB7YkY1ufdtg5ctmI2NcQNkRGbdW3Et0Riw==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "2.20.1"