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>2023-02-20 15:12:47 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 15:12:47 +0300
commitc984b0faf409dbe91a7998260fe4b8299cf21ad4 (patch)
treeb1e434732c9d94ecaab0727f56a811b80a61a371
parentbd28d0fa02dc73794e013159512900f8d10fa10b (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/gitlab/namespaced_class.yml1
-rw-r--r--.rubocop_todo/layout/space_in_lambda_literal.yml1
-rw-r--r--.rubocop_todo/lint/unused_block_argument.yml1
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml1
-rw-r--r--app/assets/javascripts/boards/components/board_column.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue11
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue37
-rw-r--r--app/assets/javascripts/pages/registrations/new/index.js5
-rw-r--r--app/assets/javascripts/right_sidebar.js2
-rw-r--r--app/controllers/admin/runners_controller.rb4
-rw-r--r--app/controllers/admin/spam_logs_controller.rb2
-rw-r--r--app/controllers/registrations_controller.rb1
-rw-r--r--app/graphql/types/ci/runner_type.rb19
-rw-r--r--app/models/ci/runner.rb9
-rw-r--r--app/models/concerns/has_unique_internal_users.rb2
-rw-r--r--app/models/concerns/uniquify.rb40
-rw-r--r--app/models/issue.rb2
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/services/resource_access_tokens/create_service.rb2
-rw-r--r--app/views/admin/runners/register.html.haml4
-rw-r--r--app/views/admin/spam_logs/index.html.haml2
-rw-r--r--config/feature_flags/development/trial_email_validation.yml8
-rw-r--r--config/routes/admin.rb1
-rw-r--r--doc/administration/auth/ldap/ldap_synchronization.md23
-rw-r--r--doc/api/graphql/reference/index.md3
-rw-r--r--doc/ci/triggers/index.md10
-rw-r--r--doc/development/secure_coding_guidelines.md25
-rw-r--r--doc/subscriptions/gitlab_com/index.md5
-rw-r--r--doc/user/admin_area/license_file.md7
-rw-r--r--lib/bulk_imports/groups/transformers/group_attributes_transformer.rb7
-rw-r--r--lib/gitlab/auth/o_auth/user.rb2
-rw-r--r--lib/gitlab/ci/config/header/input.rb24
-rw-r--r--lib/gitlab/ci/config/header/root.rb36
-rw-r--r--lib/gitlab/ci/config/header/spec.rb24
-rw-r--r--lib/gitlab/utils/uniquify.rb45
-rw-r--r--lib/safe_zip/extract.rb21
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/qa/resource/runner_base.rb2
-rw-r--r--qa/qa/service/docker_run/gitlab_runner.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb2
-rw-r--r--scripts/api/base.rb28
-rwxr-xr-xscripts/api/cancel_pipeline.rb14
-rw-r--r--scripts/api/commit_merge_requests.rb19
-rw-r--r--scripts/api/create_issue.rb24
-rw-r--r--scripts/api/create_issue_discussion.rb24
-rw-r--r--scripts/api/find_issues.rb24
-rwxr-xr-xscripts/api/get_job_id.rb19
-rw-r--r--scripts/api/pipeline_failed_jobs.rb21
-rw-r--r--spec/controllers/admin/runners_controller_spec.rb45
-rw-r--r--spec/controllers/admin/spam_logs_controller_spec.rb5
-rw-r--r--spec/frontend/boards/components/board_form_spec.js32
-rw-r--r--spec/frontend/notifications/components/custom_notifications_modal_spec.js50
-rw-r--r--spec/frontend/vue_shared/components/file_row_spec.js10
-rw-r--r--spec/graphql/types/ci/runner_type_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/config/header/input_spec.rb70
-rw-r--r--spec/lib/gitlab/ci/config/header/root_spec.rb133
-rw-r--r--spec/lib/gitlab/ci/config/header/spec_spec.rb44
-rw-r--r--spec/lib/gitlab/utils/uniquify_spec.rb (renamed from spec/models/concerns/uniquify_spec.rb)10
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb36
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/shared_examples/features/trial_email_validation_shared_example.rb65
61 files changed, 759 insertions, 323 deletions
diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml
index eec55aa7bbe..d1257e3ffac 100644
--- a/.rubocop_todo/gitlab/namespaced_class.yml
+++ b/.rubocop_todo/gitlab/namespaced_class.yml
@@ -133,7 +133,6 @@ Gitlab/NamespacedClass:
- 'app/models/commit_status.rb'
- 'app/models/commit_user_mention.rb'
- 'app/models/compare.rb'
- - 'app/models/concerns/uniquify.rb'
- 'app/models/container_expiration_policy.rb'
- 'app/models/container_repository.rb'
- 'app/models/context_commits_diff.rb'
diff --git a/.rubocop_todo/layout/space_in_lambda_literal.yml b/.rubocop_todo/layout/space_in_lambda_literal.yml
index 3abff1e8788..362d9b20eb1 100644
--- a/.rubocop_todo/layout/space_in_lambda_literal.yml
+++ b/.rubocop_todo/layout/space_in_lambda_literal.yml
@@ -400,7 +400,6 @@ Layout/SpaceInLambdaLiteral:
- 'spec/models/ability_spec.rb'
- 'spec/models/broadcast_message_spec.rb'
- 'spec/models/concerns/participable_spec.rb'
- - 'spec/models/concerns/uniquify_spec.rb'
- 'spec/models/merge_request_spec.rb'
- 'spec/support/shared_examples/lib/cache_helpers_shared_examples.rb'
- 'spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb'
diff --git a/.rubocop_todo/lint/unused_block_argument.yml b/.rubocop_todo/lint/unused_block_argument.yml
index f226f04445a..188b57db7a7 100644
--- a/.rubocop_todo/lint/unused_block_argument.yml
+++ b/.rubocop_todo/lint/unused_block_argument.yml
@@ -376,7 +376,6 @@ Lint/UnusedBlockArgument:
- 'spec/models/concerns/ci/partitionable/switch_spec.rb'
- 'spec/models/concerns/ci/partitionable_spec.rb'
- 'spec/models/concerns/each_batch_spec.rb'
- - 'spec/models/concerns/uniquify_spec.rb'
- 'spec/models/container_repository_spec.rb'
- 'spec/models/network/graph_spec.rb'
- 'spec/models/packages/debian/file_metadatum_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index b40b3115913..9ceeab04049 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -5954,7 +5954,6 @@ RSpec/MissingFeatureCategory:
- 'spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb'
- 'spec/models/concerns/transactions_spec.rb'
- 'spec/models/concerns/triggerable_hooks_spec.rb'
- - 'spec/models/concerns/uniquify_spec.rb'
- 'spec/models/concerns/usage_statistics_spec.rb'
- 'spec/models/concerns/vulnerability_finding_helpers_spec.rb'
- 'spec/models/concerns/vulnerability_finding_signature_helpers_spec.rb'
diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue
index 708e1539c6e..54af5d35005 100644
--- a/app/assets/javascripts/boards/components/board_column.vue
+++ b/app/assets/javascripts/boards/components/board_column.vue
@@ -37,7 +37,7 @@ export default {
watch: {
filterParams: {
handler() {
- if (this.list.id && !this.list.collapsed) {
+ if (!this.isApolloBoard && this.list.id && !this.list.collapsed) {
this.fetchItemsForList({ listId: this.list.id });
}
},
@@ -46,7 +46,7 @@ export default {
},
'list.id': {
handler(id) {
- if (id) {
+ if (!this.isApolloBoard && id) {
this.fetchItemsForList({ listId: this.list.id });
}
},
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index a71bde54a8f..b396686f477 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -57,6 +57,9 @@ export default {
isProjectBoard: {
default: false,
},
+ isApolloBoard: {
+ default: false,
+ },
},
props: {
canAdminBoard: {
@@ -213,7 +216,11 @@ export default {
} else {
try {
const board = await this.createOrUpdateBoard();
- this.setBoard(board);
+ if (this.isApolloBoard) {
+ this.$emit('addBoard', board);
+ } else {
+ this.setBoard(board);
+ }
this.cancel();
const param = getParameterByName('group_by')
@@ -278,7 +285,7 @@ export default {
@hide.prevent
>
<gl-alert
- v-if="error"
+ v-if="!isApolloBoard && error"
class="gl-mb-3"
variant="danger"
:dismissible="true"
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index a1a49386b37..4aec286a5f4 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -8,6 +8,7 @@ import {
GlDropdownItem,
GlModalDirective,
} from '@gitlab/ui';
+import { produce } from 'immer';
import { throttle } from 'lodash';
import { mapActions, mapState } from 'vuex';
@@ -89,6 +90,9 @@ export default {
parentType() {
return this.boardType;
},
+ boardQuery() {
+ return this.isGroupBoard ? groupBoardsQuery : projectBoardsQuery;
+ },
loading() {
return this.loadingRecentBoards || this.loadingBoards;
},
@@ -155,9 +159,6 @@ export default {
name: node.name,
}));
},
- boardQuery() {
- return this.isGroupBoard ? groupBoardsQuery : projectBoardsQuery;
- },
recentBoardsQuery() {
return this.isGroupBoard ? groupRecentBoardsQuery : projectRecentBoardsQuery;
},
@@ -191,6 +192,29 @@ export default {
},
});
},
+ addBoard(board) {
+ const { defaultClient: store } = this.$apollo.provider.clients;
+
+ const sourceData = store.readQuery({
+ query: this.boardQuery,
+ variables: { fullPath: this.fullPath },
+ });
+
+ const newData = produce(sourceData, (draftState) => {
+ draftState[this.parentType].boards.edges = [
+ ...draftState[this.parentType].boards.edges,
+ { node: board },
+ ];
+ });
+
+ store.writeQuery({
+ query: this.boardQuery,
+ variables: { fullPath: this.fullPath },
+ data: newData,
+ });
+
+ this.$emit('switchBoard', board.id);
+ },
isScrolledUp() {
const { content } = this.$refs;
@@ -226,14 +250,12 @@ export default {
boardType: this.boardType,
});
},
- fullBoardId(boardId) {
- return fullBoardId(boardId);
- },
async switchBoard(boardId, e) {
if (isMetaKey(e)) {
window.open(`${this.boardBaseUrl}/${boardId}`, '_blank');
} else if (this.isApolloBoard) {
- this.$emit('switchBoard', this.fullBoardId(boardId));
+ this.$emit('switchBoard', fullBoardId(boardId));
+ updateHistory({ url: `${this.boardBaseUrl}/${boardId}` });
} else {
this.unsetActiveId();
this.fetchCurrentBoard(boardId);
@@ -357,6 +379,7 @@ export default {
:weights="weights"
:current-board="boardToUse"
:current-page="currentPage"
+ @addBoard="addBoard"
@cancel="cancel"
/>
</span>
diff --git a/app/assets/javascripts/pages/registrations/new/index.js b/app/assets/javascripts/pages/registrations/new/index.js
index eaafc0235a8..630a91489f3 100644
--- a/app/assets/javascripts/pages/registrations/new/index.js
+++ b/app/assets/javascripts/pages/registrations/new/index.js
@@ -10,10 +10,7 @@ import Tracking from '~/tracking';
new UsernameValidator(); // eslint-disable-line no-new
new LengthValidator(); // eslint-disable-line no-new
new NoEmojiValidator(); // eslint-disable-line no-new
-
-if (gon.features.trialEmailValidation) {
- new EmailFormatValidator(); // eslint-disable-line no-new
-}
+new EmailFormatValidator(); // eslint-disable-line no-new
trackNewRegistrations();
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 3256e13f4da..297b8ae1fc2 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -70,7 +70,7 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
const $expandIcon = $('.js-sidebar-expand');
const $toggleContainer = $('.js-sidebar-toggle-container');
const isExpanded = $toggleContainer.data('is-expanded');
- const tooltipLabel = isExpanded ? __('Expand sidebar') : __('Collapse sidebar');
+ const tooltipLabel = isExpanded ? __('Collapse sidebar') : __('Expand sidebar');
e.preventDefault();
if (isExpanded) {
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 21a3a0aea0b..77f80dacd11 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -26,6 +26,10 @@ class Admin::RunnersController < Admin::ApplicationController
render_404 unless Feature.enabled?(:create_runner_workflow, current_user)
end
+ def register
+ render_404 unless Feature.enabled?(:create_runner_workflow, current_user) && runner.registration_available?
+ end
+
def update
if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success?
respond_to do |format|
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index 478708f3d9b..b27185a6add 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -5,7 +5,7 @@ class Admin::SpamLogsController < Admin::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def index
- @spam_logs = SpamLog.includes(:user).order(id: :desc).page(params[:page])
+ @spam_logs = SpamLog.includes(:user).order(id: :desc).page(params[:page]).without_count
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index ed0e019d02b..f9d522f93bb 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -25,7 +25,6 @@ class RegistrationsController < Devise::RegistrationsController
before_action only: [:new] do
push_frontend_feature_flag(:gitlab_gtm_datalayer, type: :ops)
- push_frontend_feature_flag(:trial_email_validation, type: :development)
end
feature_category :authentication_and_authorization
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index 10d18f9ad2a..8284386d1af 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -14,9 +14,6 @@ module Types
JOB_COUNT_LIMIT = 1000
- # Only allow ephemeral_authentication_token to be visible for a short while
- RUNNER_EPHEMERAL_TOKEN_AVAILABILITY_TIME = 3.hours
-
alias_method :runner, :object
field :access_level, ::Types::Ci::RunnerAccessLevelEnum, null: false,
@@ -39,7 +36,7 @@ module Types
field :edit_admin_url, GraphQL::Types::String, null: true,
description: 'Admin form URL of the runner. Only available for administrators.'
field :ephemeral_authentication_token, GraphQL::Types::String, null: true,
- description: 'Ephemeral authentication token used for runner machine registration.',
+ description: 'Ephemeral authentication token used for runner machine registration. Only available for the creator of the runner for a limited time during registration.',
authorize: :read_ephemeral_token,
alpha: { milestone: '15.9' }
field :executor_name, GraphQL::Types::String, null: true,
@@ -84,6 +81,8 @@ module Types
null: true,
resolver: ::Resolvers::Ci::RunnerProjectsResolver,
description: 'Find projects the runner is associated with. For project runners only.'
+ field :register_admin_url, GraphQL::Types::String, null: true,
+ description: 'URL of the temporary registration page of the runner. Only available before the runner is registered. Only available for administrators.'
field :revision, GraphQL::Types::String, null: true,
description: 'Revision of the runner.'
field :run_untagged, GraphQL::Types::Boolean, null: false,
@@ -141,12 +140,14 @@ module Types
Gitlab::Routing.url_helpers.edit_admin_runner_url(runner) if can_admin_runners?
end
- def ephemeral_authentication_token
- return unless runner.authenticated_user_registration_type?
- return unless runner.created_at > RUNNER_EPHEMERAL_TOKEN_AVAILABILITY_TIME.ago
- return if runner.runner_machines.any?
+ def register_admin_url
+ return unless can_admin_runners? && runner.registration_available?
- runner.token
+ Gitlab::Routing.url_helpers.register_admin_runner_url(runner)
+ end
+
+ def ephemeral_authentication_token
+ runner.token if runner.registration_available?
end
def project_count
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 09ac0fa69e7..07f2231634d 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -54,6 +54,9 @@ module Ci
# The `STALE_TIMEOUT` constant defines the how far past the last contact or creation date a runner will be considered stale
STALE_TIMEOUT = 3.months
+ # Only allow authentication token to be visible for a short while
+ REGISTRATION_AVAILABILITY_TIME = 1.hour
+
AVAILABLE_TYPES_LEGACY = %w[specific shared].freeze
AVAILABLE_TYPES = runner_types.keys.freeze
AVAILABLE_STATUSES = %w[active paused online offline never_contacted stale].freeze # TODO: Remove in %16.0: active, paused. Relevant issue: https://gitlab.com/gitlab-org/gitlab/-/issues/344648
@@ -499,6 +502,12 @@ module Ci
RunnerMachine.safe_find_or_create_by!(runner_id: id, system_xid: system_xid.to_s, &blk) # rubocop: disable Performance/ActiveRecordSubtransactionMethods
end
+ def registration_available?
+ authenticated_user_registration_type? &&
+ created_at > REGISTRATION_AVAILABILITY_TIME.ago &&
+ !runner_machines.any?
+ end
+
private
scope :with_upgrade_status, ->(upgrade_status) do
diff --git a/app/models/concerns/has_unique_internal_users.rb b/app/models/concerns/has_unique_internal_users.rb
index 4d60cfa03b0..25b56f6d70f 100644
--- a/app/models/concerns/has_unique_internal_users.rb
+++ b/app/models/concerns/has_unique_internal_users.rb
@@ -28,7 +28,7 @@ module HasUniqueInternalUsers
existing_user = uncached { scope.first }
return existing_user if existing_user.present?
- uniquify = Uniquify.new
+ uniquify = Gitlab::Utils::Uniquify.new
username = uniquify.string(username) { |s| User.find_by_username(s) }
diff --git a/app/models/concerns/uniquify.rb b/app/models/concerns/uniquify.rb
deleted file mode 100644
index 382e826ec58..00000000000
--- a/app/models/concerns/uniquify.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-# Uniquify
-#
-# Return a version of the given 'base' string that is unique
-# by appending a counter to it. Uniqueness is determined by
-# repeated calls to the passed block.
-#
-# You can pass an initial value for the counter, if not given
-# counting starts from 1.
-#
-# If `base` is a function/proc, we expect that calling it with a
-# candidate counter returns a string to test/return.
-class Uniquify
- def initialize(counter = nil)
- @counter = counter
- end
-
- def string(base)
- @base = base
-
- increment_counter! while yield(base_string)
- base_string
- end
-
- private
-
- def base_string
- if @base.respond_to?(:call)
- @base.call(@counter)
- else
- "#{@base}#{@counter}"
- end
- end
-
- def increment_counter!
- @counter ||= 0
- @counter += 1
- end
-end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index bea86168c8d..483bfca259c 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -463,7 +463,7 @@ class Issue < ApplicationRecord
"#{to_branch_name}-#{suffix}"
end
- Uniquify.new(start_counting_from).string(branch_name_generator) do |suggested_branch_name|
+ Gitlab::Utils::Uniquify.new(start_counting_from).string(branch_name_generator) do |suggested_branch_name|
project.repository.branch_exists?(suggested_branch_name)
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index c3bff24cb1a..252fb9aa308 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -244,7 +244,7 @@ class Namespace < ApplicationRecord
def clean_path(path, limited_to: Namespace.all)
slug = Gitlab::Slug::Path.new(path).generate
path = Namespaces::RandomizedSuffixPath.new(slug)
- Uniquify.new.string(path) { |s| limited_to.find_by_path_or_name(s) }
+ Gitlab::Utils::Uniquify.new.string(path) { |s| limited_to.find_by_path_or_name(s) }
end
def clean_name(value)
diff --git a/app/services/resource_access_tokens/create_service.rb b/app/services/resource_access_tokens/create_service.rb
index c6948536053..f122fea6967 100644
--- a/app/services/resource_access_tokens/create_service.rb
+++ b/app/services/resource_access_tokens/create_service.rb
@@ -85,7 +85,7 @@ module ResourceAccessTokens
end
def uniquify
- Uniquify.new
+ Gitlab::Utils::Uniquify.new
end
def create_personal_access_token(user)
diff --git a/app/views/admin/runners/register.html.haml b/app/views/admin/runners/register.html.haml
new file mode 100644
index 00000000000..f1477d38e98
--- /dev/null
+++ b/app/views/admin/runners/register.html.haml
@@ -0,0 +1,4 @@
+- add_to_breadcrumbs _('Runners'), admin_runners_path
+- breadcrumb_title s_('Runners|Register')
+- page_title s_('Runners|Register'), "##{@runner.id} (#{@runner.short_sha})"
+
diff --git a/app/views/admin/spam_logs/index.html.haml b/app/views/admin/spam_logs/index.html.haml
index c974f455112..001662c4015 100644
--- a/app/views/admin/spam_logs/index.html.haml
+++ b/app/views/admin/spam_logs/index.html.haml
@@ -17,6 +17,6 @@
%th= _('Primary Action')
%th
= render @spam_logs
- = paginate @spam_logs, theme: 'gitlab'
+ = paginate_collection @spam_logs
- else
%h4= _('There are no Spam Logs')
diff --git a/config/feature_flags/development/trial_email_validation.yml b/config/feature_flags/development/trial_email_validation.yml
deleted file mode 100644
index c658a49f195..00000000000
--- a/config/feature_flags/development/trial_email_validation.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: trial_email_validation
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92762
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368999
-milestone: '15.3'
-type: development
-group: group::acquisition
-default_enabled: false
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 9181c1c94cf..0d74ddeaabf 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -168,6 +168,7 @@ namespace :admin do
resources :runners, only: [:index, :new, :show, :edit, :update, :destroy] do
member do
+ get :register
post :resume
post :pause
end
diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md
index cc300941470..431fbd89439 100644
--- a/doc/administration/auth/ldap/ldap_synchronization.md
+++ b/doc/administration/auth/ldap/ldap_synchronization.md
@@ -33,9 +33,6 @@ For more information, see [Bitmask Searches in LDAP](https://ctovswild.com/2009/
<!-- vale gitlab.Spelling = YES -->
-The user is set to an `ldap_blocked` state in GitLab if the previous conditions
-fail. This means the user cannot sign in or push or pull code.
-
The process also updates the following user information:
- Name. Because of a [sync issue](https://gitlab.com/gitlab-org/gitlab/-/issues/342598), `name` is not synchronized if
@@ -44,6 +41,26 @@ The process also updates the following user information:
- SSH public keys if `sync_ssh_keys` is set.
- Kerberos identity if Kerberos is enabled.
+### Blocked users
+
+A user is blocked if either the:
+
+- [Access check fails](#user-sync) and that user is set to an `ldap_blocked` state in GitLab.
+- LDAP server is not available when that user signs in.
+
+If a user is blocked, that user cannot sign in or push or pull code.
+
+A blocked user is unblocked when they sign in with LDAP if all of the following are true:
+
+- All the access check conditions are true.
+- The LDAP server is available when the user signs in.
+
+**All users** are blocked if the LDAP server is unavailable when an LDAP user synchronization is run.
+
+NOTE:
+If all users are blocked due to the LDAP server not being available when an LDAP user synchronization is run,
+a subsequent LDAP user synchronization does not automatically unblock those users.
+
### Adjust LDAP user sync schedule
By default, GitLab runs a worker once per day at 01:30 a.m. server time to
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index ff663ef3156..5e0ef4f9c5d 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -11544,7 +11544,7 @@ CI/CD variables for a project.
| <a id="cirunnercreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of creation of this runner. |
| <a id="cirunnerdescription"></a>`description` | [`String`](#string) | Description of the runner. |
| <a id="cirunnereditadminurl"></a>`editAdminUrl` | [`String`](#string) | Admin form URL of the runner. Only available for administrators. |
-| <a id="cirunnerephemeralauthenticationtoken"></a>`ephemeralAuthenticationToken` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.9. This feature is in Alpha. It can be changed or removed at any time. Ephemeral authentication token used for runner machine registration. |
+| <a id="cirunnerephemeralauthenticationtoken"></a>`ephemeralAuthenticationToken` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.9. This feature is in Alpha. It can be changed or removed at any time. Ephemeral authentication token used for runner machine registration. Only available for the creator of the runner for a limited time during registration. |
| <a id="cirunnerexecutorname"></a>`executorName` | [`String`](#string) | Executor last advertised by the runner. |
| <a id="cirunnergroups"></a>`groups` | [`GroupConnection`](#groupconnection) | Groups the runner is associated with. For group runners only. (see [Connections](#connections)) |
| <a id="cirunnerid"></a>`id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner. |
@@ -11561,6 +11561,7 @@ CI/CD variables for a project.
| <a id="cirunnerprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). |
| <a id="cirunnerprojectcount"></a>`projectCount` | [`Int`](#int) | Number of projects that the runner is associated with. |
| <a id="cirunnerpublicprojectsminutescostfactor"></a>`publicProjectsMinutesCostFactor` | [`Float`](#float) | Public projects' "minutes cost factor" associated with the runner (GitLab.com only). |
+| <a id="cirunnerregisteradminurl"></a>`registerAdminUrl` | [`String`](#string) | URL of the temporary registration page of the runner. Only available before the runner is registered. Only available for administrators. |
| <a id="cirunnerrevision"></a>`revision` | [`String`](#string) | Revision of the runner. |
| <a id="cirunnerrununtagged"></a>`runUntagged` | [`Boolean!`](#boolean) | Indicates the runner is able to run untagged jobs. |
| <a id="cirunnerrunnertype"></a>`runnerType` | [`CiRunnerType!`](#cirunnertype) | Type of the runner. |
diff --git a/doc/ci/triggers/index.md b/doc/ci/triggers/index.md
index 16530ea20b6..f1f9f3e2485 100644
--- a/doc/ci/triggers/index.md
+++ b/doc/ci/triggers/index.md
@@ -201,3 +201,13 @@ doesn't exist, GitLab returns `The requested URL returned error: 400`.
For example, you might accidentally use `main` for the branch name in a project that
uses a different branch name for its default branch.
+
+Another possible cause for this error is a rule that prevents creation of the pipelines when `CI_PIPELINE_SOURCE` value is `trigger`, such as:
+
+```yaml
+rules:
+ - if: $CI_PIPELINE_SOURCE == "trigger"
+ when: never
+```
+
+Review your [`workflow:rules`](../yaml/index.md#workflowrules) to ensure a pipeline can be created when `CI_PIPELINE_SOURCE` value is `trigger`.
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index 6c64e3b2acc..12665acacff 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -852,6 +852,31 @@ In the example above, the `is_admin?` method is overwritten when passing it to t
Working with archive files like `zip`, `tar`, `jar`, `war`, `cpio`, `apk`, `rar` and `7z` presents an area where potentially critical security vulnerabilities can sneak into an application.
+### Utilities for safely working with archive files
+
+There are common utilities that can be used to securely work with archive files.
+
+#### Ruby
+
+| Archive type | Utility |
+|--------------|-------------|
+| `zip` | `SafeZip` |
+
+#### `SafeZip`
+
+SafeZip provides a safe interface to extract specific directories or files within a `zip` archive through the `SafeZip::Extract` class.
+
+Example:
+
+```ruby
+Dir.mktmpdir do |tmp_dir|
+ SafeZip::Extract.new(zip_file_path).extract(files: ['index.html', 'app/index.js'], to: tmp_dir)
+ SafeZip::Extract.new(zip_file_path).extract(directories: ['src/', 'test/'], to: tmp_dir)
+rescue SafeZip::Extract::EntrySizeError
+ raise Error, "Path `#{file_path}` has invalid size in the zip!"
+end
+```
+
### Zip Slip
In 2018, the security company Snyk [released a blog post](https://security.snyk.io/research/zip-slip-vulnerability) describing research into a widespread and critical vulnerability present in many libraries and applications which allows an attacker to overwrite arbitrary files on the server file system which, in many cases, can be leveraged to achieve remote code execution. The vulnerability was dubbed Zip Slip.
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index 632581e66d3..3d6d16c21ee 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -70,8 +70,9 @@ The following information is displayed:
A GitLab SaaS subscription uses a concurrent (_seat_) model. You pay for a
subscription according to the maximum number of users assigned to the top-level group or its children during the billing period. You can
-add and remove users during the subscription period, as long as the total users
-at any given time doesn't exceed the subscription count.
+add and remove users during the subscription period without incurring additional charges, as long as the total users
+at any given time doesn't exceed the subscription count. If the total users exceeds your subscription count, you will incur an overage
+which must be paid at your next [reconciliation](../quarterly_reconciliation.md).
A top-level group can be [changed](../../user/group/manage.md#change-a-groups-path) like any other group.
diff --git a/doc/user/admin_area/license_file.md b/doc/user/admin_area/license_file.md
index 29e43476819..69edb4551da 100644
--- a/doc/user/admin_area/license_file.md
+++ b/doc/user/admin_area/license_file.md
@@ -185,6 +185,13 @@ License.current.license_id
License.current.data
```
+#### Interaction with licenses that start in the future
+
+```ruby
+# Future license data follows the same format as current license data it just uses a different modifier for the License prefix
+License.future_dated
+```
+
#### Check if a project feature is available on the instance
Features listed in [`features.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/gitlab_subscriptions/features.rb).
diff --git a/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb b/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb
index 19993629ff5..18ef460385c 100644
--- a/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb
+++ b/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb
@@ -65,9 +65,10 @@ module BulkImports
namespace_children_names = namespace.children.pluck(:name) # rubocop: disable CodeReuse/ActiveRecord
if namespace_children_names.include?(data['name'])
- data['name'] = Uniquify.new(1).string(-> (counter) { "#{data['name']}(#{counter})" }) do |base|
- namespace_children_names.include?(base)
- end
+ data['name'] =
+ Gitlab::Utils::Uniquify.new(1).string(-> (counter) { "#{data['name']}(#{counter})" }) do |base|
+ namespace_children_names.include?(base)
+ end
end
end
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index 01e126ec2f5..bb47b4236fb 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -233,7 +233,7 @@ module Gitlab
email ||= auth_hash.email
valid_username = ::Namespace.clean_path(username)
- valid_username = Uniquify.new.string(valid_username) { |s| !NamespacePathValidator.valid_path?(s) }
+ valid_username = Gitlab::Utils::Uniquify.new.string(valid_username) { |s| !NamespacePathValidator.valid_path?(s) }
{
name: name.strip.presence || valid_username,
diff --git a/lib/gitlab/ci/config/header/input.rb b/lib/gitlab/ci/config/header/input.rb
new file mode 100644
index 00000000000..525b009afe3
--- /dev/null
+++ b/lib/gitlab/ci/config/header/input.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Header
+ ##
+ # Input parameter used for interpolation with the CI configuration.
+ class Input < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+ include ::Gitlab::Config::Entry::Attributable
+
+ attributes :default, prefix: :input
+
+ validations do
+ validates :config, type: Hash, allowed_keys: [:default]
+ validates :key, alphanumeric: true
+ validates :input_default, alphanumeric: true, allow_nil: true
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/header/root.rb b/lib/gitlab/ci/config/header/root.rb
new file mode 100644
index 00000000000..251682d13b4
--- /dev/null
+++ b/lib/gitlab/ci/config/header/root.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Header
+ ##
+ # This class represents the root entry of the GitLab CI configuration header.
+ #
+ # A header is the first document in a multi-doc YAML that contains metadata
+ # and specifications about the GitLab CI configuration (the second document).
+ #
+ # The header is optional. A CI configuration can also be represented with a
+ # YAML containing a single document.
+ class Root < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Configurable
+
+ ALLOWED_KEYS = %i[spec].freeze
+
+ validations do
+ validates :config, type: Hash, allowed_keys: ALLOWED_KEYS
+ end
+
+ entry :spec, Header::Spec,
+ description: 'Specifications of the CI configuration.',
+ inherit: false,
+ default: {}
+
+ def inputs_value
+ spec_entry.inputs_value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/header/spec.rb b/lib/gitlab/ci/config/header/spec.rb
new file mode 100644
index 00000000000..98d6d0d5783
--- /dev/null
+++ b/lib/gitlab/ci/config/header/spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Header
+ class Spec < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Configurable
+
+ ALLOWED_KEYS = %i[inputs].freeze
+
+ validations do
+ validates :config, type: Hash, allowed_keys: ALLOWED_KEYS
+ end
+
+ entry :inputs, ::Gitlab::Config::Entry::ComposableHash,
+ description: 'Allowed input parameters used for interpolation.',
+ inherit: false,
+ metadata: { composable_class: ::Gitlab::Ci::Config::Header::Input }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/utils/uniquify.rb b/lib/gitlab/utils/uniquify.rb
new file mode 100644
index 00000000000..b5908d18103
--- /dev/null
+++ b/lib/gitlab/utils/uniquify.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+# Uniquify
+#
+# Return a version of the given 'base' string that is unique
+# by appending a counter to it. Uniqueness is determined by
+# repeated calls to the passed block.
+#
+# You can pass an initial value for the counter, if not given
+# counting starts from 1.
+#
+# If `base` is a function/proc, we expect that calling it with a
+# candidate counter returns a string to test/return.
+
+module Gitlab
+ module Utils
+ class Uniquify
+ def initialize(counter = nil)
+ @counter = counter
+ end
+
+ def string(base)
+ @base = base
+
+ increment_counter! while yield(base_string)
+ base_string
+ end
+
+ private
+
+ def base_string
+ if @base.respond_to?(:call)
+ @base.call(@counter)
+ else
+ "#{@base}#{@counter}"
+ end
+ end
+
+ def increment_counter!
+ @counter ||= 0
+ @counter += 1
+ end
+ end
+ end
+end
diff --git a/lib/safe_zip/extract.rb b/lib/safe_zip/extract.rb
index b86941e6bea..3403e3e429e 100644
--- a/lib/safe_zip/extract.rb
+++ b/lib/safe_zip/extract.rb
@@ -1,6 +1,13 @@
# frozen_string_literal: true
module SafeZip
+ # SafeZip::Extract provides a safe interface
+ # to extract specific directories or files within a `zip` archive.
+ #
+ # @example Extract directories to destination
+ # SafeZip::Extract.new(archive_file).extract(directories: ['app/', 'test/'], to: destination_path)
+ # @example Extract files to destination
+ # SafeZip::Extract.new(archive_file).extract(files: ['index.html', 'app/index.js'], to: destination_path)
class Extract
Error = Class.new(StandardError)
PermissionDeniedError = Class.new(Error)
@@ -17,6 +24,20 @@ module SafeZip
@archive_path = archive_file
end
+ # extract given files or directories from the archive into the destination path
+ #
+ # @param [Hash] opts the options for extraction.
+ # @option opts [Array<String] :files list of files to be extracted
+ # @option opts [Array<String] :directories list of directories to be extracted
+ # @option opts [String] :to destination path
+ #
+ # @raise [PermissionDeniedError]
+ # @raise [SymlinkSourceDoesNotExistError]
+ # @raise [UnsupportedEntryError]
+ # @raise [EntrySizeError]
+ # @raise [AlreadyExistsError]
+ # @raise [NoMatchingError]
+ # @raise [ExtractError]
def extract(opts = {})
params = SafeZip::ExtractParams.new(**opts)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 200a8079a7b..da09ebd8400 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -37138,6 +37138,9 @@ msgstr ""
msgid "Runners|Recommended"
msgstr ""
+msgid "Runners|Register"
+msgstr ""
+
msgid "Runners|Register a group runner"
msgstr ""
diff --git a/qa/qa/resource/runner_base.rb b/qa/qa/resource/runner_base.rb
index 9386a5d9765..399d1153dc2 100644
--- a/qa/qa/resource/runner_base.rb
+++ b/qa/qa/resource/runner_base.rb
@@ -35,7 +35,7 @@ module QA
@config = nil
@run_untagged = nil
@name = "qa-runner-#{SecureRandom.hex(4)}"
- @image = 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine'
+ @image = 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine-v15.8.3'
@executor = :shell
@executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
end
diff --git a/qa/qa/service/docker_run/gitlab_runner.rb b/qa/qa/service/docker_run/gitlab_runner.rb
index d6d1a085b77..a8fcf8f9332 100644
--- a/qa/qa/service/docker_run/gitlab_runner.rb
+++ b/qa/qa/service/docker_run/gitlab_runner.rb
@@ -16,7 +16,7 @@ module QA
MSG
def initialize(name)
- @image = 'gitlab/gitlab-runner:alpine'
+ @image = 'gitlab/gitlab-runner:alpine-v15.8.3'
@name = name || "qa-runner-#{SecureRandom.hex(4)}"
@run_untagged = true
@executor = :shell
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
index a504b694308..0f6bee951a7 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
@@ -19,7 +19,7 @@ module QA
resource.project = project
resource.name = runner_name
resource.tags = [runner_name]
- resource.image = 'gitlab/gitlab-runner:alpine'
+ resource.image = 'gitlab/gitlab-runner:alpine-v15.8.3'
end
end
diff --git a/scripts/api/base.rb b/scripts/api/base.rb
new file mode 100644
index 00000000000..4872ede253d
--- /dev/null
+++ b/scripts/api/base.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'gitlab'
+require_relative 'default_options'
+
+class Base
+ def initialize(options)
+ @project = options.fetch(:project)
+
+ # If api_token is nil, it's set to '' to allow unauthenticated requests (for forks).
+ api_token = options.fetch(:api_token, '')
+
+ warn "No API token given." if api_token.empty?
+
+ @client = Gitlab.client(
+ endpoint: options.fetch(:endpoint, API::DEFAULT_OPTIONS[:endpoint]),
+ private_token: api_token
+ )
+ end
+
+ def execute
+ raise NotImplementedError
+ end
+
+ private
+
+ attr_reader :project, :client
+end
diff --git a/scripts/api/cancel_pipeline.rb b/scripts/api/cancel_pipeline.rb
index 2667cfb9733..5069527368b 100755
--- a/scripts/api/cancel_pipeline.rb
+++ b/scripts/api/cancel_pipeline.rb
@@ -1,19 +1,13 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
-require 'gitlab'
require 'optparse'
-require_relative 'default_options'
+require_relative 'base'
-class CancelPipeline
+class CancelPipeline < Base
def initialize(options)
- @project = options.delete(:project)
+ super
@pipeline_id = options.delete(:pipeline_id)
-
- @client = Gitlab.client(
- endpoint: options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint],
- private_token: options.delete(:api_token)
- )
end
def execute
@@ -22,7 +16,7 @@ class CancelPipeline
private
- attr_reader :project, :pipeline_id, :client
+ attr_reader :pipeline_id
end
if $PROGRAM_NAME == __FILE__
diff --git a/scripts/api/commit_merge_requests.rb b/scripts/api/commit_merge_requests.rb
index 3cf8dc87497..523d2e769f0 100644
--- a/scripts/api/commit_merge_requests.rb
+++ b/scripts/api/commit_merge_requests.rb
@@ -1,22 +1,11 @@
# frozen_string_literal: true
-require 'gitlab'
-require_relative 'default_options'
+require_relative 'base'
-class CommitMergeRequests
+class CommitMergeRequests < Base
def initialize(options)
- @project = options.fetch(:project)
+ super
@sha = options.fetch(:sha)
-
- # If api_token is nil, it's set to '' to allow unauthenticated requests (for forks).
- api_token = options.fetch(:api_token, '')
-
- warn "No API token given." if api_token.empty?
-
- @client = Gitlab.client(
- endpoint: options.fetch(:endpoint, API::DEFAULT_OPTIONS[:endpoint]),
- private_token: api_token
- )
end
def execute
@@ -25,5 +14,5 @@ class CommitMergeRequests
private
- attr_reader :project, :sha, :client
+ attr_reader :sha
end
diff --git a/scripts/api/create_issue.rb b/scripts/api/create_issue.rb
index 2117c285771..1c385ce41f2 100644
--- a/scripts/api/create_issue.rb
+++ b/scripts/api/create_issue.rb
@@ -1,29 +1,9 @@
# frozen_string_literal: true
-require 'gitlab'
-require_relative 'default_options'
-
-class CreateIssue
- def initialize(options)
- @project = options.fetch(:project)
-
- # Force the token to be a string so that if api_token is nil, it's set to '',
- # allowing unauthenticated requests (for forks).
- api_token = options.delete(:api_token).to_s
-
- warn "No API token given." if api_token.empty?
-
- @client = Gitlab.client(
- endpoint: options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint],
- private_token: api_token
- )
- end
+require_relative 'base'
+class CreateIssue < Base
def execute(issue_data)
client.create_issue(project, issue_data.delete(:title), issue_data)
end
-
- private
-
- attr_reader :project, :client
end
diff --git a/scripts/api/create_issue_discussion.rb b/scripts/api/create_issue_discussion.rb
index 74a9f3ae378..6471a5c2579 100644
--- a/scripts/api/create_issue_discussion.rb
+++ b/scripts/api/create_issue_discussion.rb
@@ -1,32 +1,12 @@
# frozen_string_literal: true
-require 'gitlab'
-require_relative 'default_options'
-
-class CreateIssueDiscussion
- def initialize(options)
- @project = options.fetch(:project)
-
- # Force the token to be a string so that if api_token is nil, it's set to '',
- # allowing unauthenticated requests (for forks).
- api_token = options.delete(:api_token).to_s
-
- warn "No API token given." if api_token.empty?
-
- @client = Gitlab.client(
- endpoint: options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint],
- private_token: api_token
- )
- end
+require_relative 'base'
+class CreateIssueDiscussion < Base
def execute(discussion_data)
client.post(
"/projects/#{client.url_encode project}/issues/#{discussion_data.delete(:issue_iid)}/discussions",
body: discussion_data
)
end
-
- private
-
- attr_reader :project, :client
end
diff --git a/scripts/api/find_issues.rb b/scripts/api/find_issues.rb
index a1c37030319..f74f815fba9 100644
--- a/scripts/api/find_issues.rb
+++ b/scripts/api/find_issues.rb
@@ -1,29 +1,9 @@
# frozen_string_literal: true
-require 'gitlab'
-require_relative 'default_options'
-
-class FindIssues
- def initialize(options)
- @project = options.fetch(:project)
-
- # Force the token to be a string so that if api_token is nil, it's set to '',
- # allowing unauthenticated requests (for forks).
- api_token = options.delete(:api_token).to_s
-
- warn "No API token given." if api_token.empty?
-
- @client = Gitlab.client(
- endpoint: options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint],
- private_token: api_token
- )
- end
+require_relative 'base'
+class FindIssues < Base
def execute(search_data)
client.issues(project, search_data)
end
-
- private
-
- attr_reader :project, :client
end
diff --git a/scripts/api/get_job_id.rb b/scripts/api/get_job_id.rb
index 12535106a4c..babe8f5dee0 100755
--- a/scripts/api/get_job_id.rb
+++ b/scripts/api/get_job_id.rb
@@ -1,11 +1,10 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
-require 'gitlab'
require 'optparse'
-require_relative 'default_options'
+require_relative 'base'
-class JobFinder
+class JobFinder < Base
DEFAULT_OPTIONS = API::DEFAULT_OPTIONS.merge(
pipeline_query: {}.freeze,
job_query: {}.freeze
@@ -13,22 +12,12 @@ class JobFinder
MAX_PIPELINES_TO_ITERATE = 20
def initialize(options)
- @project = options.delete(:project)
+ super
@pipeline_query = options.delete(:pipeline_query) || DEFAULT_OPTIONS[:pipeline_query]
@job_query = options.delete(:job_query) || DEFAULT_OPTIONS[:job_query]
@pipeline_id = options.delete(:pipeline_id)
@job_name = options.delete(:job_name)
@artifact_path = options.delete(:artifact_path)
-
- # Force the token to be a string so that if api_token is nil, it's set to '', allowing unauthenticated requests (for forks).
- api_token = options.delete(:api_token).to_s
-
- warn "No API token given." if api_token.empty?
-
- @client = Gitlab.client(
- endpoint: options.delete(:endpoint) || DEFAULT_OPTIONS[:endpoint],
- private_token: api_token
- )
end
def execute
@@ -37,7 +26,7 @@ class JobFinder
private
- attr_reader :project, :pipeline_query, :job_query, :pipeline_id, :job_name, :artifact_path, :client
+ attr_reader :pipeline_query, :job_query, :pipeline_id, :job_name, :artifact_path
def find_job_with_artifact
return if artifact_path.nil?
diff --git a/scripts/api/pipeline_failed_jobs.rb b/scripts/api/pipeline_failed_jobs.rb
index df9a7e76dcd..9012d48994f 100644
--- a/scripts/api/pipeline_failed_jobs.rb
+++ b/scripts/api/pipeline_failed_jobs.rb
@@ -1,25 +1,12 @@
# frozen_string_literal: true
-require 'gitlab'
+require_relative 'base'
-require_relative 'default_options'
-
-class PipelineFailedJobs
+class PipelineFailedJobs < Base
def initialize(options)
- @project = options.delete(:project)
+ super
@pipeline_id = options.delete(:pipeline_id)
@exclude_allowed_to_fail_jobs = options.delete(:exclude_allowed_to_fail_jobs)
-
- # Force the token to be a string so that if api_token is nil, it's set to '',
- # allowing unauthenticated requests (for forks).
- api_token = options.delete(:api_token).to_s
-
- warn "No API token given." if api_token.empty?
-
- @client = Gitlab.client(
- endpoint: options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint],
- private_token: api_token
- )
end
def execute
@@ -43,5 +30,5 @@ class PipelineFailedJobs
private
- attr_reader :project, :pipeline_id, :exclude_allowed_to_fail_jobs, :client
+ attr_reader :pipeline_id, :exclude_allowed_to_fail_jobs
end
diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb
index a39a1f38a11..9555caf24db 100644
--- a/spec/controllers/admin/runners_controller_spec.rb
+++ b/spec/controllers/admin/runners_controller_spec.rb
@@ -61,6 +61,51 @@ RSpec.describe Admin::RunnersController, feature_category: :runner_fleet do
end
end
+ describe '#register' do
+ subject(:register) { get :register, params: { id: new_runner.id } }
+
+ context 'when create_runner_workflow is enabled' do
+ before do
+ stub_feature_flags(create_runner_workflow: true)
+ end
+
+ context 'when runner can be registered after creation' do
+ let_it_be(:new_runner) { create(:ci_runner, registration_type: :authenticated_user) }
+
+ it 'renders a :register template' do
+ register
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:register)
+ end
+ end
+
+ context 'when runner cannot be registered after creation' do
+ let_it_be(:new_runner) { runner }
+
+ it 'returns :not_found' do
+ register
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'when create_runner_workflow is disabled' do
+ let_it_be(:new_runner) { create(:ci_runner, registration_type: :authenticated_user) }
+
+ before do
+ stub_feature_flags(create_runner_workflow: false)
+ end
+
+ it 'returns :not_found' do
+ register
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
describe '#edit' do
render_views
diff --git a/spec/controllers/admin/spam_logs_controller_spec.rb b/spec/controllers/admin/spam_logs_controller_spec.rb
index 53eb107a251..b39c3bd009b 100644
--- a/spec/controllers/admin/spam_logs_controller_spec.rb
+++ b/spec/controllers/admin/spam_logs_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::SpamLogsController do
+RSpec.describe Admin::SpamLogsController, feature_category: :instance_resiliency do
let(:admin) { create(:admin) }
let(:user) { create(:user) }
let!(:first_spam) { create(:spam_log, user: user) }
@@ -13,9 +13,10 @@ RSpec.describe Admin::SpamLogsController do
end
describe '#index' do
- it 'lists all spam logs' do
+ it 'lists paginated spam logs' do
get :index
+ expect(assigns(:spam_logs)).to be_kind_of(Kaminari::PaginatableWithoutCount)
expect(response).to have_gitlab_http_status(:ok)
end
end
diff --git a/spec/frontend/boards/components/board_form_spec.js b/spec/frontend/boards/components/board_form_spec.js
index f8154145d43..3302fb2ce43 100644
--- a/spec/frontend/boards/components/board_form_spec.js
+++ b/spec/frontend/boards/components/board_form_spec.js
@@ -59,18 +59,14 @@ describe('BoardForm', () => {
},
});
- const createComponent = (props, data) => {
+ const createComponent = (props, provide) => {
wrapper = shallowMountExtended(BoardForm, {
propsData: { ...defaultProps, ...props },
- data() {
- return {
- ...data,
- };
- },
provide: {
boardBaseUrl: 'root',
isGroupBoard: true,
isProjectBoard: false,
+ ...provide,
},
mocks: {
$apollo: {
@@ -209,6 +205,30 @@ describe('BoardForm', () => {
expect(setBoardMock).not.toHaveBeenCalled();
expect(setErrorMock).toHaveBeenCalled();
});
+
+ describe('when Apollo boards FF is on', () => {
+ it('calls a correct GraphQL mutation and emits addBoard event', async () => {
+ createComponent(
+ { canAdminBoard: true, currentPage: formType.new },
+ { isApolloBoard: true },
+ );
+ fillForm();
+
+ await waitForPromises();
+
+ expect(mutate).toHaveBeenCalledWith({
+ mutation: createBoardMutation,
+ variables: {
+ input: expect.objectContaining({
+ name: 'test',
+ }),
+ },
+ });
+
+ await waitForPromises();
+ expect(wrapper.emitted('addBoard')).toHaveLength(1);
+ });
+ });
});
});
diff --git a/spec/frontend/notifications/components/custom_notifications_modal_spec.js b/spec/frontend/notifications/components/custom_notifications_modal_spec.js
index 70749557e61..aee681e73c9 100644
--- a/spec/frontend/notifications/components/custom_notifications_modal_spec.js
+++ b/spec/frontend/notifications/components/custom_notifications_modal_spec.js
@@ -2,7 +2,6 @@ import { GlSprintf, GlModal, GlFormGroup, GlFormCheckbox, GlLoadingIcon } from '
import { shallowMount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
@@ -87,24 +86,23 @@ describe('CustomNotificationsModal', () => {
describe('checkbox items', () => {
beforeEach(async () => {
+ const endpointUrl = '/api/v4/notification_settings';
+
+ mockAxios
+ .onGet(endpointUrl)
+ .reply(HTTP_STATUS_OK, mockNotificationSettingsResponses.default);
+
wrapper = createComponent();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- events: [
- { id: 'new_release', enabled: true, name: 'New release', loading: false },
- { id: 'new_note', enabled: false, name: 'New note', loading: true },
- ],
- });
+ wrapper.findComponent(GlModal).vm.$emit('show');
- await nextTick();
+ await waitForPromises();
});
it.each`
index | eventId | eventName | enabled | loading
${0} | ${'new_release'} | ${'New release'} | ${true} | ${false}
- ${1} | ${'new_note'} | ${'New note'} | ${false} | ${true}
+ ${1} | ${'new_note'} | ${'New note'} | ${false} | ${false}
`(
'renders a checkbox for "$eventName" with checked=$enabled',
async ({ index, eventName, enabled, loading }) => {
@@ -214,16 +212,9 @@ describe('CustomNotificationsModal', () => {
wrapper = createComponent({ injectedProperties });
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- events: [
- { id: 'new_release', enabled: true, name: 'New release', loading: false },
- { id: 'new_note', enabled: false, name: 'New note', loading: false },
- ],
- });
+ wrapper.findComponent(GlModal).vm.$emit('show');
- await nextTick();
+ await waitForPromises();
findCheckboxAt(1).vm.$emit('change', true);
@@ -241,19 +232,18 @@ describe('CustomNotificationsModal', () => {
);
it('shows a toast message when the request fails', async () => {
- mockAxios.onPut('/api/v4/notification_settings').reply(HTTP_STATUS_NOT_FOUND, {});
+ const endpointUrl = '/api/v4/notification_settings';
+
+ mockAxios
+ .onGet(endpointUrl)
+ .reply(HTTP_STATUS_OK, mockNotificationSettingsResponses.default);
+
+ mockAxios.onPut(endpointUrl).reply(HTTP_STATUS_NOT_FOUND, {});
wrapper = createComponent();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- events: [
- { id: 'new_release', enabled: true, name: 'New release', loading: false },
- { id: 'new_note', enabled: false, name: 'New note', loading: false },
- ],
- });
+ wrapper.findComponent(GlModal).vm.$emit('show');
- await nextTick();
+ await waitForPromises();
findCheckboxAt(1).vm.$emit('change', true);
diff --git a/spec/frontend/vue_shared/components/file_row_spec.js b/spec/frontend/vue_shared/components/file_row_spec.js
index b70d4565f56..25129ba4cd2 100644
--- a/spec/frontend/vue_shared/components/file_row_spec.js
+++ b/spec/frontend/vue_shared/components/file_row_spec.js
@@ -6,6 +6,9 @@ import FileIcon from '~/vue_shared/components/file_icon.vue';
import FileRow from '~/vue_shared/components/file_row.vue';
import FileHeader from '~/vue_shared/components/file_row_header.vue';
+const scrollIntoViewMock = jest.fn();
+HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
+
describe('File row component', () => {
let wrapper;
@@ -72,11 +75,10 @@ describe('File row component', () => {
},
level: 0,
});
- jest.spyOn(wrapper.vm, '$emit');
wrapper.element.click();
- expect(wrapper.vm.$emit).toHaveBeenCalledWith('toggleTreeOpen', fileName);
+ expect(wrapper.emitted('toggleTreeOpen')[0][0]).toEqual(fileName);
});
it('calls scrollIntoView if made active', () => {
@@ -89,14 +91,12 @@ describe('File row component', () => {
level: 0,
});
- jest.spyOn(wrapper.vm, 'scrollIntoView');
-
wrapper.setProps({
file: { ...wrapper.props('file'), active: true },
});
return nextTick().then(() => {
- expect(wrapper.vm.scrollIntoView).toHaveBeenCalled();
+ expect(scrollIntoViewMock).toHaveBeenCalled();
});
});
diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb
index a2d107ae295..3195d2cabb8 100644
--- a/spec/graphql/types/ci/runner_type_spec.rb
+++ b/spec/graphql/types/ci/runner_type_spec.rb
@@ -11,9 +11,9 @@ RSpec.describe GitlabSchema.types['CiRunner'], feature_category: :runner do
expected_fields = %w[
id description created_at contacted_at maximum_timeout access_level active paused status
version short_sha revision locked run_untagged ip_address runner_type tag_list
- project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name
- maintenance_note maintenance_note_html groups projects jobs token_expires_at owner_project job_execution_status
- ephemeral_authentication_token
+ project_count job_count admin_url edit_admin_url register_admin_url user_permissions executor_name
+ architecture_name platform_name maintenance_note maintenance_note_html groups projects jobs token_expires_at
+ owner_project job_execution_status ephemeral_authentication_token
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/gitlab/ci/config/header/input_spec.rb b/spec/lib/gitlab/ci/config/header/input_spec.rb
new file mode 100644
index 00000000000..f890aa45396
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/header/input_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Header::Input, feature_category: :pipeline_authoring do
+ let(:factory) do
+ Gitlab::Config::Entry::Factory
+ .new(described_class)
+ .value(input_hash)
+ .with(key: input_name)
+ end
+
+ let(:input_name) { 'foo' }
+
+ subject(:config) { factory.create!.tap(&:compose!) }
+
+ shared_examples 'a valid input' do
+ let(:expected_hash) { input_hash }
+
+ it 'passes validations' do
+ expect(config).to be_valid
+ expect(config.errors).to be_empty
+ end
+
+ it 'returns the value' do
+ expect(config.value).to eq(expected_hash)
+ end
+ end
+
+ shared_examples 'an invalid input' do
+ let(:expected_hash) { input_hash }
+
+ it 'fails validations' do
+ expect(config).not_to be_valid
+ expect(config.errors).to eq(expected_errors)
+ end
+
+ it 'returns the value' do
+ expect(config.value).to eq(expected_hash)
+ end
+ end
+
+ context 'when has a default value' do
+ let(:input_hash) { { default: 'bar' } }
+
+ it_behaves_like 'a valid input'
+ end
+
+ context 'when is a required required input' do
+ let(:input_hash) { nil }
+
+ it_behaves_like 'a valid input'
+ end
+
+ context 'when contains unknown keywords' do
+ let(:input_hash) { { test: 123 } }
+ let(:expected_errors) { ['foo config contains unknown keys: test'] }
+
+ it_behaves_like 'an invalid input'
+ end
+
+ context 'when has invalid name' do
+ let(:input_name) { [123] }
+ let(:input_hash) { {} }
+
+ let(:expected_errors) { ['123 key must be an alphanumeric string'] }
+
+ it_behaves_like 'an invalid input'
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/header/root_spec.rb b/spec/lib/gitlab/ci/config/header/root_spec.rb
new file mode 100644
index 00000000000..b137d0a2aa0
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/header/root_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Header::Root, feature_category: :pipeline_authoring do
+ let(:factory) { Gitlab::Config::Entry::Factory.new(described_class).value(header_hash) }
+
+ subject(:config) { factory.create!.tap(&:compose!) }
+
+ shared_examples 'a valid header' do
+ let(:expected_hash) { header_hash }
+
+ it 'passes validations' do
+ expect(config).to be_valid
+ expect(config.errors).to be_empty
+ end
+
+ it 'returns the value' do
+ expect(config.value).to eq(expected_hash)
+ end
+ end
+
+ shared_examples 'an invalid header' do
+ let(:expected_hash) { header_hash }
+
+ it 'fails validations' do
+ expect(config).not_to be_valid
+ expect(config.errors).to eq(expected_errors)
+ end
+
+ it 'returns the value' do
+ expect(config.value).to eq(expected_hash)
+ end
+ end
+
+ context 'when header contains default and required values for inputs' do
+ let(:header_hash) do
+ {
+ spec: {
+ inputs: {
+ test: {},
+ foo: {
+ default: 'bar'
+ }
+ }
+ }
+ }
+ end
+
+ it_behaves_like 'a valid header'
+ end
+
+ context 'when header contains minimal data' do
+ let(:header_hash) do
+ {
+ spec: {
+ inputs: nil
+ }
+ }
+ end
+
+ it_behaves_like 'a valid header' do
+ let(:expected_hash) { { spec: {} } }
+ end
+ end
+
+ context 'when header contains required inputs' do
+ let(:header_hash) do
+ {
+ spec: {
+ inputs: { foo: nil }
+ }
+ }
+ end
+
+ it_behaves_like 'a valid header' do
+ let(:expected_hash) do
+ {
+ spec: {
+ inputs: { foo: {} }
+ }
+ }
+ end
+ end
+ end
+
+ context 'when header contains unknown keywords' do
+ let(:header_hash) { { test: 123 } }
+ let(:expected_errors) { ['root config contains unknown keys: test'] }
+
+ it_behaves_like 'an invalid header'
+ end
+
+ context 'when header input entry has an unknown key' do
+ let(:header_hash) do
+ {
+ spec: {
+ inputs: {
+ foo: {
+ bad: 'value'
+ }
+ }
+ }
+ }
+ end
+
+ let(:expected_errors) { ['spec:inputs:foo config contains unknown keys: bad'] }
+
+ it_behaves_like 'an invalid header'
+ end
+
+ describe '#inputs_value' do
+ let(:header_hash) do
+ {
+ spec: {
+ inputs: {
+ foo: nil,
+ bar: {
+ default: 'baz'
+ }
+ }
+ }
+ }
+ end
+
+ it 'returns the inputs' do
+ expect(config.inputs_value).to eq({
+ foo: {},
+ bar: { default: 'baz' }
+ })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/header/spec_spec.rb b/spec/lib/gitlab/ci/config/header/spec_spec.rb
new file mode 100644
index 00000000000..4e8aceb5465
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/header/spec_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Header::Spec, feature_category: :pipeline_authoring do
+ let(:factory) { Gitlab::Config::Entry::Factory.new(described_class).value(spec_hash) }
+
+ subject(:config) { factory.create!.tap(&:compose!) }
+
+ context 'when spec contains default values for inputs' do
+ let(:spec_hash) do
+ {
+ inputs: {
+ foo: {
+ default: 'bar'
+ }
+ }
+ }
+ end
+
+ it 'passes validations' do
+ expect(config).to be_valid
+ expect(config.errors).to be_empty
+ end
+
+ it 'returns the value' do
+ expect(config.value).to eq(spec_hash)
+ end
+ end
+
+ context 'when spec contains unknown keywords' do
+ let(:spec_hash) { { test: 123 } }
+ let(:expected_errors) { ['spec config contains unknown keys: test'] }
+
+ it 'fails validations' do
+ expect(config).not_to be_valid
+ expect(config.errors).to eq(expected_errors)
+ end
+
+ it 'returns the value' do
+ expect(config.value).to eq(spec_hash)
+ end
+ end
+end
diff --git a/spec/models/concerns/uniquify_spec.rb b/spec/lib/gitlab/utils/uniquify_spec.rb
index 9b79e4d4154..df02fbe8c82 100644
--- a/spec/models/concerns/uniquify_spec.rb
+++ b/spec/lib/gitlab/utils/uniquify_spec.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
-RSpec.describe Uniquify do
- let(:uniquify) { described_class.new }
+RSpec.describe Gitlab::Utils::Uniquify, feature_category: :shared do
+ subject(:uniquify) { described_class.new }
describe "#string" do
it 'returns the given string if it does not exist' do
- result = uniquify.string('test_string') { |s| false }
+ result = uniquify.string('test_string') { |_s| false }
expect(result).to eq('test_string')
end
@@ -34,7 +34,7 @@ RSpec.describe Uniquify do
end
it 'allows passing in a base function that defines the location of the counter' do
- result = uniquify.string(-> (counter) { "test_#{counter}_string" }) do |s|
+ result = uniquify.string(->(counter) { "test_#{counter}_string" }) do |s|
s == 'test__string'
end
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 986e3ce9e52..7c970c2b548 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -107,6 +107,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
),
project_count: nil,
admin_url: "http://localhost/admin/runners/#{runner.id}",
+ edit_admin_url: "http://localhost/admin/runners/#{runner.id}/edit",
+ register_admin_url: runner.registration_available? ? "http://localhost/admin/runners/#{runner.id}/register" : nil,
user_permissions: {
'readRunner' => true,
'updateRunner' => true,
@@ -135,7 +137,7 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
runner_data = graphql_data_at(:runner)
expect(runner_data).not_to be_nil
- expect(runner_data).to match a_graphql_entity_for(runner, admin_url: nil)
+ expect(runner_data).to match a_graphql_entity_for(runner, admin_url: nil, edit_admin_url: nil)
expect(runner_data['tagList']).to match_array runner.tag_list
end
end
@@ -307,6 +309,24 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
it_behaves_like 'runner details fetch'
end
+ describe 'for registration type' do
+ context 'when registered with registration token' do
+ let(:runner) do
+ create(:ci_runner, registration_type: :registration_token)
+ end
+
+ it_behaves_like 'runner details fetch'
+ end
+
+ context 'when registered with authenticated user' do
+ let(:runner) do
+ create(:ci_runner, registration_type: :authenticated_user)
+ end
+
+ it_behaves_like 'runner details fetch'
+ end
+ end
+
describe 'for group runner request' do
let(:query) do
%(
@@ -568,14 +588,14 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
end
end
- context 'with request made by creator' do
+ context 'with request made by creator', :frozen_time do
let(:user) { creator }
context 'with runner created in UI' do
let(:registration_type) { :authenticated_user }
- context 'with runner created in last 3 hours' do
- let(:created_at) { (3.hours - 1.second).ago }
+ context 'with runner created in last hour' do
+ let(:created_at) { (Ci::Runner::REGISTRATION_AVAILABILITY_TIME - 1.second).ago }
context 'with no runner machine registed yet' do
it_behaves_like 'an ephemeral_authentication_token'
@@ -589,13 +609,13 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
end
context 'with runner created almost too long ago' do
- let(:created_at) { (3.hours - 1.second).ago }
+ let(:created_at) { (Ci::Runner::REGISTRATION_AVAILABILITY_TIME - 1.second).ago }
it_behaves_like 'an ephemeral_authentication_token'
end
context 'with runner created too long ago' do
- let(:created_at) { 3.hours.ago }
+ let(:created_at) { Ci::Runner::REGISTRATION_AVAILABILITY_TIME.ago }
it_behaves_like 'a protected ephemeral_authentication_token'
end
@@ -604,8 +624,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
context 'with runner registered from command line' do
let(:registration_type) { :registration_token }
- context 'with runner created in last 3 hours' do
- let(:created_at) { (3.hours - 1.second).ago }
+ context 'with runner created in last 1 hour' do
+ let(:created_at) { (Ci::Runner::REGISTRATION_AVAILABILITY_TIME - 1.second).ago }
it_behaves_like 'a protected ephemeral_authentication_token'
end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 7aa7d8e8abd..d6c310d25f2 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -7956,7 +7956,6 @@
- './spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb'
- './spec/models/concerns/transactions_spec.rb'
- './spec/models/concerns/triggerable_hooks_spec.rb'
-- './spec/models/concerns/uniquify_spec.rb'
- './spec/models/concerns/usage_statistics_spec.rb'
- './spec/models/concerns/vulnerability_finding_helpers_spec.rb'
- './spec/models/concerns/vulnerability_finding_signature_helpers_spec.rb'
diff --git a/spec/support/shared_examples/features/trial_email_validation_shared_example.rb b/spec/support/shared_examples/features/trial_email_validation_shared_example.rb
index 8304a91af86..5bab1e76e12 100644
--- a/spec/support/shared_examples/features/trial_email_validation_shared_example.rb
+++ b/spec/support/shared_examples/features/trial_email_validation_shared_example.rb
@@ -8,52 +8,31 @@ RSpec.shared_examples 'user email validation' do
'This email address does not look right, are you sure you typed it correctly?'
end
- context 'with trial_email_validation flag enabled' do
- it 'shows an error message until a correct email is entered' do
- visit path
- expect(page).to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
-
- fill_in 'new_user_email', with: 'foo@'
- fill_in 'new_user_first_name', with: ''
-
- expect(page).not_to have_content(email_hint_message)
- expect(page).to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
-
- fill_in 'new_user_email', with: 'foo@bar'
- fill_in 'new_user_first_name', with: ''
-
- expect(page).not_to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).to have_content(email_warning_message)
-
- fill_in 'new_user_email', with: 'foo@gitlab.com'
- fill_in 'new_user_first_name', with: ''
-
- expect(page).not_to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
- end
- end
+ it 'shows an error message until a correct email is entered' do
+ visit path
+ expect(page).to have_content(email_hint_message)
+ expect(page).not_to have_content(email_error_message)
+ expect(page).not_to have_content(email_warning_message)
+
+ fill_in 'new_user_email', with: 'foo@'
+ fill_in 'new_user_first_name', with: ''
+
+ expect(page).not_to have_content(email_hint_message)
+ expect(page).to have_content(email_error_message)
+ expect(page).not_to have_content(email_warning_message)
- context 'when trial_email_validation flag disabled' do
- before do
- stub_feature_flags trial_email_validation: false
- end
+ fill_in 'new_user_email', with: 'foo@bar'
+ fill_in 'new_user_first_name', with: ''
- it 'does not show an error message' do
- visit path
- expect(page).to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
+ expect(page).not_to have_content(email_hint_message)
+ expect(page).not_to have_content(email_error_message)
+ expect(page).to have_content(email_warning_message)
- fill_in 'new_user_email', with: 'foo@'
+ fill_in 'new_user_email', with: 'foo@gitlab.com'
+ fill_in 'new_user_first_name', with: ''
- expect(page).to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
- end
+ expect(page).not_to have_content(email_hint_message)
+ expect(page).not_to have_content(email_error_message)
+ expect(page).not_to have_content(email_warning_message)
end
end