summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_todo/rails/save_bang.yml10
-rw-r--r--.rubocop_todo/style/open_struct_use.yml2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock22
-rw-r--r--app/assets/javascripts/jobs/bridge/components/sidebar.vue7
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js7
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js52
-rw-r--r--app/assets/javascripts/sidebar/stores/sidebar_store.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue2
-rw-r--r--app/controllers/import/gitlab_controller.rb2
-rw-r--r--app/controllers/projects/jobs_controller.rb1
-rw-r--r--app/helpers/ssh_keys_helper.rb10
-rw-r--r--app/models/ci/pipeline.rb6
-rw-r--r--app/models/concerns/ci/contextable.rb36
-rw-r--r--app/models/group.rb3
-rw-r--r--app/models/namespaces/traversal/linear_scopes.rb14
-rw-r--r--app/models/namespaces/traversal/recursive_scopes.rb4
-rw-r--r--app/models/project.rb3
-rw-r--r--app/models/user.rb2
-rw-r--r--app/serializers/merge_request_sidebar_basic_entity.rb4
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/services/merge_requests/handle_assignees_change_service.rb4
-rw-r--r--app/services/merge_requests/remove_approval_service.rb1
-rw-r--r--app/services/resource_access_tokens/revoke_service.rb10
-rw-r--r--app/views/groups/settings/_ip_restriction_registration_features_cta.html.haml8
-rw-r--r--app/views/groups/settings/_permissions.html.haml1
-rw-r--r--app/views/profiles/gpg_keys/index.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml4
-rw-r--r--app/views/profiles/keys/index.html.haml7
-rw-r--r--app/views/shared/access_tokens/_table.html.haml2
-rw-r--r--config/feature_flags/development/ci_skip_require_credit_card_for_addon_ci_minutes.yml8
-rw-r--r--config/feature_flags/development/ci_use_variables_builder_definitions.yml8
-rw-r--r--config/feature_flags/development/trigger_job_retry_action.yml8
-rw-r--r--doc/api/graphql/reference/index.md23
-rw-r--r--lib/api/entities/project_with_access.rb2
-rw-r--r--lib/api/entities/resource_access_token.rb2
-rw-r--r--lib/gitlab/ci/variables/builder.rb64
-rw-r--r--lib/gitlab/ssh_public_key.rb22
-rw-r--r--lib/tasks/gitlab/db.rake4
-rw-r--r--locale/gitlab.pot30
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb8
-rw-r--r--spec/frontend/jobs/bridge/components/sidebar_spec.js32
-rw-r--r--spec/helpers/ssh_keys_helper_spec.rb25
-rw-r--r--spec/initializers/doorkeeper_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb196
-rw-r--r--spec/lib/gitlab/database/migrations/runner_spec.rb2
-rw-r--r--spec/lib/gitlab/ssh_public_key_spec.rb33
-rw-r--r--spec/models/ci/build_spec.rb61
-rw-r--r--spec/requests/api/maven_packages_spec.rb2
-rw-r--r--spec/requests/projects/merge_requests_discussions_spec.rb2
-rw-r--r--spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/namespaces/traversal_scope_examples.rb6
-rw-r--r--spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb27
-rw-r--r--spec/support/system_exit_detected.rb15
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb4
-rw-r--r--spec/views/groups/edit.html.haml_spec.rb36
-rw-r--r--spec/views/profiles/keys/_form.html.haml_spec.rb6
60 files changed, 721 insertions, 159 deletions
diff --git a/.rubocop_todo/rails/save_bang.yml b/.rubocop_todo/rails/save_bang.yml
index e3d97a45afd..8f2be251dc5 100644
--- a/.rubocop_todo/rails/save_bang.yml
+++ b/.rubocop_todo/rails/save_bang.yml
@@ -18,16 +18,6 @@ Rails/SaveBang:
- ee/spec/models/visible_approvable_spec.rb
- ee/spec/models/vulnerabilities/feedback_spec.rb
- ee/spec/models/vulnerabilities/issue_link_spec.rb
- - ee/spec/services/ee/merge_requests/update_service_spec.rb
- - ee/spec/services/ee/notes/quick_actions_service_spec.rb
- - ee/spec/services/ee/notification_service_spec.rb
- - ee/spec/services/epic_links/create_service_spec.rb
- - ee/spec/services/epics/close_service_spec.rb
- - ee/spec/services/epics/issue_promote_service_spec.rb
- - ee/spec/services/epics/reopen_service_spec.rb
- - ee/spec/services/epics/tree_reorder_service_spec.rb
- - ee/spec/services/epics/update_dates_service_spec.rb
- - ee/spec/services/epics/update_service_spec.rb
- ee/spec/services/geo/blob_verification_secondary_service_spec.rb
- ee/spec/services/geo/files_expire_service_spec.rb
- ee/spec/services/geo/metrics_update_service_spec.rb
diff --git a/.rubocop_todo/style/open_struct_use.yml b/.rubocop_todo/style/open_struct_use.yml
index d50278da557..c04493fe3bb 100644
--- a/.rubocop_todo/style/open_struct_use.yml
+++ b/.rubocop_todo/style/open_struct_use.yml
@@ -25,9 +25,7 @@ Style/OpenStructUse:
- spec/graphql/mutations/commits/create_spec.rb
- spec/helpers/application_settings_helper_spec.rb
- spec/helpers/profiles_helper_spec.rb
- - spec/initializers/doorkeeper_spec.rb
- spec/lib/gitlab/auth/o_auth/provider_spec.rb
- - spec/lib/gitlab/database/migrations/runner_spec.rb
- spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
- spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
- spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 62d572c550a..35310f489d4 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-e02b0d67e48ed5a4493b073c9836d376a780f34d
+7055518ce76486791c3450a8a47b673891b6e2d6
diff --git a/Gemfile b/Gemfile
index 786fdadd157..c80163de909 100644
--- a/Gemfile
+++ b/Gemfile
@@ -313,7 +313,7 @@ gem 'pg_query', '~> 2.1'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
-gem 'gitlab-labkit', '~> 0.21.1'
+gem 'gitlab-labkit', '~> 0.21.3'
# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
gem 'thrift', '>= 0.14.0'
@@ -485,14 +485,14 @@ end
gem 'spamcheck', '~> 0.1.0'
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 14.4.0.pre.rc43'
+gem 'gitaly', '~> 14.6.0.pre.rc1'
# KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2'
-gem 'grpc', '~> 1.30.2'
+gem 'grpc', '~> 1.42.0'
-gem 'google-protobuf', '~> 3.17.1'
+gem 'google-protobuf', '~> 3.19.0'
gem 'toml-rb', '~> 2.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 238228f13fc..2a54b7aaaec 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -452,7 +452,7 @@ GEM
rails (>= 3.2.0)
git (1.7.0)
rchardet (~> 1.8)
- gitaly (14.4.0.pre.rc43)
+ gitaly (14.6.0.pre.rc1)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab (4.16.1)
@@ -474,10 +474,10 @@ GEM
fog-json (~> 1.2.0)
mime-types
ms_rest_azure (~> 0.12.0)
- gitlab-labkit (0.21.1)
+ gitlab-labkit (0.21.3)
actionpack (>= 5.0.0, < 7.0.0)
activesupport (>= 5.0.0, < 7.0.0)
- grpc (~> 1.30.2)
+ grpc (>= 1.37)
jaeger-client (~> 1.1)
opentracing (~> 0.4)
pg_query (~> 2.1)
@@ -531,8 +531,8 @@ GEM
signet (~> 0.12)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
- google-protobuf (3.17.3)
- googleapis-common-protos-types (1.1.0)
+ google-protobuf (3.19.1)
+ googleapis-common-protos-types (1.3.0)
google-protobuf (~> 3.14)
googleauth (0.14.0)
faraday (>= 0.17.3, < 2.0)
@@ -580,8 +580,8 @@ GEM
graphql (~> 1.6)
html-pipeline (~> 2.8)
sass (~> 3.4)
- grpc (1.30.2)
- google-protobuf (~> 3.12)
+ grpc (1.42.0)
+ google-protobuf (~> 3.18)
googleapis-common-protos-types (~> 1.0)
gssapi (1.2.0)
ffi (>= 1.0.1)
@@ -1473,13 +1473,13 @@ DEPENDENCIES
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 14.4.0.pre.rc43)
+ gitaly (~> 14.6.0.pre.rc1)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 2.6.1)
gitlab-experiment (~> 0.6.5)
gitlab-fog-azure-rm (~> 1.2.0)
- gitlab-labkit (~> 0.21.1)
+ gitlab-labkit (~> 0.21.3)
gitlab-license (~> 2.0)
gitlab-license_finder (~> 6.0)
gitlab-mail_room (~> 0.0.9)
@@ -1492,7 +1492,7 @@ DEPENDENCIES
gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.4.0)
google-api-client (~> 0.33)
- google-protobuf (~> 3.17.1)
+ google-protobuf (~> 3.19.0)
gpgme (~> 2.0.19)
grape (~> 1.5.2)
grape-entity (~> 0.10.0)
@@ -1502,7 +1502,7 @@ DEPENDENCIES
graphlient (~> 0.4.0)
graphql (~> 1.11.10)
graphql-docs (~> 1.6.0)
- grpc (~> 1.30.2)
+ grpc (~> 1.42.0)
gssapi
guard-rspec
haml_lint (~> 0.36.0)
diff --git a/app/assets/javascripts/jobs/bridge/components/sidebar.vue b/app/assets/javascripts/jobs/bridge/components/sidebar.vue
index 9c7ae1178ea..34cefb0d0e2 100644
--- a/app/assets/javascripts/jobs/bridge/components/sidebar.vue
+++ b/app/assets/javascripts/jobs/bridge/components/sidebar.vue
@@ -1,6 +1,7 @@
<script>
import { GlButton, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { __ } from '~/locale';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import { JOB_SIDEBAR } from '../../constants';
import CommitBlock from '../../components/commit_block.vue';
@@ -25,6 +26,7 @@ export default {
GlDropdownItem,
TooltipOnTruncate,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
bridgeJob: {
type: Object,
@@ -54,7 +56,10 @@ export default {
</h4>
</tooltip-on-truncate>
<!-- TODO: implement retry actions -->
- <div class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right">
+ <div
+ v-if="glFeatures.triggerJobRetryAction"
+ class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right"
+ >
<gl-dropdown
:text="$options.i18n.retryButton"
category="primary"
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index cbe40d0bfbe..6363422259e 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -26,6 +26,7 @@ import trackShowInviteMemberLink from '~/sidebar/track_invite_members';
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
import LabelsSelectWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
+import eventHub from '~/sidebar/event_hub';
import Translate from '../vue_shared/translate';
import SidebarAssignees from './components/assignees/sidebar_assignees.vue';
import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue';
@@ -600,6 +601,12 @@ export function mountSidebar(mediator, store) {
mountTimeTrackingComponent();
mountSeverityComponent();
+
+ if (window.gon?.features?.mrAttentionRequests) {
+ eventHub.$on('removeCurrentUserAttentionRequested', () =>
+ mediator.removeCurrentUserAttentionRequested(),
+ );
+ }
}
export { getSidebarOptions };
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index a49ddac8c89..25468d4a697 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -30,7 +30,7 @@ export default class SidebarMediator {
this.store.addAssignee(this.store.currentUser);
}
- saveAssignees(field) {
+ async saveAssignees(field) {
const selected = this.store.assignees.map((u) => u.id);
// If there are no ids, that means we have to unassign (which is id = 0)
@@ -38,10 +38,22 @@ export default class SidebarMediator {
const assignees = selected.length === 0 ? [0] : selected;
const data = { assignee_ids: assignees };
- return this.service.update(field, data);
+ try {
+ const res = await this.service.update(field, data);
+
+ this.store.overwrite('assignees', res.data.assignees);
+
+ if (res.data.reviewers) {
+ this.store.overwrite('reviewers', res.data.reviewers);
+ }
+
+ return Promise.resolve(res);
+ } catch (e) {
+ return Promise.reject(e);
+ }
}
- saveReviewers(field) {
+ async saveReviewers(field) {
const selected = this.store.reviewers.map((u) => u.id);
// If there are no ids, that means we have to unassign (which is id = 0)
@@ -49,7 +61,16 @@ export default class SidebarMediator {
const reviewers = selected.length === 0 ? [0] : selected;
const data = { reviewer_ids: reviewers };
- return this.service.update(field, data);
+ try {
+ const res = await this.service.update(field, data);
+
+ this.store.overwrite('reviewers', res.data.reviewers);
+ this.store.overwrite('assignees', res.data.assignees);
+
+ return Promise.resolve(res);
+ } catch (e) {
+ return Promise.reject();
+ }
}
requestReview({ userId, callback }) {
@@ -63,6 +84,19 @@ export default class SidebarMediator {
.catch(() => callback(userId, false));
}
+ removeCurrentUserAttentionRequested() {
+ const currentUserId = gon.current_user_id;
+
+ const currentUserReviewer = this.store.findReviewer({ id: currentUserId });
+ const currentUserAssignee = this.store.findAssignee({ id: currentUserId });
+
+ if (currentUserReviewer?.attention_requested || currentUserAssignee?.attention_requested) {
+ // Update current users attention_requested state
+ this.store.updateReviewer(currentUserId, 'attention_requested');
+ this.store.updateAssignee(currentUserId, 'attention_requested');
+ }
+ }
+
async toggleAttentionRequested(type, { user, callback }) {
try {
const isReviewer = type === 'reviewer';
@@ -82,15 +116,7 @@ export default class SidebarMediator {
const currentUserId = gon.current_user_id;
if (currentUserId !== user.id) {
- const currentUserReviewerOrAssignee = isReviewer
- ? this.store.findReviewer({ id: currentUserId })
- : this.store.findAssignee({ id: currentUserId });
-
- if (currentUserReviewerOrAssignee?.attention_requested) {
- // Update current users attention_requested state
- this.store.updateReviewer(currentUserId, 'attention_requested');
- this.store.updateAssignee(currentUserId, 'attention_requested');
- }
+ this.removeCurrentUserAttentionRequested();
}
toast(sprintf(__('Requested attention from @%{username}'), { username: user.username }));
diff --git a/app/assets/javascripts/sidebar/stores/sidebar_store.js b/app/assets/javascripts/sidebar/stores/sidebar_store.js
index 5376791469e..2caa6f4f0a0 100644
--- a/app/assets/javascripts/sidebar/stores/sidebar_store.js
+++ b/app/assets/javascripts/sidebar/stores/sidebar_store.js
@@ -98,6 +98,10 @@ export default class SidebarStore {
}
}
+ overwrite(key, newData) {
+ this[key] = newData;
+ }
+
findAssignee(findAssignee) {
return this.assignees.find(({ id }) => id === findAssignee.id);
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
index 386ba2e2d77..24cefd63ce3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
@@ -3,6 +3,7 @@ import { GlButton } from '@gitlab/ui';
import createFlash from '~/flash';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { s__ } from '~/locale';
+import sidebarEventHub from '~/sidebar/event_hub';
import eventHub from '../../event_hub';
import approvalsMixin from '../../mixins/approvals';
import MrWidgetContainer from '../mr_widget_container.vue';
@@ -172,6 +173,7 @@ export default {
this.mr.setApprovals(data);
eventHub.$emit('MRWidgetUpdateRequested');
eventHub.$emit('ApprovalUpdated');
+ sidebarEventHub.$emit('removeCurrentUserAttentionRequested');
this.$emit('updated');
})
.catch(errFn)
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index 662b02010ba..fa9517c3545 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -41,7 +41,7 @@ class Import::GitlabController < Import::BaseController
override :importable_repos
def importable_repos
- client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
+ client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS).to_a
end
override :incompatible_repos
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index fa7c62c34dd..bfc2fe6432d 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -19,6 +19,7 @@ class Projects::JobsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:infinitely_collapsible_sections, @project, default_enabled: :yaml)
+ push_frontend_feature_flag(:trigger_job_retry_action, @project, default_enabled: :yaml)
end
layout 'project'
diff --git a/app/helpers/ssh_keys_helper.rb b/app/helpers/ssh_keys_helper.rb
index f5a9bea482b..17a9fd6146d 100644
--- a/app/helpers/ssh_keys_helper.rb
+++ b/app/helpers/ssh_keys_helper.rb
@@ -18,4 +18,14 @@ module SshKeysHelper
container: 'body'
}
end
+
+ def ssh_key_allowed_algorithms
+ allowed_algorithms = Gitlab::CurrentSettings.allowed_key_types.flat_map do |ssh_key_type_name|
+ Gitlab::SSHPublicKey.supported_algorithms_for_name(ssh_key_type_name)
+ end
+
+ quoted_allowed_algorithms = allowed_algorithms.map { |name| "'#{name}'" }
+
+ Gitlab::Utils.to_exclusive_sentence(quoted_allowed_algorithms)
+ end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index d11304e5285..7ae301cdf73 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -1292,6 +1292,12 @@ module Ci
end
end
+ def use_variables_builder_definitions?
+ strong_memoize(:use_variables_builder_definitions) do
+ ::Feature.enabled?(:ci_use_variables_builder_definitions, project, default_enabled: :yaml)
+ end
+ end
+
private
def add_message(severity, content)
diff --git a/app/models/concerns/ci/contextable.rb b/app/models/concerns/ci/contextable.rb
index 12ddbc2cc40..ed3b422251f 100644
--- a/app/models/concerns/ci/contextable.rb
+++ b/app/models/concerns/ci/contextable.rb
@@ -13,6 +13,8 @@ module Ci
track_duration do
variables = pipeline.variables_builder.scoped_variables(self, environment: environment, dependencies: dependencies)
+ next variables if pipeline.use_variables_builder_definitions?
+
variables.concat(project.predefined_variables)
variables.concat(pipeline.predefined_variables)
variables.concat(runner.predefined_variables) if runnable? && runner
@@ -60,49 +62,27 @@ module Ci
end
def user_variables
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- break variables if user.blank?
-
- variables.append(key: 'GITLAB_USER_ID', value: user.id.to_s)
- variables.append(key: 'GITLAB_USER_EMAIL', value: user.email)
- variables.append(key: 'GITLAB_USER_LOGIN', value: user.username)
- variables.append(key: 'GITLAB_USER_NAME', value: user.name)
- end
+ pipeline.variables_builder.user_variables(user)
end
def kubernetes_variables
- ::Gitlab::Ci::Variables::Collection.new.tap do |collection|
- # Should get merged with the cluster kubeconfig in deployment_variables, see
- # https://gitlab.com/gitlab-org/gitlab/-/issues/335089
- template = ::Ci::GenerateKubeconfigService.new(self).execute
-
- if template.valid?
- collection.append(key: 'KUBECONFIG', value: template.to_yaml, public: false, file: true)
- end
- end
+ pipeline.variables_builder.kubernetes_variables(self)
end
def deployment_variables(environment:)
- return [] unless environment
-
- project.deployment_variables(
- environment: environment,
- kubernetes_namespace: expanded_kubernetes_namespace
- )
+ pipeline.variables_builder.deployment_variables(job: self, environment: environment)
end
def secret_instance_variables
- project.ci_instance_variables_for(ref: git_ref)
+ pipeline.variables_builder.secret_instance_variables(ref: git_ref)
end
def secret_group_variables(environment: expanded_environment_name)
- return [] unless project.group
-
- project.group.ci_variables_for(git_ref, project, environment: environment)
+ pipeline.variables_builder.secret_group_variables(environment: environment, ref: git_ref)
end
def secret_project_variables(environment: expanded_environment_name)
- project.ci_variables_for(ref: git_ref, environment: environment)
+ pipeline.variables_builder.secret_project_variables(environment: environment, ref: git_ref)
end
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 28b945f7c03..cacfe202fd2 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -627,14 +627,13 @@ class Group < Namespace
end
end
- def group_member(user)
+ def member(user)
if group_members.loaded?
group_members.find { |gm| gm.user_id == user.id }
else
group_members.find_by(user_id: user)
end
end
- alias_method :resource_member, :group_member
def highest_group_member(user)
GroupMember.where(source_id: self_and_ancestors_ids, user_id: user.id).order(:access_level).last
diff --git a/app/models/namespaces/traversal/linear_scopes.rb b/app/models/namespaces/traversal/linear_scopes.rb
index 3ed525f34c7..9f0f49e729c 100644
--- a/app/models/namespaces/traversal/linear_scopes.rb
+++ b/app/models/namespaces/traversal/linear_scopes.rb
@@ -22,7 +22,7 @@ module Namespaces
unscoped.where(id: root_ids)
end
- def self_and_ancestors(include_self: true, hierarchy_order: nil)
+ def self_and_ancestors(include_self: true, upto: nil, hierarchy_order: nil)
return super unless use_traversal_ids_for_ancestor_scopes?
ancestors_cte, base_cte = ancestor_ctes
@@ -35,11 +35,15 @@ module Namespaces
.where(namespaces[:id].eq(ancestors_cte.table[:ancestor_id]))
.order_by_depth(hierarchy_order)
- if include_self
- records
- else
- records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
+ unless include_self
+ records = records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
end
+
+ if upto
+ records = records.where.not(id: unscoped.where(id: upto).select('unnest(traversal_ids)'))
+ end
+
+ records
end
def self_and_ancestor_ids(include_self: true)
diff --git a/app/models/namespaces/traversal/recursive_scopes.rb b/app/models/namespaces/traversal/recursive_scopes.rb
index 925d9b8bb0c..583c53f8221 100644
--- a/app/models/namespaces/traversal/recursive_scopes.rb
+++ b/app/models/namespaces/traversal/recursive_scopes.rb
@@ -17,8 +17,8 @@ module Namespaces
.where(namespaces: { parent_id: nil })
end
- def self_and_ancestors(include_self: true, hierarchy_order: nil)
- records = Gitlab::ObjectHierarchy.new(all).base_and_ancestors(hierarchy_order: hierarchy_order)
+ def self_and_ancestors(include_self: true, upto: nil, hierarchy_order: nil)
+ records = Gitlab::ObjectHierarchy.new(all).base_and_ancestors(upto: upto, hierarchy_order: hierarchy_order)
if include_self
records
diff --git a/app/models/project.rb b/app/models/project.rb
index fa5e84ee83e..7e2c98c85b3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1661,14 +1661,13 @@ class Project < ApplicationRecord
attrs
end
- def project_member(user)
+ def member(user)
if project_members.loaded?
project_members.find { |member| member.user_id == user.id }
else
project_members.find_by(user_id: user)
end
end
- alias_method :resource_member, :project_member
def membership_locked?
false
diff --git a/app/models/user.rb b/app/models/user.rb
index 3dacff4a989..fec37172284 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1336,7 +1336,7 @@ class User < ApplicationRecord
def can_leave_project?(project)
project.namespace != namespace &&
- project.project_member(self)
+ project.member(self)
end
def full_website_url
diff --git a/app/serializers/merge_request_sidebar_basic_entity.rb b/app/serializers/merge_request_sidebar_basic_entity.rb
index 3c911bbe4c8..a0832655563 100644
--- a/app/serializers/merge_request_sidebar_basic_entity.rb
+++ b/app/serializers/merge_request_sidebar_basic_entity.rb
@@ -5,5 +5,9 @@ class MergeRequestSidebarBasicEntity < IssuableSidebarBasicEntity
expose :can_merge do |merge_request|
merge_request.can_be_merged_by?(current_user)
end
+
+ expose :can_update_merge_request do |merge_request|
+ current_user.can?(:update_merge_request, merge_request)
+ end
end
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index d744881549a..b0d0c32abd1 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -59,7 +59,9 @@ module MergeRequests
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
- remove_attention_requested(merge_request, current_user)
+ unless new_reviewers.include?(current_user)
+ remove_attention_requested(merge_request, current_user)
+ end
end
def cleanup_environments(merge_request)
diff --git a/app/services/merge_requests/handle_assignees_change_service.rb b/app/services/merge_requests/handle_assignees_change_service.rb
index 1d9f7ab59f4..97be9fe8d9f 100644
--- a/app/services/merge_requests/handle_assignees_change_service.rb
+++ b/app/services/merge_requests/handle_assignees_change_service.rb
@@ -23,7 +23,9 @@ module MergeRequests
execute_assignees_hooks(merge_request, old_assignees) if options[:execute_hooks]
- remove_attention_requested(merge_request, current_user)
+ unless new_assignees.include?(current_user)
+ remove_attention_requested(merge_request, current_user)
+ end
end
private
diff --git a/app/services/merge_requests/remove_approval_service.rb b/app/services/merge_requests/remove_approval_service.rb
index 872e7e0c89c..198a21884b8 100644
--- a/app/services/merge_requests/remove_approval_service.rb
+++ b/app/services/merge_requests/remove_approval_service.rb
@@ -17,6 +17,7 @@ module MergeRequests
reset_approvals_cache(merge_request)
create_note(merge_request)
merge_request_activity_counter.track_unapprove_mr_action(user: current_user)
+ remove_attention_requested(merge_request, current_user)
end
success
diff --git a/app/services/resource_access_tokens/revoke_service.rb b/app/services/resource_access_tokens/revoke_service.rb
index 9543ea4b68d..2aaf4cc31d2 100644
--- a/app/services/resource_access_tokens/revoke_service.rb
+++ b/app/services/resource_access_tokens/revoke_service.rb
@@ -43,13 +43,9 @@ module ResourceAccessTokens
def find_member
strong_memoize(:member) do
- if resource.is_a?(Project)
- resource.project_member(bot_user)
- elsif resource.is_a?(Group)
- resource.group_member(bot_user)
- else
- false
- end
+ next false unless resource.is_a?(Project) || resource.is_a?(Group)
+
+ resource.member(bot_user)
end
end
diff --git a/app/views/groups/settings/_ip_restriction_registration_features_cta.html.haml b/app/views/groups/settings/_ip_restriction_registration_features_cta.html.haml
new file mode 100644
index 00000000000..3067220ea8f
--- /dev/null
+++ b/app/views/groups/settings/_ip_restriction_registration_features_cta.html.haml
@@ -0,0 +1,8 @@
+- return unless registration_features_can_be_prompted?
+
+.form-group
+ = f.label :disabled_ip_restriction_ranges, class: 'label-bold' do
+ = _('Allow access to the following IP addresses')
+ = f.text_field :disabled_ip_restriction_ranges, value: '', class: 'form-control', disabled: true
+ %span.form-text.text-muted
+ = render 'shared/registration_features_discovery_message'
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index 59c47634c2d..9a7a7521cec 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -31,6 +31,7 @@
= render 'groups/settings/project_access_token_creation', f: f, group: @group
= render_if_exists 'groups/settings/delayed_project_removal', f: f, group: @group
+ = render 'groups/settings/ip_restriction_registration_features_cta', f: f
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
= render 'groups/settings/lfs', f: f
diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml
index bf9c77cb3ec..91af6953ee1 100644
--- a/app/views/profiles/gpg_keys/index.html.haml
+++ b/app/views/profiles/gpg_keys/index.html.haml
@@ -12,7 +12,7 @@
= _('Add a GPG key')
%p.profile-settings-content
- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/repository/gpg_signed_commits/index.md') }
- = _('Before you can add a GPG key you need to %{help_link_start}Generate it.%{help_link_end}'.html_safe) % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
+ = _('Add a GPG key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}').html_safe % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
= render 'form'
%hr
%h5
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 2b3109225a8..4b7dd42f5dc 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -5,8 +5,8 @@
.form-group
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
- %p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Do not paste your private SSH key, as that can compromise your identity.")
- = f.text_area :key, class: "form-control gl-form-input js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-ed25519 …" or "ssh-rsa …"')
+ = f.text_area :key, class: "form-control gl-form-input js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true
+ %p.form-text.text-muted= s_('Profiles|Begins with %{ssh_key_algorithms}.') % { ssh_key_algorithms: ssh_key_allowed_algorithms }
.form-row
.col.form-group
= f.label :title, _('Title'), class: 'label-bold'
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 584bd44e386..7d4c3b6115f 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -11,11 +11,8 @@
%h5.gl-mt-0
= _('Add an SSH key')
%p.profile-settings-content
- - generate_link_url = help_page_path("ssh/index", anchor: 'generate-an-ssh-key-pair')
- - existing_link_url = help_page_path("ssh/index", anchor: 'see-if-you-have-an-existing-ssh-key-pair')
- - generate_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: generate_link_url }
- - existing_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: existing_link_url }
- = _('To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}.').html_safe % { generate_link_start: generate_link_start, existing_link_start: existing_link_start, link_end: '</a>'.html_safe }
+ - help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('ssh/index.md') }
+ = _('Add an SSH key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}').html_safe % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
= render 'form'
%hr
%h5
diff --git a/app/views/shared/access_tokens/_table.html.haml b/app/views/shared/access_tokens/_table.html.haml
index a6a063d75f8..1e3432ab08b 100644
--- a/app/views/shared/access_tokens/_table.html.haml
+++ b/app/views/shared/access_tokens/_table.html.haml
@@ -55,7 +55,7 @@
- else
%span.token-never-expires-label= _('Never')
- if project
- %td= project.project_member(token.user).human_access
+ %td= project.member(token.user).human_access
%td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: "gl-button btn btn-danger btn-sm float-right qa-revoke-button #{'btn-danger-secondary' unless token.expires?}", data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } }
- else
.settings-message.text-center
diff --git a/config/feature_flags/development/ci_skip_require_credit_card_for_addon_ci_minutes.yml b/config/feature_flags/development/ci_skip_require_credit_card_for_addon_ci_minutes.yml
new file mode 100644
index 00000000000..e9c355bd119
--- /dev/null
+++ b/config/feature_flags/development/ci_skip_require_credit_card_for_addon_ci_minutes.yml
@@ -0,0 +1,8 @@
+---
+name: ci_skip_require_credit_card_for_addon_ci_minutes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77829
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349841
+milestone: '14.7'
+type: development
+group: group::fulfillment
+default_enabled: false
diff --git a/config/feature_flags/development/ci_use_variables_builder_definitions.yml b/config/feature_flags/development/ci_use_variables_builder_definitions.yml
new file mode 100644
index 00000000000..c01e4e9958e
--- /dev/null
+++ b/config/feature_flags/development/ci_use_variables_builder_definitions.yml
@@ -0,0 +1,8 @@
+---
+name: ci_use_variables_builder_definitions
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75254
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349049
+milestone: '14.7'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/trigger_job_retry_action.yml b/config/feature_flags/development/trigger_job_retry_action.yml
new file mode 100644
index 00000000000..79a8593fd05
--- /dev/null
+++ b/config/feature_flags/development/trigger_job_retry_action.yml
@@ -0,0 +1,8 @@
+---
+name: trigger_job_retry_action
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77951
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349966
+milestone: '14.7'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 5fac7a555d5..9c6191fe27c 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -2941,6 +2941,27 @@ Input type: `IssueSetEpicInput`
| <a id="mutationissuesetepicerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationissuesetepicissue"></a>`issue` | [`Issue`](#issue) | Issue after mutation. |
+### `Mutation.issueSetEscalationPolicy`
+
+Input type: `IssueSetEscalationPolicyInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationissuesetescalationpolicyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationissuesetescalationpolicyescalationpolicyid"></a>`escalationPolicyId` | [`IncidentManagementEscalationPolicyID`](#incidentmanagementescalationpolicyid) | Global ID of the escalation policy to assign to the issue. Policy will be removed if absent or set to null. |
+| <a id="mutationissuesetescalationpolicyiid"></a>`iid` | [`String!`](#string) | IID of the issue to mutate. |
+| <a id="mutationissuesetescalationpolicyprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the issue to mutate is in. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationissuesetescalationpolicyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationissuesetescalationpolicyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationissuesetescalationpolicyissue"></a>`issue` | [`Issue`](#issue) | Issue after mutation. |
+
### `Mutation.issueSetIteration`
Input type: `IssueSetIterationInput`
@@ -10294,6 +10315,7 @@ Relationship between an epic and an issue.
| <a id="epicissueemailsdisabled"></a>`emailsDisabled` | [`Boolean!`](#boolean) | Indicates if a project has email notifications disabled: `true` if email notifications are disabled. |
| <a id="epicissueepic"></a>`epic` | [`Epic`](#epic) | Epic to which this issue belongs. |
| <a id="epicissueepicissueid"></a>`epicIssueId` | [`ID!`](#id) | ID of the epic-issue relation. |
+| <a id="epicissueescalationpolicy"></a>`escalationPolicy` | [`EscalationPolicyType`](#escalationpolicytype) | Escalation policy associated with the issue. Available for issues which support escalation. |
| <a id="epicissuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Current health status. |
| <a id="epicissuehidden"></a>`hidden` | [`Boolean`](#boolean) | Indicates the issue is hidden because the author has been banned. Will always return `null` if `ban_user_feature_flag` feature flag is disabled. |
| <a id="epicissuehumantimeestimate"></a>`humanTimeEstimate` | [`String`](#string) | Human-readable time estimate of the issue. |
@@ -11476,6 +11498,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| <a id="issueduedate"></a>`dueDate` | [`Time`](#time) | Due date of the issue. |
| <a id="issueemailsdisabled"></a>`emailsDisabled` | [`Boolean!`](#boolean) | Indicates if a project has email notifications disabled: `true` if email notifications are disabled. |
| <a id="issueepic"></a>`epic` | [`Epic`](#epic) | Epic to which this issue belongs. |
+| <a id="issueescalationpolicy"></a>`escalationPolicy` | [`EscalationPolicyType`](#escalationpolicytype) | Escalation policy associated with the issue. Available for issues which support escalation. |
| <a id="issuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Current health status. |
| <a id="issuehidden"></a>`hidden` | [`Boolean`](#boolean) | Indicates the issue is hidden because the author has been banned. Will always return `null` if `ban_user_feature_flag` feature flag is disabled. |
| <a id="issuehumantimeestimate"></a>`humanTimeEstimate` | [`String`](#string) | Human-readable time estimate of the issue. |
diff --git a/lib/api/entities/project_with_access.rb b/lib/api/entities/project_with_access.rb
index ac89cb52e43..b541ccbadcf 100644
--- a/lib/api/entities/project_with_access.rb
+++ b/lib/api/entities/project_with_access.rb
@@ -8,7 +8,7 @@ module API
if options[:project_members]
options[:project_members].find { |member| member.source_id == project.id }
else
- project.project_member(options[:current_user])
+ project.member(options[:current_user])
end
end
diff --git a/lib/api/entities/resource_access_token.rb b/lib/api/entities/resource_access_token.rb
index d16baed38f0..569fd16f488 100644
--- a/lib/api/entities/resource_access_token.rb
+++ b/lib/api/entities/resource_access_token.rb
@@ -4,7 +4,7 @@ module API
module Entities
class ResourceAccessToken < Entities::PersonalAccessToken
expose :access_level do |token, options|
- options[:resource].resource_member(token.user).access_level
+ options[:resource].member(token.user).access_level
end
end
end
diff --git a/lib/gitlab/ci/variables/builder.rb b/lib/gitlab/ci/variables/builder.rb
index 3e2c2c7fc1a..4c777527ebc 100644
--- a/lib/gitlab/ci/variables/builder.rb
+++ b/lib/gitlab/ci/variables/builder.rb
@@ -13,12 +13,76 @@ module Gitlab
def scoped_variables(job, environment:, dependencies:)
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.concat(predefined_variables(job))
+
+ next variables unless pipeline.use_variables_builder_definitions?
+
+ variables.concat(project.predefined_variables)
+ variables.concat(pipeline.predefined_variables)
+ variables.concat(job.runner.predefined_variables) if job.runnable? && job.runner
+ variables.concat(kubernetes_variables(job))
+ variables.concat(deployment_variables(environment: environment, job: job))
+ variables.concat(job.yaml_variables)
+ variables.concat(user_variables(job.user))
+ variables.concat(job.dependency_variables) if dependencies
+ variables.concat(secret_instance_variables(ref: job.git_ref))
+ variables.concat(secret_group_variables(environment: environment, ref: job.git_ref))
+ variables.concat(secret_project_variables(environment: environment, ref: job.git_ref))
+ variables.concat(job.trigger_request.user_variables) if job.trigger_request
+ variables.concat(pipeline.variables)
+ variables.concat(pipeline.pipeline_schedule.job_variables) if pipeline.pipeline_schedule
+ end
+ end
+
+ def kubernetes_variables(job)
+ ::Gitlab::Ci::Variables::Collection.new.tap do |collection|
+ # Should get merged with the cluster kubeconfig in deployment_variables, see
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/335089
+ template = ::Ci::GenerateKubeconfigService.new(job).execute
+
+ if template.valid?
+ collection.append(key: 'KUBECONFIG', value: template.to_yaml, public: false, file: true)
+ end
end
end
+ def deployment_variables(environment:, job:)
+ return [] unless environment
+
+ project.deployment_variables(
+ environment: environment,
+ kubernetes_namespace: job.expanded_kubernetes_namespace
+ )
+ end
+
+ def user_variables(user)
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables if user.blank?
+
+ variables.append(key: 'GITLAB_USER_ID', value: user.id.to_s)
+ variables.append(key: 'GITLAB_USER_EMAIL', value: user.email)
+ variables.append(key: 'GITLAB_USER_LOGIN', value: user.username)
+ variables.append(key: 'GITLAB_USER_NAME', value: user.name)
+ end
+ end
+
+ def secret_instance_variables(ref:)
+ project.ci_instance_variables_for(ref: ref)
+ end
+
+ def secret_group_variables(environment:, ref:)
+ return [] unless project.group
+
+ project.group.ci_variables_for(ref, project, environment: environment)
+ end
+
+ def secret_project_variables(environment:, ref:)
+ project.ci_variables_for(ref: ref, environment: environment)
+ end
+
private
attr_reader :pipeline
+ delegate :project, to: :pipeline
def predefined_variables(job)
Gitlab::Ci::Variables::Collection.new.tap do |variables|
diff --git a/lib/gitlab/ssh_public_key.rb b/lib/gitlab/ssh_public_key.rb
index e51ec38394d..314cc5e2db6 100644
--- a/lib/gitlab/ssh_public_key.rb
+++ b/lib/gitlab/ssh_public_key.rb
@@ -2,13 +2,15 @@
module Gitlab
class SSHPublicKey
- Technology = Struct.new(:name, :key_class, :supported_sizes)
+ Technology = Struct.new(:name, :key_class, :supported_sizes, :supported_algorithms)
+ # See https://man.openbsd.org/sshd#AUTHORIZED_KEYS_FILE_FORMAT for the list of
+ # supported algorithms.
TECHNOLOGIES = [
- Technology.new(:rsa, OpenSSL::PKey::RSA, [1024, 2048, 3072, 4096]),
- Technology.new(:dsa, OpenSSL::PKey::DSA, [1024, 2048, 3072]),
- Technology.new(:ecdsa, OpenSSL::PKey::EC, [256, 384, 521]),
- Technology.new(:ed25519, Net::SSH::Authentication::ED25519::PubKey, [256])
+ Technology.new(:rsa, OpenSSL::PKey::RSA, [1024, 2048, 3072, 4096], %w(ssh-rsa)),
+ Technology.new(:dsa, OpenSSL::PKey::DSA, [1024, 2048, 3072], %w(ssh-dss)),
+ Technology.new(:ecdsa, OpenSSL::PKey::EC, [256, 384, 521], %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521)),
+ Technology.new(:ed25519, Net::SSH::Authentication::ED25519::PubKey, [256], %w(ssh-ed25519))
].freeze
def self.technology(name)
@@ -24,7 +26,15 @@ module Gitlab
end
def self.supported_sizes(name)
- technology(name)&.supported_sizes
+ technology(name).supported_sizes
+ end
+
+ def self.supported_algorithms
+ TECHNOLOGIES.flat_map { |tech| tech.supported_algorithms }
+ end
+
+ def self.supported_algorithms_for_name(name)
+ technology(name).supported_algorithms
end
def self.sanitize(key_content)
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 7807b765615..efb0e1ef1e1 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -179,7 +179,7 @@ namespace :gitlab do
task reindex: :environment do
unless Gitlab::Database::Reindexing.enabled?
puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
- next
+ exit
end
Gitlab::Database::Reindexing.invoke
@@ -193,7 +193,7 @@ namespace :gitlab do
task database_name => :environment do
unless Gitlab::Database::Reindexing.enabled?
puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
- next
+ exit
end
Gitlab::Database::Reindexing.invoke(database_name)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 38e9c348d1c..c12c26c6075 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2009,6 +2009,9 @@ msgstr ""
msgid "Add a GPG key"
msgstr ""
+msgid "Add a GPG key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}"
+msgstr ""
+
msgid "Add a Jaeger URL to replace this page with a link to your Jaeger server. You first need to %{link_start_tag}install Jaeger%{link_end_tag}."
msgstr ""
@@ -2075,6 +2078,9 @@ msgstr ""
msgid "Add an SSH key"
msgstr ""
+msgid "Add an SSH key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}"
+msgstr ""
+
msgid "Add an existing issue"
msgstr ""
@@ -4620,6 +4626,9 @@ msgstr ""
msgid "Are you sure you want to %{action} %{name}?"
msgstr ""
+msgid "Are you sure you want to approve %{user}?"
+msgstr ""
+
msgid "Are you sure you want to attempt to merge?"
msgstr ""
@@ -5535,9 +5544,15 @@ msgstr ""
msgid "Billings|Your account has been validated"
msgstr ""
+msgid "Billing|%{user} was successfully approved"
+msgstr ""
+
msgid "Billing|An email address is only visible for users with public emails."
msgstr ""
+msgid "Billing|An error occurred while approving %{user}"
+msgstr ""
+
msgid "Billing|An error occurred while getting a billable member details"
msgstr ""
@@ -9070,6 +9085,9 @@ msgstr ""
msgid "Confirm"
msgstr ""
+msgid "Confirm approval"
+msgstr ""
+
msgid "Confirm new password"
msgstr ""
@@ -25702,9 +25720,6 @@ msgstr ""
msgid "Paste this DSN into your Sentry SDK"
msgstr ""
-msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Do not paste your private SSH key, as that can compromise your identity."
-msgstr ""
-
msgid "Patch to apply"
msgstr ""
@@ -27109,6 +27124,9 @@ msgstr ""
msgid "Profiles|Avatar will be removed. Are you sure?"
msgstr ""
+msgid "Profiles|Begins with %{ssh_key_algorithms}."
+msgstr ""
+
msgid "Profiles|Bio"
msgstr ""
@@ -27358,9 +27376,6 @@ msgstr ""
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
-msgid "Profiles|Typically starts with \"ssh-ed25519 …\" or \"ssh-rsa …\""
-msgstr ""
-
msgid "Profiles|Update profile settings"
msgstr ""
@@ -37077,9 +37092,6 @@ msgstr ""
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
-msgstr ""
-
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index f757b7c69cf..117c934ad5d 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -30,6 +30,14 @@ RSpec.describe Import::GitlabController do
expect(session[:gitlab_access_token]).to eq(token)
expect(controller).to redirect_to(status_import_gitlab_url)
end
+
+ it "importable_repos should return an array" do
+ allow_next_instance_of(Gitlab::GitlabImport::Client) do |instance|
+ allow(instance).to receive(:projects).and_return([{ "id": 1 }].to_enum)
+ end
+
+ expect(controller.send(:importable_repos)).to be_an_instance_of(Array)
+ end
end
describe "GET status" do
diff --git a/spec/frontend/jobs/bridge/components/sidebar_spec.js b/spec/frontend/jobs/bridge/components/sidebar_spec.js
index 9debbb14c30..03f5a15f288 100644
--- a/spec/frontend/jobs/bridge/components/sidebar_spec.js
+++ b/spec/frontend/jobs/bridge/components/sidebar_spec.js
@@ -7,12 +7,16 @@ import { mockCommit, mockJob } from '../mock_data';
describe('Bridge Sidebar', () => {
let wrapper;
- const createComponent = (props) => {
+ const createComponent = ({ featureFlag } = {}) => {
wrapper = shallowMount(BridgeSidebar, {
+ provide: {
+ glFeatures: {
+ triggerJobRetryAction: featureFlag,
+ },
+ },
propsData: {
bridgeJob: mockJob,
commit: mockCommit,
- ...props,
},
});
};
@@ -35,10 +39,6 @@ describe('Bridge Sidebar', () => {
expect(findJobTitle().text()).toBe(mockJob.name);
});
- it('renders retry dropdown', () => {
- expect(findRetryDropdown().exists()).toBe(true);
- });
-
it('renders commit information', () => {
expect(findCommitBlock().exists()).toBe(true);
});
@@ -57,4 +57,24 @@ describe('Bridge Sidebar', () => {
expect(wrapper.emitted('toggleSidebar')).toHaveLength(1);
});
});
+
+ describe('retry action', () => {
+ describe('when feature flag is ON', () => {
+ beforeEach(() => {
+ createComponent({ featureFlag: true });
+ });
+
+ it('renders retry dropdown', () => {
+ expect(findRetryDropdown().exists()).toBe(true);
+ });
+ });
+
+ describe('when feature flag is OFF', () => {
+ it('does not render retry dropdown', () => {
+ createComponent({ featureFlag: false });
+
+ expect(findRetryDropdown().exists()).toBe(false);
+ });
+ });
+ });
});
diff --git a/spec/helpers/ssh_keys_helper_spec.rb b/spec/helpers/ssh_keys_helper_spec.rb
new file mode 100644
index 00000000000..1aa604f19be
--- /dev/null
+++ b/spec/helpers/ssh_keys_helper_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SshKeysHelper do
+ describe '#ssh_key_allowed_algorithms' do
+ it 'returns string with the names of allowed algorithms that are quoted and joined by commas' do
+ allowed_algorithms = Gitlab::CurrentSettings.allowed_key_types.flat_map do |ssh_key_type_name|
+ Gitlab::SSHPublicKey.supported_algorithms_for_name(ssh_key_type_name)
+ end
+
+ quoted_allowed_algorithms = allowed_algorithms.map { |name| "'#{name}'" }
+
+ expected_string = Gitlab::Utils.to_exclusive_sentence(quoted_allowed_algorithms)
+
+ expect(ssh_key_allowed_algorithms).to eq(expected_string)
+ end
+
+ it 'returns only allowed algorithms' do
+ expect(ssh_key_allowed_algorithms).to match('ed25519')
+ stub_application_setting(ed25519_key_restriction: ApplicationSetting::FORBIDDEN_KEY_VALUE)
+ expect(ssh_key_allowed_algorithms).not_to match('ed25519')
+ end
+ end
+end
diff --git a/spec/initializers/doorkeeper_spec.rb b/spec/initializers/doorkeeper_spec.rb
index 164225a00b2..56e0a22d59f 100644
--- a/spec/initializers/doorkeeper_spec.rb
+++ b/spec/initializers/doorkeeper_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Doorkeeper.configuration do
before do
allow(controller).to receive(:current_user).and_return(current_user)
allow(controller).to receive(:session).and_return({})
- allow(controller).to receive(:request).and_return(OpenStruct.new(fullpath: '/return-path'))
+ allow(controller).to receive(:request).and_return(double('request', fullpath: '/return-path'))
allow(controller).to receive(:redirect_to)
allow(controller).to receive(:new_user_session_url).and_return('/login')
end
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 5ff34592b2f..8a87cbe45c1 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -3,25 +3,201 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Variables::Builder do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:user) { project.owner }
+ let_it_be(:job) do
+ create(:ci_build,
+ pipeline: pipeline,
+ user: user,
+ yaml_variables: [{ key: 'YAML_VARIABLE', value: 'value' }]
+ )
+ end
+
let(:builder) { described_class.new(pipeline) }
- let(:pipeline) { create(:ci_pipeline) }
- let(:job) { create(:ci_build, pipeline: pipeline) }
describe '#scoped_variables' do
let(:environment) { job.expanded_environment_name }
let(:dependencies) { true }
+ let(:predefined_variables) do
+ [
+ { key: 'CI_JOB_NAME',
+ value: job.name },
+ { key: 'CI_JOB_STAGE',
+ value: job.stage },
+ { key: 'CI_NODE_TOTAL',
+ value: '1' },
+ { key: 'CI_BUILD_NAME',
+ value: job.name },
+ { key: 'CI_BUILD_STAGE',
+ value: job.stage },
+ { key: 'CI',
+ value: 'true' },
+ { key: 'GITLAB_CI',
+ value: 'true' },
+ { key: 'CI_SERVER_URL',
+ value: Gitlab.config.gitlab.url },
+ { key: 'CI_SERVER_HOST',
+ value: Gitlab.config.gitlab.host },
+ { key: 'CI_SERVER_PORT',
+ value: Gitlab.config.gitlab.port.to_s },
+ { key: 'CI_SERVER_PROTOCOL',
+ value: Gitlab.config.gitlab.protocol },
+ { key: 'CI_SERVER_NAME',
+ value: 'GitLab' },
+ { key: 'CI_SERVER_VERSION',
+ value: Gitlab::VERSION },
+ { key: 'CI_SERVER_VERSION_MAJOR',
+ value: Gitlab.version_info.major.to_s },
+ { key: 'CI_SERVER_VERSION_MINOR',
+ value: Gitlab.version_info.minor.to_s },
+ { key: 'CI_SERVER_VERSION_PATCH',
+ value: Gitlab.version_info.patch.to_s },
+ { key: 'CI_SERVER_REVISION',
+ value: Gitlab.revision },
+ { key: 'GITLAB_FEATURES',
+ value: project.licensed_features.join(',') },
+ { key: 'CI_PROJECT_ID',
+ value: project.id.to_s },
+ { key: 'CI_PROJECT_NAME',
+ value: project.path },
+ { key: 'CI_PROJECT_TITLE',
+ value: project.title },
+ { key: 'CI_PROJECT_PATH',
+ value: project.full_path },
+ { key: 'CI_PROJECT_PATH_SLUG',
+ value: project.full_path_slug },
+ { key: 'CI_PROJECT_NAMESPACE',
+ value: project.namespace.full_path },
+ { key: 'CI_PROJECT_ROOT_NAMESPACE',
+ value: project.namespace.root_ancestor.path },
+ { key: 'CI_PROJECT_URL',
+ value: project.web_url },
+ { key: 'CI_PROJECT_VISIBILITY',
+ value: "private" },
+ { key: 'CI_PROJECT_REPOSITORY_LANGUAGES',
+ value: project.repository_languages.map(&:name).join(',').downcase },
+ { key: 'CI_PROJECT_CLASSIFICATION_LABEL',
+ value: project.external_authorization_classification_label },
+ { key: 'CI_DEFAULT_BRANCH',
+ value: project.default_branch },
+ { key: 'CI_CONFIG_PATH',
+ value: project.ci_config_path_or_default },
+ { key: 'CI_PAGES_DOMAIN',
+ value: Gitlab.config.pages.host },
+ { key: 'CI_PAGES_URL',
+ value: project.pages_url },
+ { key: 'CI_API_V4_URL',
+ value: API::Helpers::Version.new('v4').root_url },
+ { key: 'CI_PIPELINE_IID',
+ value: pipeline.iid.to_s },
+ { key: 'CI_PIPELINE_SOURCE',
+ value: pipeline.source },
+ { key: 'CI_PIPELINE_CREATED_AT',
+ value: pipeline.created_at.iso8601 },
+ { key: 'CI_COMMIT_SHA',
+ value: job.sha },
+ { key: 'CI_COMMIT_SHORT_SHA',
+ value: job.short_sha },
+ { key: 'CI_COMMIT_BEFORE_SHA',
+ value: job.before_sha },
+ { key: 'CI_COMMIT_REF_NAME',
+ value: job.ref },
+ { key: 'CI_COMMIT_REF_SLUG',
+ value: job.ref_slug },
+ { key: 'CI_COMMIT_BRANCH',
+ value: job.ref },
+ { key: 'CI_COMMIT_MESSAGE',
+ value: pipeline.git_commit_message },
+ { key: 'CI_COMMIT_TITLE',
+ value: pipeline.git_commit_title },
+ { key: 'CI_COMMIT_DESCRIPTION',
+ value: pipeline.git_commit_description },
+ { key: 'CI_COMMIT_REF_PROTECTED',
+ value: (!!pipeline.protected_ref?).to_s },
+ { key: 'CI_COMMIT_TIMESTAMP',
+ value: pipeline.git_commit_timestamp },
+ { key: 'CI_COMMIT_AUTHOR',
+ value: pipeline.git_author_full_text },
+ { key: 'CI_BUILD_REF',
+ value: job.sha },
+ { key: 'CI_BUILD_BEFORE_SHA',
+ value: job.before_sha },
+ { key: 'CI_BUILD_REF_NAME',
+ value: job.ref },
+ { key: 'CI_BUILD_REF_SLUG',
+ value: job.ref_slug },
+ { key: 'YAML_VARIABLE',
+ value: 'value' },
+ { key: 'GITLAB_USER_ID',
+ value: user.id.to_s },
+ { key: 'GITLAB_USER_EMAIL',
+ value: user.email },
+ { key: 'GITLAB_USER_LOGIN',
+ value: user.username },
+ { key: 'GITLAB_USER_NAME',
+ value: user.name }
+ ].map { |var| var.merge(public: true, masked: false) }
+ end
subject { builder.scoped_variables(job, environment: environment, dependencies: dependencies) }
- it 'returns the expected variables' do
- keys = %w[CI_JOB_NAME
- CI_JOB_STAGE
- CI_NODE_TOTAL
- CI_BUILD_NAME
- CI_BUILD_STAGE]
+ it { is_expected.to be_instance_of(Gitlab::Ci::Variables::Collection) }
+
+ it { expect(subject.to_runner_variables).to eq(predefined_variables) }
+
+ context 'variables ordering' do
+ def var(name, value)
+ { key: name, value: value.to_s, public: true, masked: false }
+ end
+
+ before do
+ allow(builder).to receive(:predefined_variables) { [var('A', 1), var('B', 1)] }
+ allow(project).to receive(:predefined_variables) { [var('B', 2), var('C', 2)] }
+ allow(pipeline).to receive(:predefined_variables) { [var('C', 3), var('D', 3)] }
+ allow(job).to receive(:runner) { double(predefined_variables: [var('D', 4), var('E', 4)]) }
+ allow(builder).to receive(:kubernetes_variables) { [var('E', 5), var('F', 5)] }
+ allow(builder).to receive(:deployment_variables) { [var('F', 6), var('G', 6)] }
+ allow(job).to receive(:yaml_variables) { [var('G', 7), var('H', 7)] }
+ allow(builder).to receive(:user_variables) { [var('H', 8), var('I', 8)] }
+ allow(job).to receive(:dependency_variables) { [var('I', 9), var('J', 9)] }
+ allow(builder).to receive(:secret_instance_variables) { [var('J', 10), var('K', 10)] }
+ allow(builder).to receive(:secret_group_variables) { [var('K', 11), var('L', 11)] }
+ allow(builder).to receive(:secret_project_variables) { [var('L', 12), var('M', 12)] }
+ allow(job).to receive(:trigger_request) { double(user_variables: [var('M', 13), var('N', 13)]) }
+ allow(pipeline).to receive(:variables) { [var('N', 14), var('O', 14)] }
+ allow(pipeline).to receive(:pipeline_schedule) { double(job_variables: [var('O', 15), var('P', 15)]) }
+ end
+
+ it 'returns variables in order depending on resource hierarchy' do
+ expect(subject.to_runner_variables).to eq(
+ [var('A', 1), var('B', 1),
+ var('B', 2), var('C', 2),
+ var('C', 3), var('D', 3),
+ var('D', 4), var('E', 4),
+ var('E', 5), var('F', 5),
+ var('F', 6), var('G', 6),
+ var('G', 7), var('H', 7),
+ var('H', 8), var('I', 8),
+ var('I', 9), var('J', 9),
+ var('J', 10), var('K', 10),
+ var('K', 11), var('L', 11),
+ var('L', 12), var('M', 12),
+ var('M', 13), var('N', 13),
+ var('N', 14), var('O', 14),
+ var('O', 15), var('P', 15)])
+ end
- subject.map { |env| env[:key] }.tap do |names|
- expect(names).to include(*keys)
+ it 'overrides duplicate keys depending on resource hierarchy' do
+ expect(subject.to_hash).to match(
+ 'A' => '1', 'B' => '2',
+ 'C' => '3', 'D' => '4',
+ 'E' => '5', 'F' => '6',
+ 'G' => '7', 'H' => '8',
+ 'I' => '9', 'J' => '10',
+ 'K' => '11', 'L' => '12',
+ 'M' => '13', 'N' => '14',
+ 'O' => '15', 'P' => '15')
end
end
end
diff --git a/spec/lib/gitlab/database/migrations/runner_spec.rb b/spec/lib/gitlab/database/migrations/runner_spec.rb
index 4616bd6941e..7dc965c84fa 100644
--- a/spec/lib/gitlab/database/migrations/runner_spec.rb
+++ b/spec/lib/gitlab/database/migrations/runner_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Gitlab::Database::Migrations::Runner do
allow(ActiveRecord::Migrator).to receive(:new) do |dir, _all_migrations, _schema_migration_class, version_to_migrate|
migrator = double(ActiveRecord::Migrator)
expect(migrator).to receive(:run) do
- migration_runs << OpenStruct.new(dir: dir, version_to_migrate: version_to_migrate)
+ migration_runs << double('migrator', dir: dir, version_to_migrate: version_to_migrate)
end
migrator
end
diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb
index d2ff7f2d7df..38486b313cb 100644
--- a/spec/lib/gitlab/ssh_public_key_spec.rb
+++ b/spec/lib/gitlab/ssh_public_key_spec.rb
@@ -39,14 +39,43 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
]
end
- subject { described_class.supported_sizes(name) }
-
with_them do
it { expect(described_class.supported_sizes(name)).to eq(sizes) }
it { expect(described_class.supported_sizes(name.to_s)).to eq(sizes) }
end
end
+ describe '.supported_algorithms' do
+ it 'returns all supported algorithms' do
+ expect(described_class.supported_algorithms).to eq(
+ %w(
+ ssh-rsa
+ ssh-dss
+ ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
+ ssh-ed25519
+ )
+ )
+ end
+ end
+
+ describe '.supported_algorithms_for_name' do
+ where(:name, :algorithms) do
+ [
+ [:rsa, %w(ssh-rsa)],
+ [:dsa, %w(ssh-dss)],
+ [:ecdsa, %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521)],
+ [:ed25519, %w(ssh-ed25519)]
+ ]
+ end
+
+ with_them do
+ it "returns all supported algorithms for #{params[:name]}" do
+ expect(described_class.supported_algorithms_for_name(name)).to eq(algorithms)
+ expect(described_class.supported_algorithms_for_name(name.to_s)).to eq(algorithms)
+ end
+ end
+ end
+
describe '.sanitize(key_content)' do
let(:content) { build(:key).key }
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index a5af0cd32fd..9d1fa006cf8 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2824,7 +2824,7 @@ RSpec.describe Ci::Build do
allow(build).to receive(:dependency_variables) { [job_dependency_var] }
allow(build).to receive(:dependency_proxy_variables) { [dependency_proxy_var] }
- allow(build.project)
+ allow(build.pipeline.project)
.to receive(:predefined_variables) { [project_pre_var] }
project.variables.create!(key: 'secret', value: 'value')
@@ -3124,7 +3124,7 @@ RSpec.describe Ci::Build do
context 'when the branch is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3132,7 +3132,7 @@ RSpec.describe Ci::Build do
context 'when the tag is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3171,7 +3171,7 @@ RSpec.describe Ci::Build do
context 'when the branch is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3179,7 +3179,7 @@ RSpec.describe Ci::Build do
context 'when the tag is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
+ allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -3566,6 +3566,20 @@ RSpec.describe Ci::Build do
build.scoped_variables
end
+
+ context 'when variables builder is used' do
+ it 'returns the same variables' do
+ build.user = create(:user)
+
+ allow(build.pipeline).to receive(:use_variables_builder_definitions?).and_return(false)
+ legacy_variables = build.scoped_variables.to_hash
+
+ allow(build.pipeline).to receive(:use_variables_builder_definitions?).and_return(true)
+ new_variables = build.scoped_variables.to_hash
+
+ expect(new_variables).to eq(legacy_variables)
+ end
+ end
end
describe '#simple_variables_without_dependencies' do
@@ -3578,7 +3592,8 @@ RSpec.describe Ci::Build do
shared_examples "secret CI variables" do
context 'when ref is branch' do
- let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, ref: 'master', tag: false, pipeline: pipeline, project: project) }
context 'when ref is protected' do
before do
@@ -3594,7 +3609,8 @@ RSpec.describe Ci::Build do
end
context 'when ref is tag' do
- let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, pipeline: pipeline, project: project) }
context 'when ref is protected' do
before do
@@ -3692,8 +3708,6 @@ RSpec.describe Ci::Build do
.and_return(project_variables)
end
- it { is_expected.to eq(project_variables) }
-
context 'environment is nil' do
let(:environment) { nil }
@@ -3701,6 +3715,35 @@ RSpec.describe Ci::Build do
end
end
+ describe '#user_variables' do
+ subject { build.user_variables.to_hash }
+
+ context 'with user' do
+ let(:expected_variables) do
+ {
+ 'GITLAB_USER_EMAIL' => user.email,
+ 'GITLAB_USER_ID' => user.id.to_s,
+ 'GITLAB_USER_LOGIN' => user.username,
+ 'GITLAB_USER_NAME' => user.name
+ }
+ end
+
+ before do
+ build.user = user
+ end
+
+ it { is_expected.to eq(expected_variables) }
+ end
+
+ context 'without user' do
+ before do
+ expect(build).to receive(:user).and_return(nil)
+ end
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '#any_unmet_prerequisites?' do
let(:build) { create(:ci_build, :created) }
diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb
index 5a682ee8532..bc325aad823 100644
--- a/spec/requests/api/maven_packages_spec.rb
+++ b/spec/requests/api/maven_packages_spec.rb
@@ -425,7 +425,7 @@ RSpec.describe API::MavenPackages do
context 'internal project' do
before do
- group.group_member(user).destroy!
+ group.member(user).destroy!
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
diff --git a/spec/requests/projects/merge_requests_discussions_spec.rb b/spec/requests/projects/merge_requests_discussions_spec.rb
index 4921a43ab8b..6cf7bfb1795 100644
--- a/spec/requests/projects/merge_requests_discussions_spec.rb
+++ b/spec/requests/projects/merge_requests_discussions_spec.rb
@@ -244,7 +244,7 @@ RSpec.describe 'merge requests discussions' do
context 'when current_user role changes' do
before do
- Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(project.project_member(user))
+ Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(project.member(user))
end
it_behaves_like 'cache miss' do
diff --git a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
index 017e55309f7..9287bbd29fb 100644
--- a/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb
@@ -50,7 +50,7 @@ RSpec.shared_examples 'project access tokens available #create' do
expect(created_token.name).to eq(access_token_params[:name])
expect(created_token.scopes).to eq(access_token_params[:scopes])
expect(created_token.expires_at).to eq(access_token_params[:expires_at])
- expect(project.project_member(created_token.user).access_level).to eq(access_level)
+ expect(project.member(created_token.user).access_level).to eq(access_level)
end
it 'creates project bot user' do
diff --git a/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
index a2c34cdd4a1..601a53ed913 100644
--- a/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
+++ b/spec/support/shared_examples/finders/snippet_visibility_shared_examples.rb
@@ -233,7 +233,7 @@ RSpec.shared_examples 'snippet visibility' do
project.update!(visibility_level: Gitlab::VisibilityLevel.level_value(project_visibility.to_s), snippets_access_level: feature_visibility)
if user_type == :external
- member = project.project_member(external)
+ member = project.member(external)
if project.private?
project.add_developer(external) unless member
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index d5d137922eb..5b4b8c8fcc1 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'inherited access level as a member of entity' do
let(:parent_entity) { create(:group) }
let(:user) { create(:user) }
- let(:member) { entity.is_a?(Group) ? entity.group_member(user) : entity.project_member(user) }
+ let(:member) { entity.member(user) }
context 'with root parent_entity developer member' do
before do
@@ -49,7 +49,7 @@ RSpec.shared_examples 'inherited access level as a member of entity' do
entity.add_maintainer(non_member_user)
- non_member = entity.is_a?(Group) ? entity.group_member(non_member_user) : entity.project_member(non_member_user)
+ non_member = entity.member(non_member_user)
expect { non_member.update!(access_level: Gitlab::Access::GUEST) }
.to change { non_member.reload.access_level }
diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
index 19b418d1d6d..b43b7946e69 100644
--- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
@@ -130,6 +130,12 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { is_expected.to contain_exactly(group_2, nested_group_2, deep_nested_group_2) }
end
+
+ context 'with upto' do
+ subject { described_class.where(id: deep_nested_group_1).self_and_ancestors(upto: nested_group_1.id) }
+
+ it { is_expected.to contain_exactly(deep_nested_group_1) }
+ end
end
describe '.self_and_ancestors' do
diff --git a/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb b/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb
new file mode 100644
index 00000000000..535d078db63
--- /dev/null
+++ b/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'renders registration features prompt' do |disabled_field, feature_title|
+ it 'renders a placeholder input with registration features message' do
+ render
+
+ if disabled_field
+ expect(rendered).to have_field(disabled_field, disabled: true)
+ end
+
+ expect(rendered).to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: feature_title || s_('RegistrationFeatures|use this feature') })
+ expect(rendered).to have_link(s_('RegistrationFeatures|Registration Features Program'))
+ end
+end
+
+RSpec.shared_examples 'does not render registration features prompt' do |disabled_field, feature_title|
+ it 'does not render a placeholder input with registration features message' do
+ render
+
+ if disabled_field
+ expect(rendered).not_to have_field(disabled_field, disabled: true)
+ end
+
+ expect(rendered).not_to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: feature_title || s_('RegistrationFeatures|use this feature') })
+ expect(rendered).not_to have_link(s_('RegistrationFeatures|Registration Features Program'))
+ end
+end
diff --git a/spec/support/system_exit_detected.rb b/spec/support/system_exit_detected.rb
new file mode 100644
index 00000000000..86c6af3ba8c
--- /dev/null
+++ b/spec/support/system_exit_detected.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+SystemExitDetected = Class.new(RuntimeError)
+
+RSpec.configure do |config|
+ config.around do |example|
+ example.run
+ rescue SystemExit
+ # In any cases, we cannot raise SystemExit in the tests,
+ # because it'll skip any following tests from running.
+ # Convert it to something that won't skip everything.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/350060
+ raise SystemExitDetected, "SystemExit should be rescued in the tests!"
+ end
+end
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index 830d0dded2e..92c896b1ab0 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -214,7 +214,7 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
expect(Gitlab::Database::Reindexing).to receive(:enabled?).and_return(false)
expect(Gitlab::Database::Reindexing).not_to receive(:invoke)
- run_rake_task('gitlab:db:reindex')
+ expect { run_rake_task('gitlab:db:reindex') }.to raise_error(SystemExit)
end
end
end
@@ -233,7 +233,7 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
expect(Gitlab::Database::Reindexing).to receive(:enabled?).and_return(false)
expect(Gitlab::Database::Reindexing).not_to receive(:invoke).with(database_name)
- run_rake_task("gitlab:db:reindex:#{database_name}")
+ expect { run_rake_task("gitlab:db:reindex:#{database_name}") }.to raise_error(SystemExit)
end
end
end
diff --git a/spec/views/groups/edit.html.haml_spec.rb b/spec/views/groups/edit.html.haml_spec.rb
index 43e11d31611..748fe986d43 100644
--- a/spec/views/groups/edit.html.haml_spec.rb
+++ b/spec/views/groups/edit.html.haml_spec.rb
@@ -115,4 +115,40 @@ RSpec.describe 'groups/edit.html.haml' do
end
end
end
+
+ context 'ip_restriction' do
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+
+ before do
+ group.add_owner(user)
+
+ assign(:group, group)
+ allow(view).to receive(:current_user) { user }
+ end
+
+ context 'prompt user about registration features' do
+ before do
+ if Gitlab.ee?
+ allow(License).to receive(:current).and_return(nil)
+ end
+ end
+
+ context 'with service ping disabled' do
+ before do
+ stub_application_setting(usage_ping_enabled: false)
+ end
+
+ it_behaves_like 'renders registration features prompt', :group_disabled_ip_restriction_ranges
+ end
+
+ context 'with service ping enabled' do
+ before do
+ stub_application_setting(usage_ping_enabled: true)
+ end
+
+ it_behaves_like 'does not render registration features prompt', :group_disabled_ip_restriction_ranges
+ end
+ end
+ end
end
diff --git a/spec/views/profiles/keys/_form.html.haml_spec.rb b/spec/views/profiles/keys/_form.html.haml_spec.rb
index d5a605958dc..624d7492aea 100644
--- a/spec/views/profiles/keys/_form.html.haml_spec.rb
+++ b/spec/views/profiles/keys/_form.html.haml_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'profiles/keys/_form.html.haml' do
+ include SshKeysHelper
+
let_it_be(:key) { Key.new }
let(:page) { Capybara::Node::Simple.new(rendered) }
@@ -23,8 +25,8 @@ RSpec.describe 'profiles/keys/_form.html.haml' do
end
it 'has the key field', :aggregate_failures do
- expect(rendered).to have_field('Key', type: 'textarea', placeholder: 'Typically starts with "ssh-ed25519 …" or "ssh-rsa …"')
- expect(rendered).to have_text("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Do not paste your private SSH key, as that can compromise your identity.")
+ expect(rendered).to have_field('Key', type: 'textarea')
+ expect(rendered).to have_text(s_('Profiles|Begins with %{ssh_key_algorithms}.') % { ssh_key_algorithms: ssh_key_allowed_algorithms })
end
it 'has the title field', :aggregate_failures do