diff --git a/.rubocop_todo/layout/space_inside_parens.yml b/.rubocop_todo/layout/space_inside_parens.yml
index cf33828b20f..e6238992619 100644
--- a/.rubocop_todo/layout/space_inside_parens.yml
+++ b/.rubocop_todo/layout/space_inside_parens.yml
@@ -7,20 +7,6 @@ Layout/SpaceInsideParens:
Exclude:
- 'config/initializers/wikicloth_redos_patch.rb'
- 'db/post_migrate/20210722042939_update_issuable_slas_where_issue_closed.rb'
- - 'ee/app/graphql/resolvers/external_issue_resolver.rb'
- - 'ee/app/helpers/billing_plans_helper.rb'
- - 'ee/app/helpers/ee/boards_helper.rb'
- - 'ee/app/models/ee/lfs_object.rb'
- - 'ee/app/models/ee/merge_request_diff.rb'
- - 'ee/app/models/ee/pages_deployment.rb'
- - 'ee/app/models/ee/upload.rb'
- - 'ee/app/models/requirements_management/requirement.rb'
- - 'ee/app/models/resource_iteration_event.rb'
- - 'ee/app/services/compliance_management/frameworks/create_service.rb'
- - 'ee/app/services/compliance_management/frameworks/destroy_service.rb'
- - 'ee/app/services/compliance_management/frameworks/update_service.rb'
- - 'ee/app/services/elastic/cluster_reindexing_service.rb'
- - 'ee/app/services/sitemap/create_service.rb'
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
- 'ee/lib/gitlab/auth/smartcard/session.rb'
- 'ee/spec/finders/ee/alert_management/http_integrations_finder_spec.rb'
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index b8402259306..c85078ffaa1 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-15.6.0-rc1
+15.6.0-rc2
diff --git a/Gemfile b/Gemfile
index 0f74cfc0f61..6cbb1a4351f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -362,7 +362,7 @@ gem 'prometheus-client-mmap', '~> 0.16', require: 'prometheus/client'
gem 'warning', '~> 1.3.0'
group :development do
- gem 'lefthook', '~> 1.1.3', require: false
+ gem 'lefthook', '~> 1.1.4', require: false
gem 'rubocop'
gem 'solargraph', '~> 0.47.2', require: false
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 4ef340ff894..09bbd1fa875 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -302,7 +302,7 @@
{"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"},
{"name":"kubeclient","version":"4.9.3","platform":"ruby","checksum":"d5d38e719fbac44f396851aa57cd1b9f4f7dab4410ab680ccd21c9b741230046"},
{"name":"launchy","version":"2.5.0","platform":"ruby","checksum":"954243c4255920982ce682f89a42e76372dba94770bf09c23a523e204bdebef5"},
-{"name":"lefthook","version":"1.1.3","platform":"ruby","checksum":"3f8337b2176f49e6d4ab8f0f4494c8d1be0548d79bca898fbf2184d717092b75"},
+{"name":"lefthook","version":"1.1.4","platform":"ruby","checksum":"251fbc6681a7d0f05e594b5091206998fd21060285f9752ac40b92441d5eb93c"},
{"name":"letter_opener","version":"1.7.0","platform":"ruby","checksum":"095bc0d58e006e5b43ea7d219e64ecf2de8d1f7d9dafc432040a845cf59b4725"},
{"name":"letter_opener_web","version":"2.0.0","platform":"ruby","checksum":"33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f"},
{"name":"libyajl2","version":"1.2.0","platform":"ruby","checksum":"1117cd1e48db013b626e36269bbf1cef210538ca6d2e62d3fa3db9ded005b258"},
diff --git a/Gemfile.lock b/Gemfile.lock
index cdd22499a82..6d8244cadcb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -802,7 +802,7 @@ GEM
rest-client (~> 2.0)
launchy (2.5.0)
addressable (~> 2.7)
- lefthook (1.1.3)
+ lefthook (1.1.4)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (2.0.0)
@@ -1680,7 +1680,7 @@ DEPENDENCIES
knapsack (~> 1.21.1)
kramdown (~> 2.3.1)
kubeclient (~> 4.9.3)
- lefthook (~> 1.1.3)
+ lefthook (~> 1.1.4)
letter_opener_web (~> 2.0.0)
license_finder (~> 7.0)
licensee (~> 9.15)
diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
new file mode 100644
index 00000000000..29f6aecca03
--- /dev/null
+++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+ {{ $options.i18n.rssButtonText }}
+
+
+ {{ $options.i18n.calendarButtonText }}
+
+
+
+
+
+
+
+
diff --git a/app/assets/javascripts/issues/dashboard/index.js b/app/assets/javascripts/issues/dashboard/index.js
new file mode 100644
index 00000000000..a1ae3b93f7d
--- /dev/null
+++ b/app/assets/javascripts/issues/dashboard/index.js
@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import IssuesDashboardApp from './components/issues_dashboard_app.vue';
+
+export function mountIssuesDashboardApp() {
+ const el = document.querySelector('.js-issues-dashboard');
+
+ if (!el) {
+ return null;
+ }
+
+ const { calendarPath, emptyStateSvgPath, isSignedIn, rssPath } = el.dataset;
+
+ return new Vue({
+ el,
+ name: 'IssuesDashboardRoot',
+ provide: {
+ calendarPath,
+ emptyStateSvgPath,
+ isSignedIn: parseBoolean(isSignedIn),
+ rssPath,
+ },
+ render: (createComponent) => createComponent(IssuesDashboardApp),
+ });
+}
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue
index 80bca536b7c..23d8e97dd79 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/image_list_row.vue
@@ -3,7 +3,6 @@ import { GlTooltipDirective, GlIcon, GlSprintf, GlSkeletonLoader, GlButton } fro
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { n__ } from '~/locale';
import Tracking from '~/tracking';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import { joinPaths } from '~/lib/utils/url_utility';
@@ -38,7 +37,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [Tracking.mixin(), glFeatureFlagsMixin()],
+ mixins: [Tracking.mixin()],
inject: ['config'],
props: {
item: {
@@ -91,17 +90,14 @@ export default {
);
},
imageName() {
- if (this.glFeatures.containerRegistryShowShortenedPath) {
- if (this.showFullPath) {
- return this.item.path;
- }
- const projectPath = this.item?.project?.path?.toLowerCase() ?? '';
- if (this.item.name) {
- return joinPaths(projectPath, this.item.name);
- }
- return projectPath;
+ if (this.showFullPath) {
+ return this.item.path;
}
- return this.item.path;
+ const projectPath = this.item?.project?.path?.toLowerCase() ?? '';
+ if (this.item.name) {
+ return joinPaths(projectPath, this.item.name);
+ }
+ return projectPath;
},
routerLinkEvent() {
return this.deleting ? '' : 'click';
@@ -136,7 +132,7 @@ export default {
>
0
+ fetch_pages_content!(pages) if load_content
+
+ pages
end
def sidebar_entries(limit: Gitlab::WikiPages::MAX_SIDEBAR_PAGES, **options)
@@ -229,7 +249,27 @@ class Wiki
# Returns an initialized WikiPage instance or nil
def find_page(title, version = nil, load_content: true)
create_wiki_repository unless repository_exists?
- find_page_with_repository_rpcs(title, version, load_content: load_content)
+
+ version = version.presence || default_branch
+ path = find_matched_file(title, version)
+ return if path.blank?
+
+ blob_options = load_content ? {} : { limit: 0 }
+ blob = repository.blob_at(version, path, **blob_options)
+ commit = repository.commit(blob.commit_id)
+ format = find_page_format(path)
+
+ page = Gitlab::Git::WikiPage.new(
+ url_path: sluggified_title(strip_extension(path)),
+ title: canonicalize_filename(path),
+ format: format,
+ path: sluggified_title(path),
+ raw_data: blob.data,
+ name: canonicalize_filename(path),
+ historical: version == default_branch ? false : check_page_historical(path, commit),
+ version: Gitlab::Git::WikiPageVersion.new(commit, format)
+ )
+ WikiPage.new(self, page)
end
def find_sidebar(version = nil)
@@ -315,12 +355,6 @@ class Wiki
[title, title_array.join("/")]
end
- # TODO: This method is redundant. Should be replaced by create_wiki_repository
- def ensure_repository
- create_wiki_repository
- raise CouldNotCreateWikiError unless repository_exists?
- end
-
def hook_attrs
{
web_url: web_url,
@@ -472,29 +506,6 @@ class Wiki
repository.last_commit_for_path(default_branch, path)&.id != commit&.id
end
- def find_page_with_repository_rpcs(title, version, load_content: true)
- version = version.presence || default_branch
- path = find_matched_file(title, version)
- return if path.blank?
-
- blob_options = load_content ? {} : { limit: 0 }
- blob = repository.blob_at(version, path, **blob_options)
- commit = repository.commit(blob.commit_id)
- format = find_page_format(path)
-
- page = Gitlab::Git::WikiPage.new(
- url_path: sluggified_title(strip_extension(path)),
- title: canonicalize_filename(path),
- format: format,
- path: sluggified_title(path),
- raw_data: blob.data,
- name: canonicalize_filename(path),
- historical: version == default_branch ? false : check_page_historical(path, commit),
- version: Gitlab::Git::WikiPageVersion.new(commit, format)
- )
- WikiPage.new(self, page)
- end
-
def file_extension_regexp
# We could not use ALLOWED_EXTENSIONS_REGEX constant or similar regexp with
# Regexp.union. The result combination complicated modifiers:
@@ -516,29 +527,6 @@ class Wiki
repository.search_files_by_regexp(path_regexp, default_branch, limit: limit, offset: offset)
end
- def list_pages_with_repository_rpcs(direction:, load_content:, limit:, offset:)
- paths = list_page_paths(limit: limit, offset: offset)
- return [] if paths.empty?
-
- pages = paths.map do |path|
- page = Gitlab::Git::WikiPage.new(
- url_path: sluggified_title(strip_extension(path)),
- title: canonicalize_filename(path),
- format: find_page_format(path),
- path: sluggified_title(path),
- raw_data: '',
- name: canonicalize_filename(path),
- historical: false
- )
- WikiPage.new(self, page)
- end
- sort_pages!(pages, direction)
- pages = pages.take(limit) if limit > 0
- fetch_pages_content!(pages) if load_content
-
- pages
- end
-
# After migrating to normal repository RPCs, it's very expensive to sort the
# pages by created_at. We have to either ListLastCommitsForTree RPC call or
# N+1 LastCommitForPath. Either are efficient for a large repository.
diff --git a/app/services/work_items/widgets/hierarchy_service/base_service.rb b/app/services/work_items/widgets/hierarchy_service/base_service.rb
index 8418bc4c5a7..236762d6937 100644
--- a/app/services/work_items/widgets/hierarchy_service/base_service.rb
+++ b/app/services/work_items/widgets/hierarchy_service/base_service.rb
@@ -7,7 +7,6 @@ module WorkItems
private
def handle_hierarchy_changes(params)
- return feature_flag_error unless feature_flag_enabled?
return incompatible_args_error if incompatible_args?(params)
if params.key?(:parent)
@@ -48,18 +47,10 @@ module WorkItems
.execute
end
- def feature_flag_enabled?
- Feature.enabled?(:work_items_hierarchy, work_item&.project)
- end
-
def incompatible_args?(params)
params[:children] && params[:parent]
end
- def feature_flag_error
- error(_('`work_items_hierarchy` feature flag disabled for this project'))
- end
-
def incompatible_args_error
error(_('A Work Item can be a parent or a child, but not both.'))
end
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 95e772f324b..79f6bfc866a 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,6 +1,7 @@
- @hide_top_links = true
- page_title _("Issues")
- @breadcrumb_link = issues_dashboard_path(assignee_username: current_user.username)
+- add_page_specific_style 'page_bundles/dashboard'
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
@@ -13,14 +14,20 @@
.page-title-controls
= render 'shared/new_project_item_select', path: 'issues/new', label: _("issue"), with_feature_enabled: 'issues', type: :issues
-.top-area
- = render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
- .nav-controls
- = render 'shared/issuable/feed_buttons'
-
-= render 'shared/issuable/search_bar', type: :issues
-
-- if current_user && @no_filters_set
- = render 'shared/dashboard/no_filter_selected'
+- if ::Feature.enabled?(:vue_issues_dashboard)
+ .js-issues-dashboard{ data: { calendar_path: url_for(safe_params.merge(calendar_url_options)),
+ empty_state_svg_path: image_path('illustrations/issue-dashboard_results-without-filter.svg'),
+ is_signed_in: current_user.present?.to_s,
+ rss_path: url_for(safe_params.merge(rss_url_options)) } }
- else
- = render 'shared/issues'
+ .top-area
+ = render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
+ .nav-controls
+ = render 'shared/issuable/feed_buttons'
+
+ = render 'shared/issuable/search_bar', type: :issues
+
+ - if current_user && @no_filters_set
+ = render 'shared/dashboard/no_filter_selected'
+ - else
+ = render 'shared/issues'
diff --git a/app/views/projects/issues/_work_item_links.html.haml b/app/views/projects/issues/_work_item_links.html.haml
index c0de711136a..a2cd967484c 100644
--- a/app/views/projects/issues/_work_item_links.html.haml
+++ b/app/views/projects/issues/_work_item_links.html.haml
@@ -1,2 +1 @@
-- if Feature.enabled?(:work_items_hierarchy, @project)
- .js-work-item-links-root{ data: { issuable_id: @issue.id, iid: @issue.iid, project_namespace: @project.namespace.path, project_path: @project.full_path, wi: work_items_index_data(@project) } }
+.js-work-item-links-root{ data: { issuable_id: @issue.id, iid: @issue.iid, project_namespace: @project.namespace.path, project_path: @project.full_path, wi: work_items_index_data(@project) } }
diff --git a/config/application.rb b/config/application.rb
index ce62f5833b5..85b1a676409 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -268,6 +268,7 @@ module Gitlab
config.assets.precompile << "page_bundles/cluster_agents.css"
config.assets.precompile << "page_bundles/clusters.css"
config.assets.precompile << "page_bundles/cycle_analytics.css"
+ config.assets.precompile << "page_bundles/dashboard.css"
config.assets.precompile << "page_bundles/dashboard_projects.css"
config.assets.precompile << "page_bundles/design_management.css"
config.assets.precompile << "page_bundles/dev_ops_reports.css"
diff --git a/config/feature_flags/development/container_registry_show_shortened_path.yml b/config/feature_flags/development/vue_issues_dashboard.yml
similarity index 61%
rename from config/feature_flags/development/container_registry_show_shortened_path.yml
rename to config/feature_flags/development/vue_issues_dashboard.yml
index 33781386e8a..133343b3a3c 100644
--- a/config/feature_flags/development/container_registry_show_shortened_path.yml
+++ b/config/feature_flags/development/vue_issues_dashboard.yml
@@ -1,8 +1,8 @@
---
-name: container_registry_show_shortened_path
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91548
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366808
-milestone: '15.2'
+name: vue_issues_dashboard
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102197
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379025
+milestone: '15.6'
type: development
-group: group::package
+group: group::project management
default_enabled: false
diff --git a/config/feature_flags/development/work_items_hierarchy.yml b/config/feature_flags/development/work_items_hierarchy.yml
deleted file mode 100644
index ce27c58df96..00000000000
--- a/config/feature_flags/development/work_items_hierarchy.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: work_items_hierarchy
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88504
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363447
-milestone: '15.1'
-type: development
-group: group::product planning
-default_enabled: true
diff --git a/db/migrate/20221028015347_add_commit_committer_name_check_to_push_rules.rb b/db/migrate/20221028015347_add_commit_committer_name_check_to_push_rules.rb
new file mode 100644
index 00000000000..e9a0887f353
--- /dev/null
+++ b/db/migrate/20221028015347_add_commit_committer_name_check_to_push_rules.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddCommitCommitterNameCheckToPushRules < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :push_rules, :commit_committer_name_check, :boolean, default: false, null: false
+ end
+end
diff --git a/db/migrate/20221028152422_add_finding_data_column_to_security_findings.rb b/db/migrate/20221028152422_add_finding_data_column_to_security_findings.rb
new file mode 100644
index 00000000000..a5d3929579b
--- /dev/null
+++ b/db/migrate/20221028152422_add_finding_data_column_to_security_findings.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddFindingDataColumnToSecurityFindings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def up
+ add_column :security_findings, :finding_data, :jsonb, default: {}, null: false
+ end
+
+ def down
+ remove_column :security_findings, :finding_data
+ end
+end
diff --git a/db/post_migrate/20220927171740_prepare_for_vulnerability_occurrences_uuid_type_transition.rb b/db/post_migrate/20220927171740_prepare_for_vulnerability_occurrences_uuid_type_transition.rb
new file mode 100644
index 00000000000..e6f3384514d
--- /dev/null
+++ b/db/post_migrate/20220927171740_prepare_for_vulnerability_occurrences_uuid_type_transition.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class PrepareForVulnerabilityOccurrencesUuidTypeTransition < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ TABLE = :vulnerability_occurrences
+ MAPPINGS = {
+ uuid: {
+ from_type: :string,
+ to_type: :uuid,
+ default_value: '00000000-0000-0000-0000-000000000000'
+ }
+ }
+
+ def up
+ create_temporary_columns_and_triggers(TABLE, MAPPINGS)
+ end
+
+ def down
+ columns = MAPPINGS.keys
+ temporary_columns = columns.map { |column| convert_to_type_column(column, :string, :uuid) }
+ trigger_name = rename_trigger_name(TABLE, columns, temporary_columns)
+ remove_rename_triggers(TABLE, trigger_name)
+ temporary_columns.each { |column| remove_column(TABLE, column) }
+ end
+end
diff --git a/db/post_migrate/20221025115006_check_vulnerabilities_state_transition_from_state_not_equal_to_state.rb b/db/post_migrate/20221025115006_check_vulnerabilities_state_transition_from_state_not_equal_to_state.rb
new file mode 100644
index 00000000000..2ab7f8a9c04
--- /dev/null
+++ b/db/post_migrate/20221025115006_check_vulnerabilities_state_transition_from_state_not_equal_to_state.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class CheckVulnerabilitiesStateTransitionFromStateNotEqualToState < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_check_constraint(:vulnerability_state_transitions, '(from_state != to_state)', constraint_name)
+ end
+
+ def down
+ remove_check_constraint(:vulnerability_state_transitions, constraint_name)
+ end
+
+ private
+
+ def constraint_name
+ check_constraint_name('vulnerability_state_transitions', 'fully_qualified_table_name', 'state_not_equal')
+ end
+end
diff --git a/db/schema_migrations/20220927171740 b/db/schema_migrations/20220927171740
new file mode 100644
index 00000000000..e775b211200
--- /dev/null
+++ b/db/schema_migrations/20220927171740
@@ -0,0 +1 @@
+5ba49d525d6238975f990c94972ee4f3587a2446a4873e6e8a7f4791cf015b7e
\ No newline at end of file
diff --git a/db/schema_migrations/20221025115006 b/db/schema_migrations/20221025115006
new file mode 100644
index 00000000000..d831d6aeec9
--- /dev/null
+++ b/db/schema_migrations/20221025115006
@@ -0,0 +1 @@
+1529e1b436b65ff7b787f43fc5b8de7515aebe427719d2e4e62e9a7f923e877b
\ No newline at end of file
diff --git a/db/schema_migrations/20221028015347 b/db/schema_migrations/20221028015347
new file mode 100644
index 00000000000..ab633b763df
--- /dev/null
+++ b/db/schema_migrations/20221028015347
@@ -0,0 +1 @@
+7b86ae0739c4c381b050539261c67dbf3d4716edf0f0bde9b281cbdc5143a4d2
\ No newline at end of file
diff --git a/db/schema_migrations/20221028152422 b/db/schema_migrations/20221028152422
new file mode 100644
index 00000000000..bfe9807b1c6
--- /dev/null
+++ b/db/schema_migrations/20221028152422
@@ -0,0 +1 @@
+c9322bdc7e862bd20ec548fbcd3ec6a9ef4da6abc0a688d503e1792acc262472
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 0e775c38b25..4179ff49548 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -225,6 +225,15 @@ RETURN NULL;
END
$$;
+CREATE FUNCTION trigger_1a857e8db6cd() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."uuid_convert_string_to_uuid" := NEW."uuid";
+ RETURN NEW;
+END;
+$$;
+
CREATE FUNCTION unset_has_issues_on_vulnerability_reads() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -434,6 +443,7 @@ CREATE TABLE security_findings (
uuid uuid,
overridden_uuid uuid,
partition_number integer DEFAULT 1 NOT NULL,
+ finding_data jsonb DEFAULT '{}'::jsonb NOT NULL,
CONSTRAINT check_6c2851a8c9 CHECK ((uuid IS NOT NULL)),
CONSTRAINT check_b9508c6df8 CHECK ((char_length(project_fingerprint) <= 40))
)
@@ -20652,7 +20662,8 @@ CREATE TABLE push_rules (
commit_committer_check boolean,
regexp_uses_re2 boolean DEFAULT true,
commit_message_negative_regex character varying,
- reject_non_dco_commits boolean
+ reject_non_dco_commits boolean,
+ commit_committer_name_check boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE push_rules_id_seq
@@ -22945,6 +22956,7 @@ CREATE TABLE vulnerability_occurrences (
cve text,
location jsonb,
detection_method smallint DEFAULT 0 NOT NULL,
+ uuid_convert_string_to_uuid uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL,
CONSTRAINT check_4a3a60f2ba CHECK ((char_length(solution) <= 7000)),
CONSTRAINT check_ade261da6b CHECK ((char_length(description) <= 15000)),
CONSTRAINT check_df6dd20219 CHECK ((char_length(message) <= 3000)),
@@ -23041,7 +23053,8 @@ CREATE TABLE vulnerability_state_transitions (
author_id bigint,
comment text,
dismissal_reason smallint,
- CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255))
+ CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255)),
+ CONSTRAINT state_not_equal CHECK ((from_state <> to_state))
);
CREATE SEQUENCE vulnerability_state_transitions_id_seq
@@ -32516,6 +32529,8 @@ CREATE TRIGGER nullify_merge_request_metrics_build_data_on_update BEFORE UPDATE
CREATE TRIGGER projects_loose_fk_trigger AFTER DELETE ON projects REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
+CREATE TRIGGER trigger_1a857e8db6cd BEFORE INSERT OR UPDATE ON vulnerability_occurrences FOR EACH ROW EXECUTE FUNCTION trigger_1a857e8db6cd();
+
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
diff --git a/doc/.vale/gitlab/Wordy.yml b/doc/.vale/gitlab/Wordy.yml
index 8fe8a12f161..7432256efc9 100644
--- a/doc/.vale/gitlab/Wordy.yml
+++ b/doc/.vale/gitlab/Wordy.yml
@@ -14,3 +14,4 @@ swap:
note that: "Be concise: rewrite the sentence to not use"
please: "Remove this word from the sentence: "
respectively: "Rewrite the sentence to be more precise, instead of using "
+ and so on: "Be more specific, rewrite the sentence to not use"
diff --git a/doc/api/runners.md b/doc/api/runners.md
index cb199f06159..b2377fda7d6 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -46,11 +46,11 @@ GET /runners?tag_list=tag1,tag2
| Attribute | Type | Required | Description |
|------------|--------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
-| `tag_list` | string array | no | List of the runner's tags |
+| `tag_list` | string array | no | A list of runner tags |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/runners"
@@ -111,11 +111,11 @@ GET /runners/all?tag_list=tag1,tag2
| Attribute | Type | Required | Description |
|------------|--------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online` and `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to return, one of: `specific`, `shared`, `active`, `paused`, `online` and `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
-| `tag_list` | string array | no | List of the runner's tags |
+| `tag_list` | string array | no | A list of runner tags |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/runners/all"
@@ -260,17 +260,17 @@ Update details of a runner.
PUT /runners/:id
```
-| Attribute | Type | Required | Description |
-|-------------------|---------|----------|--------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID of a runner |
-| `description` | string | no | The description of a runner |
-| `active` | boolean | no | Deprecated: Use `:paused` instead. Flag indicating whether the runner is allowed to receive jobs |
-| `paused` | boolean | no | Flag indicating whether the runner should ignore new jobs |
-| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
-| `run_untagged` | boolean | no | Flag indicating the runner can execute untagged jobs |
-| `locked` | boolean | no | Flag indicating the runner is locked |
-| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
-| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
+| Attribute | Type | Required | Description |
+|-------------------|---------|----------|-------------------------------------------------------------------------------------------------|
+| `id` | integer | yes | The ID of a runner |
+| `description` | string | no | The description of the runner |
+| `active` | boolean | no | Deprecated: Use `paused` instead. Flag indicating whether the runner is allowed to receive jobs |
+| `paused` | boolean | no | Specifies whether the runner should ignore new jobs |
+| `tag_list` | array | no | The list of tags for the runner |
+| `run_untagged` | boolean | no | Specifies whether the runner can execute untagged jobs |
+| `locked` | boolean | no | Specifies whether the runner is locked |
+| `access_level` | string | no | The access level of the runner; `not_protected` or `ref_protected` |
+| `maximum_timeout` | integer | no | Maximum timeout that limits the amount of time (in seconds) that runners can run jobs |
```shell
curl --request PUT --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/runners/6" \
@@ -370,7 +370,7 @@ GET /runners/:id/jobs
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a runner |
| `status` | string | no | Status of the job; one of: `running`, `success`, `failed`, `canceled` |
-| `order_by`| string | no | Order jobs by `id`. |
+| `order_by`| string | no | Order jobs by `id` |
| `sort` | string | no | Sort jobs in `asc` or `desc` order (default: `desc`). Specify `order_by` as well, including for `id`. |
```shell
@@ -463,11 +463,11 @@ GET /projects/:id/runners?tag_list=tag1,tag2
| Attribute | Type | Required | Description |
|------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
-| `tag_list` | string array | no | List of the runner's tags |
+| `tag_list` | string array | no | A list of runner tags |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/9/runners"
@@ -580,10 +580,10 @@ GET /groups/:id/runners?tag_list=tag1,tag2
| Attribute | Type | Required | Description |
|------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer | yes | The ID of the group owned by the authenticated user |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type`. The `project_type` value is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351466) and will be removed in GitLab 15.0 |
-| `status` | string | no | The status of runners to show, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
+| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type`. The `project_type` value is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351466) and will be removed in GitLab 15.0 |
+| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
-| `tag_list` | string array | no | List of the runner's tags |
+| `tag_list` | string array | no | A list of runner tags |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/groups/9/runners"
@@ -774,7 +774,7 @@ curl --request POST --header "PRIVATE-TOKEN: " \
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
-Resets the runner registration token for a project.
+Reset the runner registration token for a project.
```plaintext
POST /projects/:id/runners/reset_registration_token
@@ -789,7 +789,7 @@ curl --request POST --header "PRIVATE-TOKEN: " \
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 14.3.
-Resets the runner registration token for a group.
+Reset the runner registration token for a group.
```plaintext
POST /groups/:id/runners/reset_registration_token
@@ -802,7 +802,7 @@ curl --request POST --header "PRIVATE-TOKEN: " \
## Reset runner's authentication token by using the runner ID
-Resets the runner's authentication token by using its runner ID.
+Reset the runner's authentication token by using its runner ID.
```plaintext
POST /runners/:id/reset_authentication_token
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 3f63fb6a2cd..362874d5b29 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -356,6 +356,8 @@ Advanced vulnerability tracking is available in a subset of the [supported langu
Support for more languages and analyzers is tracked in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/5144).
+For more information, see the confidential project `https://gitlab.com/gitlab-org/security-products/post-analyzers/tracking-calculator`. The content of this project is available only to GitLab team members.
+
### Using CI/CD variables to pass credentials for private repositories
Some analyzers require downloading the project's dependencies to
diff --git a/doc/user/project/repository/reducing_the_repo_size_using_git.md b/doc/user/project/repository/reducing_the_repo_size_using_git.md
index c85dd4555ca..9c977e4da40 100644
--- a/doc/user/project/repository/reducing_the_repo_size_using_git.md
+++ b/doc/user/project/repository/reducing_the_repo_size_using_git.md
@@ -72,6 +72,12 @@ To purge files from a GitLab repository:
cd project.git
```
+1. Because cloning from a bundle file sets the `origin` remote to the local bundle file, change it to the URL of your repository:
+
+ ```shell
+ git remote set-url origin https://gitlab.example.com//.git
+ ```
+
1. Using either `git filter-repo` or `git-sizer`, analyze your repository
and review the results to determine which items you want to purge:
@@ -84,38 +90,39 @@ To purge files from a GitLab repository:
git-sizer
```
-1. Proceed to purging any files from the history of your repository. Because we are
- trying to remove internal refs, we rely on the `commit-map` produced by each run to tell us
- which internal refs to remove.
+1. Purge the history of your repository using relevant `git filter-repo` options.
+ Two common options are:
- NOTE:
- `git filter-repo` creates a new `commit-map` file every run, and overwrites the `commit-map` from
- the previous run. You need this file from **every** run. Do the next step every time you run
- `git filter-repo`.
+ - `--path` and `--invert-paths` to purge specific files:
- To purge specific files, the `--path` and `--invert-paths` options can be combined:
+ ```shell
+ git filter-repo --path path/to/file.ext --invert-paths
+ ```
- ```shell
- git filter-repo --path path/to/file.ext --invert-paths
- ```
+ - `--strip-blobs-bigger-than` to purge all files larger than for example 10M:
- To generally purge all files larger than 10M, the `--strip-blobs-bigger-than` option can be used:
-
- ```shell
- git filter-repo --strip-blobs-bigger-than 10M
- ```
+ ```shell
+ git filter-repo --strip-blobs-bigger-than 10M
+ ```
See the
[`git filter-repo` documentation](https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html#EXAMPLES)
for more examples and the complete documentation.
-1. Because cloning from a bundle file sets the `origin` remote to the local bundle file, delete this `origin` remote, and set it to the URL to your repository:
+1. Because you are trying to remove internal refs,
+ you'll later rely on `commit-map` files produced by each run
+ to tell you which internal refs to remove.
+ Every `git filter-repo` run creates a new `commit-map`,
+ and overwrites the `commit-map` from the previous run.
+ You can use the following command to back up each `commit-map` file:
```shell
- git remote remove origin
- git remote add origin https://gitlab.example.com//.git
+ cp .git/filter-repo/commit-map ./_filter_repo_commit_map_$(date +%s)
```
+ Repeat this step and all following steps (including the [repository cleanup](#repository-cleanup) step)
+ every time you run any `git filter-repo` command.
+
1. Force push your changes to overwrite all branches on GitLab:
```shell
diff --git a/doc/user/tasks.md b/doc/user/tasks.md
index 10a86724bf1..bc83e09a052 100644
--- a/doc/user/tasks.md
+++ b/doc/user/tasks.md
@@ -18,7 +18,7 @@ For the latest updates, check the [Tasks Roadmap](https://gitlab.com/groups/gitl
FLAG:
On self-managed GitLab, by default this feature is available. To hide the feature,
-ask an administrator to [disable the feature flags](../administration/feature_flags.md) named `work_items` and `work_items_hierarchy`.
+ask an administrator to [disable the feature flags](../administration/feature_flags.md) named `work_items`.
On GitLab.com, this feature is available.
Use tasks to track steps needed for the [issue](project/issues/index.md) to be closed.
diff --git a/lib/api/api.rb b/lib/api/api.rb
index fb71b8b048d..c21437cf948 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -174,6 +174,7 @@ module API
mount ::API::Appearance
mount ::API::BulkImports
mount ::API::Ci::Runner
+ mount ::API::Ci::Runners
mount ::API::Clusters::Agents
mount ::API::Clusters::AgentTokens
mount ::API::DeployKeys
@@ -218,7 +219,6 @@ module API
mount ::API::Ci::PipelineSchedules
mount ::API::Ci::Pipelines
mount ::API::Ci::ResourceGroups
- mount ::API::Ci::Runners
mount ::API::Ci::SecureFiles
mount ::API::Ci::Triggers
mount ::API::Ci::Variables
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 4b578f8b7e5..5250a9bca2c 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -10,296 +10,23 @@ module API
feature_category :runner
urgency :low
- resource :runners do
- desc 'Get runners available for user' do
- success Entities::Ci::Runner
- end
- params do
- optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
- desc: 'The scope of specific runners to show'
- optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
- desc: 'The type of the runners to show'
- optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
- optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
- desc: 'The status of the runners to show'
- optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
- use :pagination
- end
- get do
- runners = current_user.ci_owned_runners
- runners = filter_runners(runners, params[:scope], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
- runners = apply_filter(runners, params)
-
- present paginate(runners), with: Entities::Ci::Runner
- end
-
- desc 'Get all runners - shared and specific' do
- success Entities::Ci::Runner
- end
- params do
- optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
- desc: 'The scope of specific runners to show'
- optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
- desc: 'The type of the runners to show'
- optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
- optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
- desc: 'The status of the runners to show'
- optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
- use :pagination
- end
- get 'all' do
- authenticated_as_admin!
-
- runners = ::Ci::Runner.all
- runners = filter_runners(runners, params[:scope])
- runners = apply_filter(runners, params)
-
- present paginate(runners), with: Entities::Ci::Runner
- end
-
- desc "Get runner's details" do
- success Entities::Ci::RunnerDetails
- end
- params do
- requires :id, type: Integer, desc: 'The ID of the runner'
- end
- get ':id' do
- runner = get_runner(params[:id])
- authenticate_show_runner!(runner)
-
- present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
- end
-
- desc "Update runner's details" do
- success Entities::Ci::RunnerDetails
- end
- params do
- requires :id, type: Integer, desc: 'The ID of the runner'
- optional :description, type: String, desc: 'The description of the runner'
- optional :active, type: Boolean, desc: 'Deprecated: Use `:paused` instead. Flag indicating whether the runner is allowed to receive jobs'
- optional :paused, type: Boolean, desc: 'Flag indicating whether the runner should ignore new jobs'
- optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of tags for a runner'
- optional :run_untagged, type: Boolean, desc: 'Flag indicating whether the runner can execute untagged jobs'
- optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
- optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
- desc: 'The access_level of the runner'
- optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
- at_least_one_of :description, :active, :paused, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
- mutually_exclusive :active, :paused
- end
- put ':id' do
- runner = get_runner(params.delete(:id))
- authenticate_update_runner!(runner)
- params[:active] = !params.delete(:paused) if params.include?(:paused)
- update_service = ::Ci::Runners::UpdateRunnerService.new(runner)
-
- if update_service.execute(declared_params(include_missing: false)).success?
- present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
- else
- render_validation_error!(runner)
- end
- end
-
- desc 'Remove a runner' do
- success Entities::Ci::Runner
- end
- params do
- requires :id, type: Integer, desc: 'The ID of the runner'
- end
- delete ':id' do
- runner = get_runner(params[:id])
-
- authenticate_delete_runner!(runner)
-
- destroy_conditionally!(runner) { ::Ci::Runners::UnregisterRunnerService.new(runner, current_user).execute }
- end
-
- desc 'List jobs running on a runner' do
- success Entities::Ci::JobBasicWithProject
- end
- params do
- requires :id, type: Integer, desc: 'The ID of the runner'
- optional :status, type: String, desc: 'Status of the job', values: ::Ci::Build::AVAILABLE_STATUSES
- optional :order_by, type: String, desc: 'Order by `id` or not', values: ::Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
- optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by asc (ascending) or desc (descending)'
- use :pagination
- end
- get ':id/jobs' do
- runner = get_runner(params[:id])
- authenticate_list_runners_jobs!(runner)
-
- jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
- jobs = jobs.preload( # rubocop: disable CodeReuse/ActiveRecord
- [
- :user,
- { pipeline: { project: [:route, { namespace: :route }] } },
- { project: [:route, { namespace: :route }] }
- ]
- )
- jobs = paginate(jobs)
- jobs.each(&:commit) # batch loads all commits in the page
-
- present jobs, with: Entities::Ci::JobBasicWithProject
- end
-
- desc 'Reset runner authentication token' do
- success Entities::Ci::ResetTokenResult
- end
- params do
- requires :id, type: Integer, desc: 'The ID of the runner'
- end
- post ':id/reset_authentication_token' do
- runner = get_runner(params[:id])
- authenticate_update_runner!(runner)
-
- runner.reset_token!
- present runner.token_with_expiration, with: Entities::Ci::ResetTokenResult
- end
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before { authorize_admin_project }
-
- desc 'Get runners available for project' do
- success Entities::Ci::Runner
- end
- params do
- optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
- desc: 'The scope of specific runners to show'
- optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
- desc: 'The type of the runners to show'
- optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
- optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
- desc: 'The status of the runners to show'
- optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
- use :pagination
- end
- get ':id/runners' do
- runners = ::Ci::Runner.owned_or_instance_wide(user_project.id)
- # scope is deprecated (for project runners), however api documentation still supports it.
- # Not including them in `apply_filter` method as it's not supported for group runners
- runners = filter_runners(runners, params[:scope])
- runners = apply_filter(runners, params)
-
- present paginate(runners), with: Entities::Ci::Runner
- end
-
- desc 'Enable a runner for a project' do
- success Entities::Ci::Runner
- end
- params do
- requires :runner_id, type: Integer, desc: 'The ID of the runner'
- end
- post ':id/runners' do
- runner = get_runner(params[:runner_id])
- authenticate_enable_runner!(runner)
-
- if ::Ci::Runners::AssignRunnerService.new(runner, user_project, current_user).execute.success?
- present runner, with: Entities::Ci::Runner
- else
- render_validation_error!(runner)
- end
- end
-
- desc "Disable project's runner" do
- success Entities::Ci::Runner
- end
- params do
- requires :runner_id, type: Integer, desc: 'The ID of the runner'
- end
- # rubocop: disable CodeReuse/ActiveRecord
- delete ':id/runners/:runner_id' do
- runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
- not_found!('Runner') unless runner_project
-
- runner = runner_project.runner
- forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.runner_projects.count == 1
-
- destroy_conditionally!(runner_project)
- end
- # rubocop: enable CodeReuse/ActiveRecord
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a group'
- end
- resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before { authorize_admin_group }
-
- desc 'Get runners available for group' do
- success Entities::Ci::Runner
- end
- params do
- optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES,
- desc: 'The type of the runners to show'
- optional :paused, type: Boolean, desc: 'Whether to include only runners that are accepting or ignoring new jobs'
- optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
- desc: 'The status of the runners to show'
- optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
- use :pagination
- end
- get ':id/runners' do
- runners = ::Ci::Runner.group_or_instance_wide(user_group)
- runners = apply_filter(runners, params)
-
- present paginate(runners), with: Entities::Ci::Runner
- end
- end
-
- resource :runners do
- before { authenticate_non_get! }
-
- desc 'Resets runner registration token' do
- success Entities::Ci::ResetTokenResult
- end
- post 'reset_registration_token' do
- authorize! :update_runners_registration_token, ApplicationSetting.current
-
- ::Ci::Runners::ResetRegistrationTokenService.new(ApplicationSetting.current, current_user).execute
- present ApplicationSetting.current_without_cache.runners_registration_token_with_expiration, with: Entities::Ci::ResetTokenResult
- end
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before { authenticate_non_get! }
-
- desc 'Resets runner registration token' do
- success Entities::Ci::ResetTokenResult
- end
- post ':id/runners/reset_registration_token' do
- project = find_project! user_project.id
- authorize! :update_runners_registration_token, project
-
- project.reset_runners_token!
- present project.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
- end
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a group'
- end
- resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before { authenticate_non_get! }
-
- desc 'Resets runner registration token' do
- success Entities::Ci::ResetTokenResult
- end
- post ':id/runners/reset_registration_token' do
- group = find_group! user_group.id
- authorize! :update_runners_registration_token, group
-
- group.reset_runners_token!
- present group.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
- end
- end
-
helpers do
+ params :deprecated_filter_params do
+ optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
+ desc: 'Deprecated: Use `type` or `status` instead. The scope of specific runners to return'
+ end
+
+ params :filter_params do
+ optional :type, type: String, values: ::Ci::Runner::AVAILABLE_TYPES, desc: 'The type of runners to return'
+ optional :paused, type: Boolean,
+ desc: 'Whether to include only runners that are accepting or ignoring new jobs'
+ optional :status, type: String, values: ::Ci::Runner::AVAILABLE_STATUSES,
+ desc: 'The status of runners to return'
+ optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'A list of runner tags', documentation: { example: "['macos', 'shell']" }
+ use :pagination
+ end
+
def filter_runners(runners, scope, allowed_scopes: ::Ci::Runner::AVAILABLE_SCOPES)
return runners unless scope.present?
@@ -364,6 +91,329 @@ module API
forbidden!("No access granted") unless can?(current_user, :read_builds, runner)
end
end
+
+ resource :runners do
+ desc 'Get runners available for user' do
+ summary 'List owned runners'
+ success Entities::Ci::Runner
+ failure [[400, 'Scope contains invalid value'], [401, 'Unauthorized']]
+ tags %w[runners]
+ end
+ params do
+ use :deprecated_filter_params
+ use :filter_params
+ end
+ get do
+ runners = current_user.ci_owned_runners
+ runners = filter_runners(runners, params[:scope], allowed_scopes: ::Ci::Runner::AVAILABLE_STATUSES)
+ runners = apply_filter(runners, params)
+
+ present paginate(runners), with: Entities::Ci::Runner
+ end
+
+ desc 'Get all runners - shared and specific' do
+ summary 'List all runners'
+ detail 'Get a list of all runners in the GitLab instance (specific and shared). ' \
+ 'Access is restricted to users with administrator access.'
+ success Entities::Ci::Runner
+ failure [[400, 'Scope contains invalid value'], [401, 'Unauthorized']]
+ tags %w[runners]
+ end
+ params do
+ use :deprecated_filter_params
+ use :filter_params
+ end
+ get 'all' do
+ authenticated_as_admin!
+
+ runners = ::Ci::Runner.all
+ runners = filter_runners(runners, params[:scope])
+ runners = apply_filter(runners, params)
+
+ present paginate(runners), with: Entities::Ci::Runner
+ end
+
+ desc "Get runner's details" do
+ detail 'At least the Maintainer role is required to get runner details at the project and group level. ' \
+ 'Instance-level runner details via this endpoint are available to all signed in users.'
+ success Entities::Ci::RunnerDetails
+ failure [[401, 'Unauthorized'], [403, 'No access granted'], [404, 'Runner not found']]
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a runner'
+ end
+ get ':id' do
+ runner = get_runner(params[:id])
+ authenticate_show_runner!(runner)
+
+ present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
+ end
+
+ desc "Update runner's details" do
+ summary "Update details of a runner"
+ success Entities::Ci::RunnerDetails
+ failure [[400, 'Bad Request'], [401, 'Unauthorized'], [403, 'No access granted'], [404, 'Runner not found']]
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a runner'
+ optional :description, type: String, desc: 'The description of the runner'
+ optional :active, type: Boolean, desc: 'Deprecated: Use `paused` instead. Flag indicating whether the runner is allowed to receive jobs'
+ optional :paused, type: Boolean, desc: 'Specifies whether the runner should ignore new jobs'
+ optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ desc: 'The list of tags for a runner', documentation: { example: "['macos', 'shell']" }
+ optional :run_untagged, type: Boolean, desc: 'Specifies whether the runner can execute untagged jobs'
+ optional :locked, type: Boolean, desc: 'Specifies whether the runner is locked'
+ optional :access_level, type: String, values: ::Ci::Runner.access_levels.keys,
+ desc: 'The access level of the runner'
+ optional :maximum_timeout, type: Integer,
+ desc: 'Maximum timeout that limits the amount of time (in seconds) ' \
+ 'that runners can run jobs'
+ at_least_one_of :description, :active, :paused, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
+ mutually_exclusive :active, :paused
+ end
+ put ':id' do
+ runner = get_runner(params.delete(:id))
+ authenticate_update_runner!(runner)
+ params[:active] = !params.delete(:paused) if params.include?(:paused)
+ update_service = ::Ci::Runners::UpdateRunnerService.new(runner)
+
+ if update_service.execute(declared_params(include_missing: false)).success?
+ present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
+ else
+ render_validation_error!(runner)
+ end
+ end
+
+ desc 'Remove a runner' do
+ summary 'Delete a runner'
+ success Entities::Ci::Runner
+ failure [[401, 'Unauthorized'], [403, 'No access granted'],
+ [403, 'Runner associated with more than one project'], [404, 'Runner not found'],
+ [412, 'Precondition Failed']]
+ tags %w[runners]
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a runner'
+ end
+ delete ':id' do
+ runner = get_runner(params[:id])
+
+ authenticate_delete_runner!(runner)
+
+ destroy_conditionally!(runner) { ::Ci::Runners::UnregisterRunnerService.new(runner, current_user).execute }
+ end
+
+ desc 'List jobs running on a runner' do
+ summary "List runner's jobs"
+ detail 'List jobs that are being processed or were processed by the specified runner. ' \
+ 'The list of jobs is limited to projects where the user has at least the Reporter role.'
+ success Entities::Ci::JobBasicWithProject
+ failure [[401, 'Unauthorized'], [403, 'No access granted'], [404, 'Runner not found']]
+ tags %w[runners jobs]
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a runner'
+ optional :status, type: String, desc: 'Status of the job', values: ::Ci::Build::AVAILABLE_STATUSES
+ optional :order_by, type: String, desc: 'Order by `id`', values: ::Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
+ optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by `asc` or `desc` order. ' \
+ 'Specify `order_by` as well, including for `id`'
+ use :pagination
+ end
+ get ':id/jobs' do
+ runner = get_runner(params[:id])
+ authenticate_list_runners_jobs!(runner)
+
+ jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
+ jobs = jobs.preload( # rubocop: disable CodeReuse/ActiveRecord
+ [
+ :user,
+ { pipeline: { project: [:route, { namespace: :route }] } },
+ { project: [:route, { namespace: :route }] }
+ ]
+ )
+ jobs = paginate(jobs)
+ jobs.each(&:commit) # batch loads all commits in the page
+
+ present jobs, with: Entities::Ci::JobBasicWithProject
+ end
+
+ desc 'Reset runner authentication token' do
+ summary "Reset runner's authentication token"
+ success Entities::Ci::ResetTokenResult
+ failure [[403, 'No access granted'], [404, 'Runner not found']]
+ tags %w[runners]
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the runner'
+ end
+ post ':id/reset_authentication_token' do
+ runner = get_runner(params[:id])
+ authenticate_update_runner!(runner)
+
+ runner.reset_token!
+ present runner.token_with_expiration, with: Entities::Ci::ResetTokenResult
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before { authorize_admin_project }
+
+ desc 'Get runners available for project' do
+ summary "List project's runners"
+ detail 'List all runners available in the project, including from ancestor groups ' \
+ 'and any allowed shared runners.'
+ success Entities::Ci::Runner
+ failure [[400, 'Scope contains invalid value'], [403, 'No access granted']]
+ tags %w[runners projects]
+ end
+ params do
+ use :deprecated_filter_params
+ use :filter_params
+ end
+ get ':id/runners' do
+ runners = ::Ci::Runner.owned_or_instance_wide(user_project.id)
+ # scope is deprecated (for project runners), however api documentation still supports it.
+ # Not including them in `apply_filter` method as it's not supported for group runners
+ runners = filter_runners(runners, params[:scope])
+ runners = apply_filter(runners, params)
+
+ present paginate(runners), with: Entities::Ci::Runner
+ end
+
+ desc 'Enable a runner in project' do
+ detail "Enable an available specific runner in the project."
+ success Entities::Ci::Runner
+ failure [[400, 'Bad Request'],
+ [403, 'No access granted'], [403, 'Runner is a group runner'], [403, 'Runner is locked'],
+ [404, 'Runner not found']]
+ tags %w[runners projects]
+ end
+ params do
+ requires :runner_id, type: Integer, desc: 'The ID of a runner'
+ end
+ post ':id/runners' do
+ runner = get_runner(params[:runner_id])
+ authenticate_enable_runner!(runner)
+
+ if ::Ci::Runners::AssignRunnerService.new(runner, user_project, current_user).execute.success?
+ present runner, with: Entities::Ci::Runner
+ else
+ render_validation_error!(runner)
+ end
+ end
+
+ desc "Disable project's runner" do
+ summary "Disable a specific runner from the project"
+ detail "It works only if the project isn't the only project associated with the specified runner. " \
+ "If so, an error is returned. Use the call to delete a runner instead."
+ success Entities::Ci::Runner
+ failure [[400, 'Bad Request'],
+ [403, 'Only one project associated with the runner. Please remove the runner instead'],
+ [404, 'Runner not found'], [412, 'Precondition Failed']]
+ tags %w[runners projects]
+ end
+ params do
+ requires :runner_id, type: Integer, desc: 'The ID of a runner'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ delete ':id/runners/:runner_id' do
+ runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
+ not_found!('Runner') unless runner_project
+
+ runner = runner_project.runner
+ forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.runner_projects.count == 1
+
+ destroy_conditionally!(runner_project)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a group'
+ end
+ resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before { authorize_admin_group }
+
+ desc 'Get runners available for group' do
+ summary "List group's runners"
+ detail 'List all runners available in the group as well as its ancestor groups, ' \
+ 'including any allowed shared runners.'
+ success Entities::Ci::Runner
+ failure [[400, 'Scope contains invalid value'], [403, 'Forbidden']]
+ tags %w[runners groups]
+ end
+ params do
+ use :filter_params
+ end
+ get ':id/runners' do
+ runners = ::Ci::Runner.group_or_instance_wide(user_group)
+ runners = apply_filter(runners, params)
+
+ present paginate(runners), with: Entities::Ci::Runner
+ end
+ end
+
+ resource :runners do
+ before { authenticate_non_get! }
+
+ desc 'Reset runner registration token' do
+ summary "Reset instance's runner registration token"
+ success Entities::Ci::ResetTokenResult
+ failure [[403, 'Forbidden']]
+ tags %w[runners groups]
+ end
+ post 'reset_registration_token' do
+ authorize! :update_runners_registration_token, ApplicationSetting.current
+
+ ::Ci::Runners::ResetRegistrationTokenService.new(ApplicationSetting.current, current_user).execute
+ present ApplicationSetting.current_without_cache.runners_registration_token_with_expiration, with: Entities::Ci::ResetTokenResult
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before { authenticate_non_get! }
+
+ desc 'Reset runner registration token' do
+ summary "Reset the runner registration token for a project"
+ success Entities::Ci::ResetTokenResult
+ failure [[401, 'Unauthorized'], [403, 'Forbidden'], [404, 'Project Not Found']]
+ tags %w[runners projects]
+ end
+ post ':id/runners/reset_registration_token' do
+ project = find_project! user_project.id
+ authorize! :update_runners_registration_token, project
+
+ project.reset_runners_token!
+ present project.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a group'
+ end
+ resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before { authenticate_non_get! }
+
+ desc 'Reset runner registration token' do
+ summary "Reset the runner registration token for a group"
+ success Entities::Ci::ResetTokenResult
+ failure [[401, 'Unauthorized'], [403, 'Forbidden'], [404, 'Group Not Found']]
+ tags %w[runners groups]
+ end
+ post ':id/runners/reset_registration_token' do
+ group = find_group! user_group.id
+ authorize! :update_runners_registration_token, group
+
+ group.reset_runners_token!
+ present group.runners_token_with_expiration, with: Entities::Ci::ResetTokenResult
+ end
+ end
end
end
end
diff --git a/lib/bulk_imports/common/pipelines/wiki_pipeline.rb b/lib/bulk_imports/common/pipelines/wiki_pipeline.rb
index fea550b9f9d..68d511b065f 100644
--- a/lib/bulk_imports/common/pipelines/wiki_pipeline.rb
+++ b/lib/bulk_imports/common/pipelines/wiki_pipeline.rb
@@ -24,7 +24,7 @@ module BulkImports
Gitlab::UrlBlocker.validate!(url, schemes: %w[http https], allow_local_network: allow_local_requests?, allow_localhost: allow_local_requests?)
- wiki.ensure_repository
+ wiki.create_wiki_repository
wiki.repository.fetch_as_mirror(url)
end
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 27baa6b47b2..58cdff2e977 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -706,6 +706,10 @@ module Gitlab
install_rename_triggers(table, old, new)
end
+ def convert_to_type_column(column, from_type, to_type)
+ "#{column}_convert_#{from_type}_to_#{to_type}"
+ end
+
def convert_to_bigint_column(column)
"#{column}_convert_to_bigint"
end
@@ -736,7 +740,22 @@ module Gitlab
# columns - The name, or array of names, of the column(s) that we want to convert to bigint.
# primary_key - The name of the primary key column (most often :id)
def initialize_conversion_of_integer_to_bigint(table, columns, primary_key: :id)
- create_temporary_columns_and_triggers(table, columns, primary_key: primary_key, data_type: :bigint)
+ mappings = Array(columns).map do |c|
+ {
+ c => {
+ from_type: :int,
+ to_type: :bigint,
+ default_value: 0
+ }
+ }
+ end.reduce(&:merge)
+
+ create_temporary_columns_and_triggers(
+ table,
+ mappings,
+ primary_key: primary_key,
+ old_bigint_column_naming: true
+ )
end
# Reverts `initialize_conversion_of_integer_to_bigint`
@@ -759,9 +778,23 @@ module Gitlab
# table - The name of the database table containing the columns
# columns - The name, or array of names, of the column(s) that we have converted to bigint.
# primary_key - The name of the primary key column (most often :id)
-
def restore_conversion_of_integer_to_bigint(table, columns, primary_key: :id)
- create_temporary_columns_and_triggers(table, columns, primary_key: primary_key, data_type: :int)
+ mappings = Array(columns).map do |c|
+ {
+ c => {
+ from_type: :bigint,
+ to_type: :int,
+ default_value: 0
+ }
+ }
+ end.reduce(&:merge)
+
+ create_temporary_columns_and_triggers(
+ table,
+ mappings,
+ primary_key: primary_key,
+ old_bigint_column_naming: true
+ )
end
# Backfills the new columns used in an integer-to-bigint conversion using background migrations.
@@ -1170,13 +1203,20 @@ into similar problems in the future (e.g. when new tables are created).
SQL
end
- private
+ # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
+ def create_temporary_columns_and_triggers(table, mappings, primary_key: :id, old_bigint_column_naming: false)
+ raise ArgumentError, "No mappings for column conversion provided" if mappings.blank?
- def multiple_columns(columns, separator: ', ')
- Array.wrap(columns).join(separator)
- end
+ unless mappings.values.all? { |values| mapping_has_required_columns?(values) }
+ raise ArgumentError, "Some mappings don't have required keys provided"
+ end
+
+ neutral_values_for_type = {
+ int: 0,
+ bigint: 0,
+ uuid: '00000000-0000-0000-0000-000000000000'
+ }
- def create_temporary_columns_and_triggers(table, columns, primary_key: :id, data_type: :bigint)
unless table_exists?(table)
raise "Table #{table} does not exist"
end
@@ -1185,7 +1225,7 @@ into similar problems in the future (e.g. when new tables are created).
raise "Column #{primary_key} does not exist on #{table}"
end
- columns = Array.wrap(columns)
+ columns = mappings.keys
columns.each do |column|
next if column_exists?(table, column)
@@ -1194,26 +1234,81 @@ into similar problems in the future (e.g. when new tables are created).
check_trigger_permissions!(table)
- conversions = columns.to_h { |column| [column, convert_to_bigint_column(column)] }
+ if old_bigint_column_naming
+ mappings.each do |column, params|
+ params.merge!(
+ temporary_column_name: convert_to_bigint_column(column)
+ )
+ end
+ else
+ mappings.each do |column, params|
+ params.merge!(
+ temporary_column_name: convert_to_type_column(column, params[:from_type], params[:to_type])
+ )
+ end
+ end
with_lock_retries do
- conversions.each do |(source_column, temporary_name)|
- column = column_for(table, source_column)
+ mappings.each do |(column_name, params)|
+ column = column_for(table, column_name)
+ temporary_name = params[:temporary_column_name]
+ data_type = params[:to_type]
+ default_value = params[:default_value]
if (column.name.to_s == primary_key.to_s) || !column.null
# If the column to be converted is either a PK or is defined as NOT NULL,
# set it to `NOT NULL DEFAULT 0` and we'll copy paste the correct values bellow
# That way, we skip the expensive validation step required to add
# a NOT NULL constraint at the end of the process
- add_column(table, temporary_name, data_type, default: column.default || 0, null: false)
+ add_column(
+ table,
+ temporary_name,
+ data_type,
+ default: column.default || default_value || neutral_values_for_type.fetch(data_type),
+ null: false
+ )
else
- add_column(table, temporary_name, data_type, default: column.default)
+ add_column(
+ table,
+ temporary_name,
+ data_type,
+ default: column.default
+ )
end
end
- install_rename_triggers(table, conversions.keys, conversions.values)
+ old_column_names = mappings.keys
+ temporary_column_names = mappings.values.map { |v| v[:temporary_column_name] }
+ install_rename_triggers(table, old_column_names, temporary_column_names)
end
end
+ # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
+
+ private
+
+ def multiple_columns(columns, separator: ', ')
+ Array.wrap(columns).join(separator)
+ end
+
+ def cascade_statement(cascade)
+ cascade ? 'CASCADE' : ''
+ end
+
+ def validate_check_constraint_name!(constraint_name)
+ if constraint_name.to_s.length > MAX_IDENTIFIER_NAME_LENGTH
+ raise "The maximum allowed constraint name is #{MAX_IDENTIFIER_NAME_LENGTH} characters"
+ end
+ end
+
+ # mappings => {} where keys are column names and values are hashes with the following keys:
+ # from_type - from which type we're migrating
+ # to_type - to which type we're migrating
+ # default_value - custom default value, if not provided will be taken from neutral_values_for_type
+ def mapping_has_required_columns?(mapping)
+ %i[from_type to_type].map do |required_key|
+ mapping.has_key?(required_key)
+ end.all?
+ end
def column_is_nullable?(table, column)
# Check if table.column has not been defined with NOT NULL
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6edcf6f70ba..aba42326d98 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -18137,6 +18137,12 @@ msgstr ""
msgid "GitLab Billing Team."
msgstr ""
+msgid "GitLab Community Edition"
+msgstr ""
+
+msgid "GitLab Enterprise Edition"
+msgstr ""
+
msgid "GitLab Error Tracking"
msgstr ""
@@ -46738,7 +46744,7 @@ msgstr ""
msgid "You must be logged in to search across all of GitLab"
msgstr ""
-msgid "You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline configuration or CI/CD settings. If you need to view this job log, a project maintainer or owner must add you to the project with developer permissions or higher."
+msgid "You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to 'false' in your pipeline configuration or CI/CD settings. If you must view this job log, a project maintainer or owner must add you to the project with developer permissions or higher."
msgstr ""
msgid "You must have maintainer access to force delete a lock"
@@ -47302,9 +47308,6 @@ msgstr ""
msgid "`start_time` should precede `end_time`"
msgstr ""
-msgid "`work_items_hierarchy` feature flag disabled for this project"
-msgstr ""
-
msgid "a deleted user"
msgstr ""
diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb
index f79a2c6a6d0..2d145b6ff1d 100644
--- a/spec/controllers/projects/artifacts_controller_spec.rb
+++ b/spec/controllers/projects/artifacts_controller_spec.rb
@@ -228,8 +228,9 @@ RSpec.describe Projects::ArtifactsController do
expect(response).to have_gitlab_http_status(:forbidden)
expect(response.body).to include(
'You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. ' \
- 'To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline configuration or CI/CD settings. ' \
- 'If you need to view this job log, a project maintainer or owner must add you to the project with developer permissions or higher.'
+ 'To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to 'false' ' \
+ 'in your pipeline configuration or CI/CD settings. If you must view this job log, a project maintainer or owner must ' \
+ 'add you to the project with developer permissions or higher.'
)
end
end
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 58627c4dc74..3dc89365530 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -660,6 +660,38 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
end
end
+
+ context 'when CI_DEBUG_SERVICES enabled' do
+ let!(:variable) { create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: 'true') }
+
+ context 'with proper permissions on a project' do
+ let(:user) { developer }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'returns response ok' do
+ get_trace
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'without proper permissions for debug logging' do
+ let(:user) { guest }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'returns response forbidden' do
+ get_trace
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
end
context 'when job has a live trace' do
@@ -1184,36 +1216,51 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
- context 'when CI_DEBUG_TRACE enabled' do
- before do
- create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: 'true')
+ context 'when CI_DEBUG_TRACE and/or CI_DEBUG_SERVICES are enabled' do
+ using RSpec::Parameterized::TableSyntax
+ where(:ci_debug_trace, :ci_debug_services) do
+ 'true' | 'true'
+ 'true' | 'false'
+ 'false' | 'true'
+ 'false' | 'false'
end
- context 'with proper permissions for debug logging on a project' do
- let(:user) { developer }
-
+ with_them do
before do
- sign_in(user)
+ create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: ci_debug_trace)
+ create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: ci_debug_services)
end
- it 'returns response ok' do
- response = subject
+ context 'with proper permissions for debug logging on a project' do
+ let(:user) { developer }
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
+ before do
+ sign_in(user)
+ end
- context 'without proper permissions for debug logging on a project' do
- let(:user) { reporter }
+ it 'returns response ok' do
+ response = subject
- before do
- sign_in(user)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
- it 'returns response forbidden' do
- response = subject
+ context 'without proper permissions for debug logging on a project' do
+ let(:user) { reporter }
- expect(response).to have_gitlab_http_status(:forbidden)
+ before do
+ sign_in(user)
+ end
+
+ it 'returns response forbidden if dev mode enabled' do
+ response = subject
+
+ if ci_debug_trace == 'true' || ci_debug_services == 'true'
+ expect(response).to have_gitlab_http_status(:forbidden)
+ else
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
end
end
end
diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb
index b6019944071..740d009d6b8 100644
--- a/spec/features/projects/jobs/permissions_spec.rb
+++ b/spec/features/projects/jobs/permissions_spec.rb
@@ -211,4 +211,48 @@ RSpec.describe 'Project Jobs Permissions' do
end
end
end
+
+ context 'with CI_DEBUG_SERVICES' do
+ let_it_be(:ci_instance_variable) { create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES') }
+
+ describe 'trace endpoint and raw page' do
+ let_it_be(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
+
+ where(:public_builds, :user_project_role, :ci_debug_services, :expected_status_code, :expected_msg) do
+ true | 'developer' | true | 200 | nil
+ true | 'guest' | true | 403 | 'You must have developer or higher permissions'
+ true | nil | true | 404 | 'Page Not Found Make sure the address is correct'
+ true | 'developer' | false | 200 | nil
+ true | 'guest' | false | 200 | nil
+ true | nil | false | 404 | 'Page Not Found Make sure the address is correct'
+ false | 'developer' | true | 200 | nil
+ false | 'guest' | true | 403 | 'You must have developer or higher permissions'
+ false | nil | true | 404 | 'Page Not Found Make sure the address is correct'
+ false | 'developer' | false | 200 | nil
+ false | 'guest' | false | 403 | 'The current user is not authorized to access the job log'
+ false | nil | false | 404 | 'Page Not Found Make sure the address is correct'
+ end
+
+ with_them do
+ before do
+ ci_instance_variable.update!(value: ci_debug_services)
+ project.update!(public_builds: public_builds)
+ user_project_role && project.add_role(user, user_project_role)
+ end
+
+ it 'renders trace to authorized users' do
+ visit trace_project_job_path(project, job)
+
+ expect(status_code).to eq(expected_status_code)
+ end
+
+ it 'renders raw trace to authorized users' do
+ visit raw_project_job_path(project, job)
+
+ expect(status_code).to eq(expected_status_code)
+ expect(page).to have_content(expected_msg)
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/work_items/work_item_children_spec.rb b/spec/features/work_items/work_item_children_spec.rb
index 95774680a2b..e83193da599 100644
--- a/spec/features/work_items/work_item_children_spec.rb
+++ b/spec/features/work_items/work_item_children_spec.rb
@@ -15,7 +15,6 @@ RSpec.describe 'Work item children', :js do
sign_in(user)
stub_feature_flags(work_items: true)
- stub_feature_flags(work_items_hierarchy: true)
visit project_issue_path(project, issue)
diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
new file mode 100644
index 00000000000..3f72396cce6
--- /dev/null
+++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
@@ -0,0 +1,58 @@
+import { GlEmptyState } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import IssuesDashboardApp from '~/issues/dashboard/components/issues_dashboard_app.vue';
+import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
+import { IssuableStates } from '~/vue_shared/issuable/list/constants';
+
+describe('IssuesDashboardApp component', () => {
+ let wrapper;
+
+ const defaultProvide = {
+ calendarPath: 'calendar/path',
+ emptyStateSvgPath: 'empty-state.svg',
+ isSignedIn: true,
+ rssPath: 'rss/path',
+ };
+
+ const findCalendarButton = () =>
+ wrapper.findByRole('link', { name: IssuesDashboardApp.i18n.calendarButtonText });
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findIssuableList = () => wrapper.findComponent(IssuableList);
+ const findRssButton = () =>
+ wrapper.findByRole('link', { name: IssuesDashboardApp.i18n.rssButtonText });
+
+ const mountComponent = () => {
+ wrapper = mountExtended(IssuesDashboardApp, { provide: defaultProvide });
+ };
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('renders IssuableList component', () => {
+ expect(findIssuableList().props()).toMatchObject({
+ currentTab: IssuableStates.Opened,
+ namespace: 'dashboard',
+ recentSearchesStorageKey: 'issues',
+ searchInputPlaceholder: IssuesDashboardApp.i18n.searchInputPlaceholder,
+ tabs: IssuesDashboardApp.IssuableListTabs,
+ });
+ });
+
+ it('renders RSS button link', () => {
+ expect(findRssButton().attributes('href')).toBe(defaultProvide.rssPath);
+ expect(findRssButton().props('icon')).toBe('rss');
+ });
+
+ it('renders calendar button link', () => {
+ expect(findCalendarButton().attributes('href')).toBe(defaultProvide.calendarPath);
+ expect(findCalendarButton().props('icon')).toBe('calendar');
+ });
+
+ it('renders empty state', () => {
+ expect(findEmptyState().props()).toMatchObject({
+ svgPath: defaultProvide.emptyStateSvgPath,
+ title: IssuesDashboardApp.i18n.emptyStateTitle,
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js
index 0b59fe2d8ce..7da91c4af96 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js
@@ -33,7 +33,7 @@ describe('Image List Row', () => {
const findListItemComponent = () => wrapper.findComponent(ListItem);
const findShowFullPathButton = () => wrapper.findComponent(GlButton);
- const mountComponent = (props, features = {}) => {
+ const mountComponent = (props) => {
wrapper = shallowMount(Component, {
stubs: {
RouterLink,
@@ -47,9 +47,6 @@ describe('Image List Row', () => {
},
provide: {
config: {},
- glFeatures: {
- ...features,
- },
},
directives: {
GlTooltip: createMockDirective(),
@@ -88,23 +85,43 @@ describe('Image List Row', () => {
});
describe('image title and path', () => {
- it('contains a link to the details page', () => {
+ it('renders shortened name of image and contains a link to the details page', () => {
mountComponent();
const link = findDetailsLink();
- expect(link.text()).toBe(item.path);
- expect(findDetailsLink().props('to')).toMatchObject({
+ expect(link.text()).toBe('gitlab-test/rails-12009');
+
+ expect(link.props('to')).toMatchObject({
name: 'details',
params: {
id: getIdFromGraphQLId(item.id),
},
});
+
+ expect(findShowFullPathButton().exists()).toBe(true);
});
it('when the image has no name lists the path', () => {
mountComponent({ item: { ...item, name: '' } });
+ expect(findDetailsLink().text()).toBe('gitlab-test');
+ });
+
+ it('clicking on shortened name of image hides the button & shows full path', async () => {
+ mountComponent();
+
+ const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ const mockFocusFn = jest.fn();
+ wrapper.vm.$refs.imageName.$el.focus = mockFocusFn;
+
+ await findShowFullPathButton().trigger('click');
+
+ expect(findShowFullPathButton().exists()).toBe(false);
expect(findDetailsLink().text()).toBe(item.path);
+ expect(mockFocusFn).toHaveBeenCalled();
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_show_full_path', {
+ label: 'registry_image_list',
+ });
});
it('contains a clipboard button', () => {
@@ -149,35 +166,6 @@ describe('Image List Row', () => {
expect(findClipboardButton().attributes('disabled')).toBe('true');
});
});
-
- describe('when containerRegistryShowShortenedPath feature enabled', () => {
- let trackingSpy;
-
- beforeEach(() => {
- mountComponent({}, { containerRegistryShowShortenedPath: true });
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- it('renders shortened name of image', () => {
- expect(findShowFullPathButton().exists()).toBe(true);
- expect(findDetailsLink().text()).toBe('gitlab-test/rails-12009');
- });
-
- it('clicking on shortened name of image hides the button & shows full path', async () => {
- const btn = findShowFullPathButton();
- const mockFocusFn = jest.fn();
- wrapper.vm.$refs.imageName.$el.focus = mockFocusFn;
-
- await btn.trigger('click');
-
- expect(findShowFullPathButton().exists()).toBe(false);
- expect(findDetailsLink().text()).toBe(item.path);
- expect(mockFocusFn).toHaveBeenCalled();
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_show_full_path', {
- label: 'registry_image_list',
- });
- });
- });
});
describe('delete button', () => {
diff --git a/spec/helpers/appearances_helper_spec.rb b/spec/helpers/appearances_helper_spec.rb
index 2206c1ce2ae..b3afd350397 100644
--- a/spec/helpers/appearances_helper_spec.rb
+++ b/spec/helpers/appearances_helper_spec.rb
@@ -169,4 +169,13 @@ RSpec.describe AppearancesHelper do
expect(helper.brand_title).to eq(helper.default_brand_title)
end
end
+
+ describe '#default_brand_title' do
+ it 'returns the default title' do
+ edition = Gitlab.ee? ? 'Enterprise' : 'Community'
+ expected_default_brand_title = "GitLab #{edition} Edition"
+
+ expect(helper.default_brand_title).to eq _(expected_default_brand_title)
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 26e97e09f30..5bc56f11425 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -2097,6 +2097,110 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
end
+ describe '#convert_to_type_column' do
+ it 'returns the name of the temporary column used to convert to bigint' do
+ expect(model.convert_to_type_column(:id, :int, :bigint)).to eq('id_convert_int_to_bigint')
+ end
+
+ it 'returns the name of the temporary column used to convert to uuid' do
+ expect(model.convert_to_type_column(:uuid, :string, :uuid)).to eq('uuid_convert_string_to_uuid')
+ end
+ end
+
+ describe '#create_temporary_columns_and_triggers' do
+ let(:table) { :test_table }
+ let(:column) { :id }
+ let(:mappings) do
+ {
+ id: {
+ from_type: :int,
+ to_type: :bigint
+ }
+ }
+ end
+
+ let(:old_bigint_column_naming) { false }
+
+ subject do
+ model.create_temporary_columns_and_triggers(
+ table,
+ mappings,
+ old_bigint_column_naming: old_bigint_column_naming
+ )
+ end
+
+ before do
+ model.create_table table, id: false do |t|
+ t.integer :id, primary_key: true
+ t.integer :non_nullable_column, null: false
+ t.integer :nullable_column
+ t.timestamps
+ end
+ end
+
+ context 'when no mappings are provided' do
+ let(:mappings) { nil }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error("No mappings for column conversion provided")
+ end
+ end
+
+ context 'when any of the mappings does not have the required keys' do
+ let(:mappings) do
+ {
+ id: {
+ from_type: :int
+ }
+ }
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error("Some mappings don't have required keys provided")
+ end
+ end
+
+ context 'when the target table does not exist' do
+ it 'raises an error' do
+ expect { model.create_temporary_columns_and_triggers(:non_existent_table, mappings) }.to raise_error("Table non_existent_table does not exist")
+ end
+ end
+
+ context 'when the column to migrate does not exist' do
+ let(:missing_column) { :test }
+ let(:mappings) do
+ {
+ missing_column => {
+ from_type: :int,
+ to_type: :bigint
+ }
+ }
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error("Column #{missing_column} does not exist on #{table}")
+ end
+ end
+
+ context 'when old_bigint_column_naming is true' do
+ let(:old_bigint_column_naming) { true }
+
+ it 'calls convert_to_bigint_column' do
+ expect(model).to receive(:convert_to_bigint_column).with(:id).and_return("id_convert_to_bigint")
+
+ subject
+ end
+ end
+
+ context 'when old_bigint_column_naming is false' do
+ it 'calls convert_to_type_column' do
+ expect(model).to receive(:convert_to_type_column).with(:id, :int, :bigint).and_return("id_convert_to_bigint")
+
+ subject
+ end
+ end
+ end
+
describe '#initialize_conversion_of_integer_to_bigint' do
let(:table) { :test_table }
let(:column) { :id }
@@ -2253,7 +2357,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
let(:columns) { :id }
it 'removes column, trigger, and function' do
- temporary_column = model.convert_to_bigint_column(:id)
+ temporary_column = model.convert_to_bigint_column(columns)
trigger_name = model.rename_trigger_name(table, :id, temporary_column)
model.revert_initialize_conversion_of_integer_to_bigint(table, columns)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 9c83c1df10c..b3313c09ef6 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -5095,6 +5095,60 @@ RSpec.describe Ci::Build do
context 'when CI_DEBUG_TRACE is not in variables' do
it { is_expected.to eq false }
end
+
+ context 'when CI_DEBUG_SERVICES=true is in variables' do
+ context 'when in instance variables' do
+ before do
+ create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: 'true')
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in group variables' do
+ before do
+ create(:ci_group_variable, key: 'CI_DEBUG_SERVICES', value: 'true', group: project.group)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in pipeline variables' do
+ before do
+ create(:ci_pipeline_variable, key: 'CI_DEBUG_SERVICES', value: 'true', pipeline: pipeline)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in project variables' do
+ before do
+ create(:ci_variable, key: 'CI_DEBUG_SERVICES', value: 'true', project: project)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in job variables' do
+ before do
+ create(:ci_job_variable, key: 'CI_DEBUG_SERVICES', value: 'true', job: build)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in yaml variables' do
+ before do
+ build.update!(yaml_variables: [{ key: :CI_DEBUG_SERVICES, value: 'true' }])
+ end
+
+ it { is_expected.to eq true }
+ end
+ end
+
+ context 'when CI_DEBUG_SERVICES is not in variables' do
+ it { is_expected.to eq false }
+ end
end
describe '#drop_with_exit_code!' do
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index 0e17db516f4..c1b7461f444 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -606,6 +606,32 @@ RSpec.describe API::Ci::Jobs do
end
end
end
+
+ context 'when ci_debug_services is set to true' do
+ before_all do
+ create(:ci_instance_variable, key: 'CI_DEBUG_SERVICES', value: true)
+ end
+
+ where(:public_builds, :user_project_role, :expected_status) do
+ true | 'developer' | :ok
+ true | 'guest' | :forbidden
+ false | 'developer' | :ok
+ false | 'guest' | :forbidden
+ end
+
+ with_them do
+ before do
+ project.update!(public_builds: public_builds)
+ project.add_role(user, user_project_role)
+
+ get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
+ end
+
+ it 'renders successfully to authorized users' do
+ expect(response).to have_gitlab_http_status(expected_status)
+ end
+ end
+ end
end
describe 'POST /projects/:id/jobs/:job_id/cancel' do
diff --git a/spec/requests/jira_connect/subscriptions_controller_spec.rb b/spec/requests/jira_connect/subscriptions_controller_spec.rb
index ee5c26295fd..29da7f25d30 100644
--- a/spec/requests/jira_connect/subscriptions_controller_spec.rb
+++ b/spec/requests/jira_connect/subscriptions_controller_spec.rb
@@ -5,7 +5,6 @@ require 'spec_helper'
RSpec.describe JiraConnect::SubscriptionsController do
describe 'GET /-/jira_connect/subscriptions' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'http://self-managed-gitlab.com') }
-
let(:qsh) do
Atlassian::Jwt.create_query_string_hash('https://gitlab.test/subscriptions', 'GET', 'https://gitlab.test')
end
@@ -20,12 +19,14 @@ RSpec.describe JiraConnect::SubscriptionsController do
it { is_expected.to include('http://self-managed-gitlab.com/-/jira_connect/') }
it { is_expected.to include('http://self-managed-gitlab.com/api/') }
+ it { is_expected.to include('http://self-managed-gitlab.com/oauth/') }
context 'with no self-managed instance configured' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: '') }
it { is_expected.not_to include('http://self-managed-gitlab.com/-/jira_connect/') }
it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
+ it { is_expected.not_to include('http://self-managed-gitlab.com/oauth/') }
end
context 'with jira_connect_oauth_self_managed_setting feature disabled' do
@@ -35,6 +36,7 @@ RSpec.describe JiraConnect::SubscriptionsController do
it { is_expected.not_to include('http://self-managed-gitlab.com/-/jira_connect/') }
it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
+ it { is_expected.not_to include('http://self-managed-gitlab.com/oauth/') }
end
end
end
diff --git a/spec/services/issues/relative_position_rebalancing_service_spec.rb b/spec/services/issues/relative_position_rebalancing_service_spec.rb
index 37a94e1d6a2..43c94ef07aa 100644
--- a/spec/services/issues/relative_position_rebalancing_service_spec.rb
+++ b/spec/services/issues/relative_position_rebalancing_service_spec.rb
@@ -34,10 +34,6 @@ RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_s
end
end
- before do
- stub_feature_flags(issue_rebalancing_with_retry: false)
- end
-
def issues_in_position_order
project.reload.issues.order_by_relative_position.to_a
end
diff --git a/spec/services/work_items/create_service_spec.rb b/spec/services/work_items/create_service_spec.rb
index c0bcf9b606d..1bd7e15db67 100644
--- a/spec/services/work_items/create_service_spec.rb
+++ b/spec/services/work_items/create_service_spec.rb
@@ -179,16 +179,6 @@ RSpec.describe WorkItems::CreateService do
let(:error_message) { 'only Issue and Incident can be parent of Task.' }
end
end
-
- context 'when hierarchy feature flag is disabled' do
- before do
- stub_feature_flags(work_items_hierarchy: false)
- end
-
- it_behaves_like 'fails creating work item and returns errors' do
- let(:error_message) { '`work_items_hierarchy` feature flag disabled for this project' }
- end
- end
end
context 'when user cannot admin parent link' do
diff --git a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
index 9a425d5308c..1b8c4c5f15f 100644
--- a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
@@ -42,18 +42,6 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do
let_it_be(:child_work_item3) { create(:work_item, :task, project: project) }
let_it_be(:child_work_item4) { create(:work_item, :task, project: project) }
- context 'when work_items_hierarchy feature flag is disabled' do
- let(:params) { { children: [child_work_item4] } }
-
- before do
- stub_feature_flags(work_items_hierarchy: false)
- end
-
- it_behaves_like 'raises a WidgetError' do
- let(:message) { '`work_items_hierarchy` feature flag disabled for this project' }
- end
- end
-
context 'when user has insufficient permissions to link work items' do
let(:params) { { children: [child_work_item4] } }
@@ -105,16 +93,6 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do
let(:params) { { parent: parent_work_item } }
- context 'when work_items_hierarchy feature flag is disabled' do
- before do
- stub_feature_flags(work_items_hierarchy: false)
- end
-
- it_behaves_like 'raises a WidgetError' do
- let(:message) { '`work_items_hierarchy` feature flag disabled for this project' }
- end
- end
-
context 'when user has insufficient permissions to link work items' do
it_behaves_like 'raises a WidgetError' do
let(:message) { not_found_error }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8a6570fba8d..9d169c50013 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -310,6 +310,10 @@ RSpec.configure do |config|
# See https://docs.gitlab.com/ee/development/feature_flags/#selectively-disable-by-actor
stub_feature_flags(legacy_merge_request_state_check_for_merged_result_pipelines: false)
+ # Disable the `vue_issues_dashboard` feature flag in specs as we migrate the issues
+ # dashboard page to Vue. https://gitlab.com/gitlab-org/gitlab/-/issues/379025
+ stub_feature_flags(vue_issues_dashboard: false)
+
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
else
unstub_all_feature_flags
diff --git a/spec/support/shared_examples/bulk_imports/common/pipelines/wiki_pipeline_examples.rb b/spec/support/shared_examples/bulk_imports/common/pipelines/wiki_pipeline_examples.rb
index cd4432af4ed..a9edf18d562 100644
--- a/spec/support/shared_examples/bulk_imports/common/pipelines/wiki_pipeline_examples.rb
+++ b/spec/support/shared_examples/bulk_imports/common/pipelines/wiki_pipeline_examples.rb
@@ -35,7 +35,7 @@ RSpec.shared_examples 'wiki pipeline imports a wiki for an entity' do
it 'does not import wiki' do
expect(subject).to receive(:source_wiki_exists?).and_return(false)
- expect(parent.wiki).not_to receive(:ensure_repository)
+ expect(parent.wiki).not_to receive(:create_wiki_repository)
expect(parent.wiki.repository).not_to receive(:ensure_repository)
expect { subject.run }.not_to raise_error
@@ -75,7 +75,7 @@ RSpec.shared_examples 'wiki pipeline imports a wiki for an entity' do
describe 'unsuccessful response' do
shared_examples 'does not raise an error' do
it 'does not raise an error' do
- expect(parent.wiki).not_to receive(:ensure_repository)
+ expect(parent.wiki).not_to receive(:create_wiki_repository)
expect(parent.wiki.repository).not_to receive(:ensure_repository)
expect { subject.run }.not_to raise_error
diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb
index be7e19492d7..7e69a6663d5 100644
--- a/spec/support/shared_examples/models/wiki_shared_examples.rb
+++ b/spec/support/shared_examples/models/wiki_shared_examples.rb
@@ -846,29 +846,6 @@ RSpec.shared_examples 'wiki model' do
end
end
- describe '#ensure_repository' do
- context 'if the repository exists' do
- it 'does not create the repository' do
- expect(subject.repository.exists?).to eq(true)
- expect(subject.repository.raw).not_to receive(:create_repository)
-
- subject.ensure_repository
- end
- end
-
- context 'if the repository does not exist' do
- let(:wiki_container) { wiki_container_without_repo }
-
- it 'creates the repository' do
- expect(subject.repository.exists?).to eq(false)
-
- subject.ensure_repository
-
- expect(subject.repository.exists?).to eq(true)
- end
- end
- end
-
describe '#hook_attrs' do
it 'returns a hash with values' do
expect(subject.hook_attrs).to be_a Hash