diff --git a/app/graphql/resolvers/noteable/notes_resolver.rb b/app/graphql/resolvers/noteable/notes_resolver.rb index 0d25c747ffb..b4bd1068723 100644 --- a/app/graphql/resolvers/noteable/notes_resolver.rb +++ b/app/graphql/resolvers/noteable/notes_resolver.rb @@ -7,6 +7,11 @@ module Resolvers type Types::Notes::NoteType.connection_type, null: false + argument :filter, Types::WorkItems::NotesFilterTypeEnum, + required: false, + default_value: ::UserPreference::NOTES_FILTERS[:all_notes], + description: 'Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY.' + before_connection_authorization do |nodes, current_user| next if nodes.blank? @@ -16,8 +21,9 @@ module Resolvers ::Preloaders::Projects::NotesPreloader.new(project, current_user).call(nodes) end - def resolve_with_lookahead(*) - apply_lookahead(object.notes.fresh) + def resolve_with_lookahead(**args) + notes = NotesFinder.new(current_user, build_params(args)).execute + apply_lookahead(notes) end private @@ -31,6 +37,17 @@ module Resolvers award_emoji: [:award_emoji] } end + + def build_params(args) + params = { + project: object.project, + target: object + } + + params[:notes_filter] = args[:filter] if args[:filter].present? + + params + end end end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index ef21434d8f1..f48157cb65a 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -188,7 +188,7 @@ module GroupsHelper } end - def enabled_git_access_protocol_options_for_group + def enabled_git_access_protocol_options_for_group(_) case ::Gitlab::CurrentSettings.enabled_git_access_protocol when nil, "" [[_("Both SSH and HTTP(S)"), "all"], [_("Only SSH"), "ssh"], [_("Only HTTP(S)"), "http"]] diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index c7088908de8..79b1718233f 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -305,7 +305,7 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord if: :auto_devops_enabled? validates :enabled_git_access_protocol, - inclusion: { in: %w[ssh http], allow_blank: true } + inclusion: { in: ->(_) { enabled_git_access_protocol_values }, allow_blank: true } validates :domain_denylist, presence: { message: 'Domain denylist cannot be empty if denylist is enabled.' }, @@ -959,6 +959,10 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord HUMANIZED_ATTRIBUTES[attribute.to_sym] || super end + def self.enabled_git_access_protocol_values + %w[ssh http] + end + def parsed_grafana_url @parsed_grafana_url ||= Gitlab::Utils.parse_url(grafana_url) end diff --git a/app/models/group.rb b/app/models/group.rb index 9330ffef156..8ce8b745b35 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -694,7 +694,7 @@ class Group < Namespace return GroupMember::NO_ACCESS unless user return GroupMember::OWNER if user.can_admin_all_resources? && !only_concrete_membership - max_member_access([user.id])[user.id] + max_member_access(user) end def mattermost_team_params @@ -953,16 +953,16 @@ class Group < Namespace end end - def max_member_access(user_ids) - ::Gitlab::Database.allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/417455") do - Gitlab::SafeRequestLoader.execute( - resource_key: max_member_access_for_resource_key(User), - resource_ids: user_ids, - default_value: Gitlab::Access::NO_ACCESS - ) do |user_ids| - members_with_parents.where(user_id: user_ids).group(:user_id).maximum(:access_level) - end - end + def max_member_access(user) + Gitlab::SafeRequestLoader.execute( + resource_key: max_member_access_for_resource_key(User), + resource_ids: [user.id], + default_value: Gitlab::Access::NO_ACCESS + ) do |_| + next {} unless user.active? + + members_with_parents(only_active_users: false).where(user_id: user.id).group(:user_id).maximum(:access_level) + end.fetch(user.id) end def update_two_factor_requirement diff --git a/app/models/namespace_setting.rb b/app/models/namespace_setting.rb index 8d5d788c738..3befcdeaec5 100644 --- a/app/models/namespace_setting.rb +++ b/app/models/namespace_setting.rb @@ -10,7 +10,7 @@ class NamespaceSetting < ApplicationRecord belongs_to :namespace, inverse_of: :namespace_settings enum jobs_to_be_done: { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5 }, _suffix: true - enum enabled_git_access_protocol: { all: 0, ssh: 1, http: 2 }, _suffix: true + enum enabled_git_access_protocol: { all: 0, ssh: 1, http: 2, ssh_certificates: 3 }, _suffix: true attribute :default_branch_protection_defaults, default: -> { {} } diff --git a/app/models/namespaces/traversal/linear.rb b/app/models/namespaces/traversal/linear.rb index 1ca3c8e85f3..c3348c49ea1 100644 --- a/app/models/namespaces/traversal/linear.rb +++ b/app/models/namespaces/traversal/linear.rb @@ -61,8 +61,6 @@ module Namespaces # INPUT: [[4909902], [4909902,51065789], [4909902,51065793], [7135830], [15599674, 1], [15599674, 1, 3], [15599674, 2]] # RESULT: [[4909902], [7135830], [15599674, 1], [15599674, 2]] def shortest_traversal_ids_prefixes - raise ArgumentError, 'Feature not supported since the `:use_traversal_ids` is disabled' unless use_traversal_ids? - prefixes = [] # The array needs to be sorted (O(nlogn)) to ensure shortest elements are always first @@ -91,8 +89,6 @@ module Namespaces end def use_traversal_ids? - return false unless Feature.enabled?(:use_traversal_ids) - traversal_ids.present? end diff --git a/app/models/namespaces/traversal/linear_scopes.rb b/app/models/namespaces/traversal/linear_scopes.rb index d152ca412f8..c63639e721a 100644 --- a/app/models/namespaces/traversal/linear_scopes.rb +++ b/app/models/namespaces/traversal/linear_scopes.rb @@ -12,14 +12,10 @@ module Namespaces # list of namespace IDs, it can be faster to reference the ID in # traversal_ids than the primary key ID column. def as_ids - return super unless use_traversal_ids? - select(Arel.sql('namespaces.traversal_ids[array_length(namespaces.traversal_ids, 1)]').as('id')) end def roots - return super unless use_traversal_ids? - root_ids = all.select("#{quoted_table_name}.traversal_ids[1]").distinct unscoped.where(id: root_ids) end @@ -37,20 +33,14 @@ module Namespaces end def self_and_descendants(include_self: true) - return super unless use_traversal_ids? - self_and_descendants_with_comparison_operators(include_self: include_self) end def self_and_descendant_ids(include_self: true) - return super unless use_traversal_ids? - self_and_descendants(include_self: include_self).as_ids end def self_and_hierarchy - return super unless use_traversal_ids? - unscoped.from_union([all.self_and_ancestors, all.self_and_descendants(include_self: false)]) end @@ -74,10 +64,6 @@ module Namespaces private - def use_traversal_ids? - Feature.enabled?(:use_traversal_ids) - end - def self_and_ancestors_from_inner_join(include_self: true, upto: nil, hierarchy_order: nil) base_cte = all.reselect('namespaces.traversal_ids').as_cte(:base_ancestors_cte) diff --git a/app/models/preloaders/group_root_ancestor_preloader.rb b/app/models/preloaders/group_root_ancestor_preloader.rb index 29c60e90964..410f48c8176 100644 --- a/app/models/preloaders/group_root_ancestor_preloader.rb +++ b/app/models/preloaders/group_root_ancestor_preloader.rb @@ -8,8 +8,6 @@ module Preloaders end def execute - return unless ::Feature.enabled?(:use_traversal_ids) - # type == 'Group' condition located on subquery to prevent a filter in the query root_query = Namespace.joins("INNER JOIN (#{join_sql}) as root_query ON root_query.root_id = namespaces.id") .select('namespaces.*, root_query.id as source_id') diff --git a/app/models/preloaders/project_root_ancestor_preloader.rb b/app/models/preloaders/project_root_ancestor_preloader.rb index ccb9d2eab98..1e96e139f94 100644 --- a/app/models/preloaders/project_root_ancestor_preloader.rb +++ b/app/models/preloaders/project_root_ancestor_preloader.rb @@ -10,7 +10,6 @@ module Preloaders def execute return unless @projects.is_a?(ActiveRecord::Relation) - return unless ::Feature.enabled?(:use_traversal_ids) root_query = Namespace.joins("INNER JOIN (#{join_sql}) as root_query ON root_query.root_id = namespaces.id") .select('namespaces.*, root_query.id as source_id') diff --git a/app/models/preloaders/user_max_access_level_in_groups_preloader.rb b/app/models/preloaders/user_max_access_level_in_groups_preloader.rb index 16d46facb96..aaa54e0228b 100644 --- a/app/models/preloaders/user_max_access_level_in_groups_preloader.rb +++ b/app/models/preloaders/user_max_access_level_in_groups_preloader.rb @@ -10,27 +10,11 @@ module Preloaders end def execute - if ::Feature.enabled?(:use_traversal_ids) - preload_with_traversal_ids - else - preload_direct_memberships - end + preload_with_traversal_ids end private - def preload_direct_memberships - group_memberships = GroupMember.active_without_invites_and_requests - .where(user: @user, source_id: @groups) - .group(:source_id) - .maximum(:access_level) - - @groups.each do |group| - access_level = group_memberships[group.id] - group.merge_value_to_request_store(User, @user.id, access_level) if access_level.present? - end - end - def preload_with_traversal_ids # Diagrammatic representation of this step: # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111157#note_1271550140 diff --git a/app/models/user.rb b/app/models/user.rb index b76d19240f8..bebd8316352 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2509,14 +2509,6 @@ class User < MainClusterwide::ApplicationRecord def ci_namespace_mirrors_for_group_members(level) search_members = group_members.where('access_level >= ?', level) - # This reduces searched prefixes to only shortest ones - # to avoid querying descendants since they are already covered - # by ancestor namespaces. If the FF is not available fallback to - # inefficient search: https://gitlab.com/gitlab-org/gitlab/-/issues/336436 - unless Feature.enabled?(:use_traversal_ids) - return Ci::NamespaceMirror.contains_any_of_namespaces(search_members.pluck(:source_id)) - end - traversal_ids = Group.joins(:all_group_members) .merge(search_members) .shortest_traversal_ids_prefixes diff --git a/app/views/groups/settings/_git_access_protocols.html.haml b/app/views/groups/settings/_git_access_protocols.html.haml index db177da1d84..d23f72a3055 100644 --- a/app/views/groups/settings/_git_access_protocols.html.haml +++ b/app/views/groups/settings/_git_access_protocols.html.haml @@ -1,7 +1,7 @@ - if group.root? .form-group = f.label _('Enabled Git access protocols'), class: 'label-bold' - = f.select :enabled_git_access_protocol, options_for_select(enabled_git_access_protocol_options_for_group, group.enabled_git_access_protocol), {}, class: 'form-control', data: { qa_selector: 'enabled_git_access_protocol_dropdown' }, disabled: !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank? + = f.select :enabled_git_access_protocol, options_for_select(enabled_git_access_protocol_options_for_group(group), group.enabled_git_access_protocol), {}, class: 'form-control', data: { qa_selector: 'enabled_git_access_protocol_dropdown' }, disabled: !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank? - if !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank? .form-text.text-muted = _("This setting has been configured at the instance level and cannot be overridden per group") diff --git a/config/feature_flags/development/use_traversal_ids.yml b/config/feature_flags/development/enforce_ssh_certificates.yml similarity index 55% rename from config/feature_flags/development/use_traversal_ids.yml rename to config/feature_flags/development/enforce_ssh_certificates.yml index 51f0ba39025..beaa6c02869 100644 --- a/config/feature_flags/development/use_traversal_ids.yml +++ b/config/feature_flags/development/enforce_ssh_certificates.yml @@ -1,8 +1,8 @@ --- -name: use_traversal_ids -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56296 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321948 -milestone: '13.11' +name: enforce_ssh_certificates +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132653 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/426235 +milestone: '16.5' type: development -group: group::tenant scale -default_enabled: true +group: group::source code +default_enabled: false diff --git a/db/docs/scan_result_policy_violations.yml b/db/docs/scan_result_policy_violations.yml new file mode 100644 index 00000000000..bb56c02fb33 --- /dev/null +++ b/db/docs/scan_result_policy_violations.yml @@ -0,0 +1,10 @@ +--- +table_name: scan_result_policy_violations +classes: +- Security::ScanResultPolicyViolation +feature_categories: +- security_policy_management +description: Stores scan result policy violations. +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132254 +milestone: '16.5' +gitlab_schema: gitlab_main diff --git a/db/migrate/20230915111914_create_scan_result_policy_violations.rb b/db/migrate/20230915111914_create_scan_result_policy_violations.rb new file mode 100644 index 00000000000..fc9fce4b2cd --- /dev/null +++ b/db/migrate/20230915111914_create_scan_result_policy_violations.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class CreateScanResultPolicyViolations < Gitlab::Database::Migration[2.1] + enable_lock_retries! + + def up + create_table :scan_result_policy_violations do |t| + t.bigint :scan_result_policy_id, + index: false, + null: false + + t.bigint :merge_request_id, + index: { name: 'index_scan_result_policy_violations_on_merge_request_id' }, + null: false + + t.bigint :project_id, + index: { name: 'index_scan_result_policy_violations_on_project_id' }, + null: false + + t.timestamps_with_timezone null: false + end + + add_index(:scan_result_policy_violations, + %i[scan_result_policy_id merge_request_id], + unique: true, + name: 'index_scan_result_policy_violations_on_policy_and_merge_request') + end + + def down + drop_table :scan_result_policy_violations + end +end diff --git a/db/migrate/20230915111915_add_scan_result_policy_fk_to_scan_result_policy_violations.rb b/db/migrate/20230915111915_add_scan_result_policy_fk_to_scan_result_policy_violations.rb new file mode 100644 index 00000000000..4d7c925e528 --- /dev/null +++ b/db/migrate/20230915111915_add_scan_result_policy_fk_to_scan_result_policy_violations.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddScanResultPolicyFkToScanResultPolicyViolations < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key( + :scan_result_policy_violations, + :scan_result_policies, + column: :scan_result_policy_id, + on_delete: :cascade) + end + + def down + with_lock_retries do + remove_foreign_key(:scan_result_policy_violations, column: :scan_result_policy_id) + end + end +end diff --git a/db/migrate/20230915111916_add_merge_request_fk_to_scan_result_policy_violations.rb b/db/migrate/20230915111916_add_merge_request_fk_to_scan_result_policy_violations.rb new file mode 100644 index 00000000000..d151db82d45 --- /dev/null +++ b/db/migrate/20230915111916_add_merge_request_fk_to_scan_result_policy_violations.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddMergeRequestFkToScanResultPolicyViolations < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key( + :scan_result_policy_violations, + :merge_requests, + column: :merge_request_id, + on_delete: :cascade) + end + + def down + with_lock_retries do + remove_foreign_key(:scan_result_policy_violations, column: :merge_request_id) + end + end +end diff --git a/db/migrate/20230915111917_add_project_fk_to_scan_result_policy_violations.rb b/db/migrate/20230915111917_add_project_fk_to_scan_result_policy_violations.rb new file mode 100644 index 00000000000..005df7be92d --- /dev/null +++ b/db/migrate/20230915111917_add_project_fk_to_scan_result_policy_violations.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddProjectFkToScanResultPolicyViolations < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key( + :scan_result_policy_violations, + :projects, + column: :project_id, + on_delete: :cascade) + end + + def down + with_lock_retries do + remove_foreign_key(:scan_result_policy_violations, column: :project_id) + end + end +end diff --git a/db/post_migrate/20230913071219_delete_pages_domain_with_reserved_domains.rb b/db/post_migrate/20230913071219_delete_pages_domain_with_reserved_domains.rb new file mode 100644 index 00000000000..c4d9d8c8a69 --- /dev/null +++ b/db/post_migrate/20230913071219_delete_pages_domain_with_reserved_domains.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class DeletePagesDomainWithReservedDomains < Gitlab::Database::Migration[2.1] + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + execute <<~SQL.squish + DELETE FROM "pages_domains" + WHERE LOWER("pages_domains"."domain") IN + ('aol.com', 'gmail.com', 'hotmail.co.uk', 'hotmail.com', 'hotmail.fr', 'icloud.com', + 'live.com', 'mail.com', 'me.com', 'msn.com', 'outlook.com', 'proton.me', 'protonmail.com', + 'tutanota.com', 'yahoo.com', 'yandex.com', 'zohomail.com'); + SQL + end + + def down + # no-op + # This migration can't be rolled back as we are deleting entires + end +end diff --git a/db/post_migrate/20231004120426_change_workspaces_force_include_all_resources_default.rb b/db/post_migrate/20231004120426_change_workspaces_force_include_all_resources_default.rb new file mode 100644 index 00000000000..e2ff6f22cfa --- /dev/null +++ b/db/post_migrate/20231004120426_change_workspaces_force_include_all_resources_default.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class ChangeWorkspacesForceIncludeAllResourcesDefault < Gitlab::Database::Migration[2.1] + enable_lock_retries! + + def change + change_column_default(:workspaces, :force_include_all_resources, from: false, to: true) + end +end diff --git a/db/schema_migrations/20230913071219 b/db/schema_migrations/20230913071219 new file mode 100644 index 00000000000..c5c0f2e173f --- /dev/null +++ b/db/schema_migrations/20230913071219 @@ -0,0 +1 @@ +fdc307e3bd5e7762d276219fe877c8b2dd94c90117d914b483c624fc28f4b7dd \ No newline at end of file diff --git a/db/schema_migrations/20230915111914 b/db/schema_migrations/20230915111914 new file mode 100644 index 00000000000..3084763fcae --- /dev/null +++ b/db/schema_migrations/20230915111914 @@ -0,0 +1 @@ +5890e80dd082fbef80ec5772597f90486defd9055b9506e588ac5c63f55d69e7 \ No newline at end of file diff --git a/db/schema_migrations/20230915111915 b/db/schema_migrations/20230915111915 new file mode 100644 index 00000000000..ca33cdb4533 --- /dev/null +++ b/db/schema_migrations/20230915111915 @@ -0,0 +1 @@ +6279203061e730ad1e6b969d55cc7aa78c0cca943beaad66852552f72511aba6 \ No newline at end of file diff --git a/db/schema_migrations/20230915111916 b/db/schema_migrations/20230915111916 new file mode 100644 index 00000000000..02c189f04be --- /dev/null +++ b/db/schema_migrations/20230915111916 @@ -0,0 +1 @@ +4c391db76b7cf1d380beb4317db727d01372da47e7a97be1bf52f9a791ed0b89 \ No newline at end of file diff --git a/db/schema_migrations/20230915111917 b/db/schema_migrations/20230915111917 new file mode 100644 index 00000000000..9dec1e72a75 --- /dev/null +++ b/db/schema_migrations/20230915111917 @@ -0,0 +1 @@ +7f51d5f2f6382dc2b48a766e32b5248846a912a47158872c1c26524127b08c61 \ No newline at end of file diff --git a/db/schema_migrations/20231004120426 b/db/schema_migrations/20231004120426 new file mode 100644 index 00000000000..b81d70e03a6 --- /dev/null +++ b/db/schema_migrations/20231004120426 @@ -0,0 +1 @@ +7d7349723ad38ef6615437d563b01aff39b6eb1c58ec29601e770330d0d5ada6 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 20c03150a5f..0373c51c155 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -22754,6 +22754,24 @@ CREATE SEQUENCE scan_result_policies_id_seq ALTER SEQUENCE scan_result_policies_id_seq OWNED BY scan_result_policies.id; +CREATE TABLE scan_result_policy_violations ( + id bigint NOT NULL, + scan_result_policy_id bigint NOT NULL, + merge_request_id bigint NOT NULL, + project_id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + +CREATE SEQUENCE scan_result_policy_violations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE scan_result_policy_violations_id_seq OWNED BY scan_result_policy_violations.id; + CREATE TABLE schema_migrations ( version character varying NOT NULL, finished_at timestamp with time zone DEFAULT now() @@ -25244,7 +25262,7 @@ CREATE TABLE workspaces ( personal_access_token_id bigint, config_version integer DEFAULT 1 NOT NULL, force_full_reconciliation boolean DEFAULT false NOT NULL, - force_include_all_resources boolean DEFAULT false NOT NULL, + force_include_all_resources boolean DEFAULT true NOT NULL, CONSTRAINT check_15543fb0fa CHECK ((char_length(name) <= 64)), CONSTRAINT check_157d5f955c CHECK ((char_length(namespace) <= 64)), CONSTRAINT check_2b401b0034 CHECK ((char_length(deployment_resource_version) <= 64)), @@ -26736,6 +26754,8 @@ ALTER TABLE ONLY sbom_sources ALTER COLUMN id SET DEFAULT nextval('sbom_sources_ ALTER TABLE ONLY scan_result_policies ALTER COLUMN id SET DEFAULT nextval('scan_result_policies_id_seq'::regclass); +ALTER TABLE ONLY scan_result_policy_violations ALTER COLUMN id SET DEFAULT nextval('scan_result_policy_violations_id_seq'::regclass); + ALTER TABLE ONLY scim_identities ALTER COLUMN id SET DEFAULT nextval('scim_identities_id_seq'::regclass); ALTER TABLE ONLY scim_oauth_access_tokens ALTER COLUMN id SET DEFAULT nextval('scim_oauth_access_tokens_id_seq'::regclass); @@ -29235,6 +29255,9 @@ ALTER TABLE ONLY sbom_sources ALTER TABLE ONLY scan_result_policies ADD CONSTRAINT scan_result_policies_pkey PRIMARY KEY (id); +ALTER TABLE ONLY scan_result_policy_violations + ADD CONSTRAINT scan_result_policy_violations_pkey PRIMARY KEY (id); + ALTER TABLE ONLY schema_migrations ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); @@ -33989,6 +34012,12 @@ CREATE UNIQUE INDEX index_scan_result_policies_on_position_in_configuration ON s CREATE INDEX index_scan_result_policies_on_project_id ON scan_result_policies USING btree (project_id); +CREATE INDEX index_scan_result_policy_violations_on_merge_request_id ON scan_result_policy_violations USING btree (merge_request_id); + +CREATE UNIQUE INDEX index_scan_result_policy_violations_on_policy_and_merge_request ON scan_result_policy_violations USING btree (scan_result_policy_id, merge_request_id); + +CREATE INDEX index_scan_result_policy_violations_on_project_id ON scan_result_policy_violations USING btree (project_id); + CREATE INDEX index_scim_identities_on_group_id ON scim_identities USING btree (group_id); CREATE UNIQUE INDEX index_scim_identities_on_lower_extern_uid_and_group_id ON scim_identities USING btree (lower((extern_uid)::text), group_id); @@ -36710,6 +36739,9 @@ ALTER TABLE ONLY internal_ids ALTER TABLE ONLY incident_management_timeline_events ADD CONSTRAINT fk_17a5fafbd4 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE; +ALTER TABLE ONLY scan_result_policy_violations + ADD CONSTRAINT fk_17ce579abf FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; + ALTER TABLE ONLY incident_management_timeline_events ADD CONSTRAINT fk_1800597ef9 FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL; @@ -37091,6 +37123,9 @@ ALTER TABLE ONLY notes ALTER TABLE ONLY oauth_openid_requests ADD CONSTRAINT fk_77114b3b09 FOREIGN KEY (access_grant_id) REFERENCES oauth_access_grants(id) ON DELETE CASCADE; +ALTER TABLE ONLY scan_result_policy_violations + ADD CONSTRAINT fk_77251168f1 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY agent_user_access_project_authorizations ADD CONSTRAINT fk_78034b05d8 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; @@ -37673,6 +37708,9 @@ ALTER TABLE ONLY status_check_responses ALTER TABLE ONLY design_management_designs_versions ADD CONSTRAINT fk_f4d25ba00c FOREIGN KEY (version_id) REFERENCES design_management_versions(id) ON DELETE CASCADE; +ALTER TABLE ONLY scan_result_policy_violations + ADD CONSTRAINT fk_f53706dbdd FOREIGN KEY (scan_result_policy_id) REFERENCES scan_result_policies(id) ON DELETE CASCADE; + ALTER TABLE ONLY analytics_devops_adoption_segments ADD CONSTRAINT fk_f5aa768998 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; diff --git a/doc/administration/backup_restore/backup_gitlab.md b/doc/administration/backup_restore/backup_gitlab.md index cd65bf67638..05a330bf3f5 100644 --- a/doc/administration/backup_restore/backup_gitlab.md +++ b/doc/administration/backup_restore/backup_gitlab.md @@ -1195,6 +1195,8 @@ There are two ways to fix this: ###### Environment variable overrides +> Multiple databases support was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/133177) in GitLab 16.5. + By default, GitLab uses the database configuration stored in a configuration file (`database.yml`). However, you can override the database settings for the backup and restore task by setting environment @@ -1218,6 +1220,15 @@ and port 5432 with the Linux package (Omnibus): sudo GITLAB_BACKUP_PGHOST=192.168.1.10 GITLAB_BACKUP_PGPORT=5432 /opt/gitlab/bin/gitlab-backup create ``` +If you run GitLab on [multiple databases](../postgresql/multiple_databases.md), you can override database settings by including +the database name in the environment variable. For example if your `main` and `ci` databases are +hosted on different database servers, you would append their name after the `GITLAB_BACKUP_` prefix, +leaving the `PG*` names as is: + +```shell +sudo GITLAB_BACKUP_MAIN_PGHOST=192.168.1.10 GITLAB_BACKUP_CI_PGHOST=192.168.1.12 /opt/gitlab/bin/gitlab-backup create +``` + See the [PostgreSQL documentation](https://www.postgresql.org/docs/12/libpq-envars.html) for more details on what these parameters do. diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b6406210560..031313e7bc6 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -7954,6 +7954,30 @@ The edge type for [`Achievement`](#achievement). | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`Achievement`](#achievement) | The item at the end of the edge. | +#### `AddOnUserConnection` + +The connection type for [`AddOnUser`](#addonuser). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `count` | [`Int!`](#int) | Total count of collection. | +| `edges` | [`[AddOnUserEdge]`](#addonuseredge) | A list of edges. | +| `nodes` | [`[AddOnUser]`](#addonuser) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `AddOnUserEdge` + +The edge type for [`AddOnUser`](#addonuser). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`AddOnUser`](#addonuser) | The item at the end of the edge. | + #### `AgentConfigurationConnection` The connection type for [`AgentConfiguration`](#agentconfiguration). @@ -12854,6 +12878,8 @@ A user with add-on data. | `id` | [`ID!`](#id) | ID of the user. | | `ide` | [`Ide`](#ide) | IDE settings. | | `jobTitle` | [`String`](#string) | Job title of the user. | +| `lastActivityOn` | [`Date`](#date) | Date the user last performed any actions. | +| `lastLoginAt` | [`Time`](#time) | Timestamp of the last sign in. | | `linkedin` | [`String`](#string) | LinkedIn profile name of the user. | | `location` | [`String`](#string) | Location of the user. | | `name` | [`String!`](#string) | Human-readable name of the user. Returns `****` if the user is a project bot and the requester does not have permission to view the project. | @@ -13207,7 +13233,6 @@ Describes an alert from the project's Alert Management. | `issueIid` **{warning-solid}** | [`ID`](#id) | **Deprecated** in 13.10. Use issue field. | | `metricsDashboardUrl` **{warning-solid}** | [`String`](#string) | **Deprecated** in 16.0. Returns no data. Underlying feature was removed in 16.0. | | `monitoringTool` | [`String`](#string) | Monitoring tool the alert came from. | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `prometheusAlert` | [`PrometheusAlert`](#prometheusalert) | Alert condition for Prometheus. | | `runbook` | [`String`](#string) | Runbook for the alert as defined in alert details. | | `service` | [`String`](#string) | Service the alert came from. | @@ -13220,6 +13245,22 @@ Describes an alert from the project's Alert Management. #### Fields with arguments +##### `AlertManagementAlert.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ##### `AlertManagementAlert.todos` To-do items of the current user for the alert. @@ -13905,7 +13946,6 @@ Represents an epic on an issue board. | `iid` | [`ID!`](#id) | Internal ID of the epic. | | `issues` | [`EpicIssueConnection`](#epicissueconnection) | A list of issues associated with the epic. (see [Connections](#connections)) | | `labels` | [`LabelConnection`](#labelconnection) | Labels assigned to the epic. (see [Connections](#connections)) | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `parent` | [`Epic`](#epic) | Parent epic of the epic. | | `participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants for the epic. (see [Connections](#connections)) | | `relationPath` | [`String`](#string) | URI path of the epic-issue relationship. | @@ -14021,6 +14061,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `state` | [`TodoStateEnum`](#todostateenum) | State of the to-do items. | +##### `BoardEpic.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ##### `BoardEpic.reference` Internal reference of the epic. Returned in shortened format by default. @@ -15904,7 +15960,6 @@ A single design. | `image` | [`String!`](#string) | URL of the full-sized image. | | `imageV432x230` | [`String`](#string) | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated. | | `issue` | [`Issue!`](#issue) | Issue the design belongs to. | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `notesCount` | [`Int!`](#int) | Total count of user-created notes for this design. | | `project` | [`Project!`](#project) | Project the design belongs to. | | `webUrl` | [`String!`](#string) | URL of the design. | @@ -15927,6 +15982,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `state` | [`TodoStateEnum`](#todostateenum) | State of the to-do items. | +##### `Design.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ##### `Design.versions` All versions related to this design ordered newest first. @@ -16494,7 +16565,6 @@ Represents an epic. | `iid` | [`ID!`](#id) | Internal ID of the epic. | | `issues` | [`EpicIssueConnection`](#epicissueconnection) | A list of issues associated with the epic. (see [Connections](#connections)) | | `labels` | [`LabelConnection`](#labelconnection) | Labels assigned to the epic. (see [Connections](#connections)) | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `parent` | [`Epic`](#epic) | Parent epic of the epic. | | `participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants for the epic. (see [Connections](#connections)) | | `relationPath` | [`String`](#string) | URI path of the epic-issue relationship. | @@ -16609,6 +16679,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `state` | [`TodoStateEnum`](#todostateenum) | State of the to-do items. | +##### `Epic.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ##### `Epic.reference` Internal reference of the epic. Returned in shortened format by default. @@ -16744,7 +16830,6 @@ Relationship between an epic and an issue. | `milestone` | [`Milestone`](#milestone) | Milestone of the issue. | | `moved` | [`Boolean`](#boolean) | Indicates if issue got moved from other project. | | `movedTo` | [`Issue`](#issue) | Updated Issue after it got moved to another project. | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants in the issue. (see [Connections](#connections)) | | `projectId` | [`Int!`](#int) | ID of the issue project. | | `relatedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests related to the issue. This field can only be resolved for one issue in any single request. (see [Connections](#connections)) | @@ -16828,6 +16913,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `incidentId` | [`IssueID!`](#issueid) | ID of the incident. | +##### `EpicIssue.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ##### `EpicIssue.reference` Internal reference of the issue. Returned in shortened format by default. @@ -17551,6 +17652,27 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `ids` | [`[AchievementsAchievementID!]`](#achievementsachievementid) | Filter achievements by IDs. | +##### `Group.addOnEligibleUsers` + +Users in the namespace hierarchy that add ons are applicable for. This only applies to root namespaces. + +WARNING: +**Introduced** in 16.5. +This feature is an Experiment. It can be changed or removed at any time. + +Returns [`AddOnUserConnection`](#addonuserconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `addOnType` | [`GitlabSubscriptionsAddOnType!`](#gitlabsubscriptionsaddontype) | Type of add on to filter the eligible users by. | +| `search` | [`String`](#string) | Search the user list. | + ##### `Group.addOnPurchase` AddOnPurchase associated with the namespace. @@ -19043,7 +19165,6 @@ Describes an issuable resource link for incident issues. | `milestone` | [`Milestone`](#milestone) | Milestone of the issue. | | `moved` | [`Boolean`](#boolean) | Indicates if issue got moved from other project. | | `movedTo` | [`Issue`](#issue) | Updated Issue after it got moved to another project. | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants in the issue. (see [Connections](#connections)) | | `projectId` | [`Int!`](#int) | ID of the issue project. | | `relatedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests related to the issue. This field can only be resolved for one issue in any single request. (see [Connections](#connections)) | @@ -19126,6 +19247,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `incidentId` | [`IssueID!`](#issueid) | ID of the incident. | +##### `Issue.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ##### `Issue.reference` Internal reference of the issue. Returned in shortened format by default. @@ -19543,7 +19680,6 @@ Defines which user roles, users, or groups can merge into a protected branch. | `mergeableDiscussionsState` | [`Boolean`](#boolean) | Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged. | | `mergedAt` | [`Time`](#time) | Timestamp of when the merge request was merged, null if not merged. | | `milestone` | [`Milestone`](#milestone) | Milestone of the merge request. | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `participants` | [`MergeRequestParticipantConnection`](#mergerequestparticipantconnection) | Participants in the merge request. This includes the author, assignees, reviewers, and users mentioned in notes. (see [Connections](#connections)) | | `preparedAt` | [`Time`](#time) | Timestamp of when the merge request was prepared. | | `project` | [`Project!`](#project) | Alias for target_project. | @@ -19629,6 +19765,22 @@ Returns [`FindingReportsComparer`](#findingreportscomparer). | ---- | ---- | ----------- | | `reportType` | [`ComparableSecurityReportType!`](#comparablesecurityreporttype) | Filter vulnerability findings by report type. | +##### `MergeRequest.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ##### `MergeRequest.pipelines` Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned. @@ -21026,6 +21178,27 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `ids` | [`[AchievementsAchievementID!]`](#achievementsachievementid) | Filter achievements by IDs. | +##### `Namespace.addOnEligibleUsers` + +Users in the namespace hierarchy that add ons are applicable for. This only applies to root namespaces. + +WARNING: +**Introduced** in 16.5. +This feature is an Experiment. It can be changed or removed at any time. + +Returns [`AddOnUserConnection`](#addonuserconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `addOnType` | [`GitlabSubscriptionsAddOnType!`](#gitlabsubscriptionsaddontype) | Type of add on to filter the eligible users by. | +| `search` | [`String`](#string) | Search the user list. | + ##### `Namespace.addOnPurchase` AddOnPurchase associated with the namespace. @@ -24773,7 +24946,6 @@ Represents a snippet entry. | `fileName` | [`String`](#string) | File Name of the snippet. | | `httpUrlToRepo` | [`String`](#string) | HTTP URL to the snippet repository. | | `id` | [`SnippetID!`](#snippetid) | ID of the snippet. | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `project` | [`Project`](#project) | Project the snippet is associated with. | | `rawUrl` | [`String!`](#string) | Raw URL of the snippet. | | `sshUrlToRepo` | [`String`](#string) | SSH URL to the snippet repository. | @@ -24801,6 +24973,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `paths` | [`[String!]`](#string) | Paths of the blobs. | +##### `Snippet.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ### `SnippetBlob` Represents the snippet blob. @@ -25788,7 +25976,6 @@ Represents a vulnerability. | `location` | [`VulnerabilityLocation`](#vulnerabilitylocation) | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability. | | `mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request that fixes the vulnerability. | | `message` **{warning-solid}** | [`String`](#string) | **Deprecated** in 16.1. message field has been removed from security reports schema. | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | `primaryIdentifier` | [`VulnerabilityIdentifier`](#vulnerabilityidentifier) | Primary identifier of the vulnerability. | | `project` | [`Project`](#project) | Project on which the vulnerability was found. | | `reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING, CLUSTER_IMAGE_SCANNING, GENERIC). `Scan Type` in the UI. | @@ -25827,6 +26014,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | `linkType` | [`VulnerabilityIssueLinkType`](#vulnerabilityissuelinktype) | Filter issue links by link type. | +##### `Vulnerability.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | + ### `VulnerabilityContainerImage` Represents a container image reported on the related vulnerability. @@ -27922,6 +28125,14 @@ Geo registry class. | `TERRAFORM_STATE_VERSION_REGISTRY` | Geo::TerraformStateVersionRegistry registry class. | | `UPLOAD_REGISTRY` | Geo::UploadRegistry registry class. | +### `GitlabSubscriptionsAddOnType` + +Types of add-ons. + +| Value | Description | +| ----- | ----------- | +| `CODE_SUGGESTIONS` | Code suggestions seat add-on. | + ### `GitlabSubscriptionsUserRole` Role of User. @@ -30600,7 +30811,24 @@ Implementations: | ---- | ---- | ----------- | | `commenters` | [`UserCoreConnection!`](#usercoreconnection) | All commenters on this noteable. (see [Connections](#connections)) | | `discussions` | [`DiscussionConnection!`](#discussionconnection) | All discussions on this noteable. (see [Connections](#connections)) | -| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | + +##### Fields with arguments + +###### `NoteableInterface.notes` + +All notes on this noteable. + +Returns [`NoteConnection!`](#noteconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `filter` | [`NotesFilterType`](#notesfiltertype) | Type of notes collection: ALL_NOTES, ONLY_COMMENTS, ONLY_ACTIVITY. | #### `OrchestrationPolicy` diff --git a/doc/api/integrations.md b/doc/api/integrations.md index f2f5c1d5dfb..202513482ef 100644 --- a/doc/api/integrations.md +++ b/doc/api/integrations.md @@ -231,9 +231,9 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `new_issue_url` | string | true | New Issue URL | -| `issues_url` | string | true | Issue URL | -| `project_url` | string | true | Project URL | +| `new_issue_url` | string | true | New issue URL. | +| `issues_url` | string | true | Issue URL. | +| `project_url` | string | true | Project URL. | ### Disable Bugzilla @@ -371,7 +371,7 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `confluence_url` | string | true | The URL of the Confluence Cloud Workspace hosted on atlassian.net. | +| `confluence_url` | string | true | The URL of the Confluence Workspace hosted on `atlassian.net`. | ### Disable Confluence Workspace @@ -403,9 +403,9 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `new_issue_url` | string | true | New Issue URL | -| `issues_url` | string | true | Issue URL | -| `project_url` | string | true | Project URL | +| `new_issue_url` | string | true | New issue URL. | +| `issues_url` | string | true | Issue URL. | +| `project_url` | string | true | Project URL. | ### Disable a custom issue tracker @@ -438,12 +438,12 @@ Parameters: | Parameter | Type | Required | Description | |------------------------|---------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `api_key` | string | true | API key used for authentication with Datadog. | -| `api_url` | string | false | (Advanced) The full URL for your Datadog site | -| `datadog_env` | string | false | For self-managed deployments, set the env% tag for all the data sent to Datadog. | -| `datadog_service` | string | false | Tag all data from this GitLab instance in Datadog. Useful when managing several self-managed deployments | -| `datadog_site` | string | false | The Datadog site to send data to. To send data to the EU site, use `datadoghq.eu` | -| `datadog_tags` | string | false | Custom tags in Datadog. Specify one tag per line in the format: `key:value\nkey2:value2` ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79665) in GitLab 14.8.) | -| `archive_trace_events` | boolean | false | When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346339) in GitLab 15.3) | +| `api_url` | string | false | (Advanced) The full URL for your Datadog site. | +| `datadog_env` | string | false | For self-managed deployments, set the `env%` tag for all the data sent to Datadog. | +| `datadog_service` | string | false | Tag all data from this GitLab instance in Datadog. Can be used when managing several self-managed deployments. | +| `datadog_site` | string | false | The Datadog site to send data to. To send data to the EU site, use `datadoghq.eu`. | +| `datadog_tags` | string | false | Custom tags in Datadog. Specify one tag per line in the format `key:value\nkey2:value2` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79665) in GitLab 14.8.). | +| `archive_trace_events` | boolean | false | When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346339) in GitLab 15.3). | ### Disable Datadog @@ -480,24 +480,24 @@ Parameters: | `webhook` | string | true | Discord webhook (for example, `https://discord.com/api/webhooks/…`). | | `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is `default`. | | `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events. | -| `confidential_issue_channel` | string | false | The webhook override to receive confidential issues events notifications. | +| `confidential_issue_channel` | string | false | The webhook override to receive notifications for confidential issue events. | | `confidential_note_events` | boolean | false | Enable notifications for confidential note events. | -| `confidential_note_channel` | string | false | The webhook override to receive confidential note events notifications. | +| `confidential_note_channel` | string | false | The webhook override to receive notifications for confidential note events. | | `issues_events` | boolean | false | Enable notifications for issue events. | -| `issue_channel` | string | false | The webhook override to receive issues events notifications. | +| `issue_channel` | string | false | The webhook override to receive notifications for issue events. | | `merge_requests_events` | boolean | false | Enable notifications for merge request events. | -| `merge_request_channel` | string | false | The webhook override to receive merge request events notifications. | +| `merge_request_channel` | string | false | The webhook override to receive notifications for merge request events. | | `note_events` | boolean | false | Enable notifications for note events. | -| `note_channel` | string | false | The webhook override to receive note events notifications. | +| `note_channel` | string | false | The webhook override to receive notifications for note events. | | `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines. | | `pipeline_events` | boolean | false | Enable notifications for pipeline events. | -| `pipeline_channel` | string | false | The webhook override to receive pipeline events notifications. | +| `pipeline_channel` | string | false | The webhook override to receive notifications for pipeline events. | | `push_events` | boolean | false | Enable notifications for push events. | -| `push_channel` | string | false | The webhook override to receive push events notifications. | +| `push_channel` | string | false | The webhook override to receive notifications for push events. | | `tag_push_events` | boolean | false | Enable notifications for tag push events. | -| `tag_push_channel` | string | false | The webhook override to receive tag push events notifications. | +| `tag_push_channel` | string | false | The webhook override to receive notifications for tag push events. | | `wiki_page_events` | boolean | false | Enable notifications for wiki page events. | -| `wiki_page_channel` | string | false | The webhook override to receive wiki page events notifications. | +| `wiki_page_channel` | string | false | The webhook override to receive notifications for wiki page events. | ### Disable Discord Notifications @@ -637,7 +637,7 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `external_wiki_url` | string | true | The URL of the external wiki | +| `external_wiki_url` | string | true | The URL of the external wiki. | ### Disable an external wiki @@ -707,33 +707,33 @@ Parameters: | `username` | string | false | username. | | `channel` | string | false | Default channel to use if others are not configured. | | `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines. | -| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified`. | +| `notify_only_default_branch` | boolean | false | **Deprecated:** This parameter has been replaced with `branches_to_be_notified`. | | `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is `default`. | -| `alert_channel` | string | false | The name of the channel to receive alert events notifications. | +| `alert_channel` | string | false | The name of the channel to receive notifications for alert events. | | `alert_events` | boolean | false | Enable notifications for alert events. | | `commit_events` | boolean | false | Enable notifications for commit events. | -| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications. | +| `confidential_issue_channel` | string | false | The name of the channel to receive notifications for confidential issue events. | | `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events. | -| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications. | +| `confidential_note_channel` | string | false | The name of the channel to receive notifications for confidential note events. | | `confidential_note_events` | boolean | false | Enable notifications for confidential note events. | -| `deployment_channel` | string | false | The name of the channel to receive deployment events notifications. | +| `deployment_channel` | string | false | The name of the channel to receive notifications for deployment events. | | `deployment_events` | boolean | false | Enable notifications for deployment events. | -| `incident_channel` | string | false | The name of the channel to receive incidents events notifications. | +| `incident_channel` | string | false | The name of the channel to receive notifications for incident events. | | `incidents_events` | boolean | false | Enable notifications for incident events. | -| `issue_channel` | string | false | The name of the channel to receive issues events notifications. | +| `issue_channel` | string | false | The name of the channel to receive notifications for issue events. | | `issues_events` | boolean | false | Enable notifications for issue events. | | `job_events` | boolean | false | Enable notifications for job events. | -| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications. | +| `merge_request_channel` | string | false | The name of the channel to receive notifications for merge request events. | | `merge_requests_events` | boolean | false | Enable notifications for merge request events. | -| `note_channel` | string | false | The name of the channel to receive note events notifications. | +| `note_channel` | string | false | The name of the channel to receive notifications for note events. | | `note_events` | boolean | false | Enable notifications for note events. | -| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications. | +| `pipeline_channel` | string | false | The name of the channel to receive notifications for pipeline events. | | `pipeline_events` | boolean | false | Enable notifications for pipeline events. | -| `push_channel` | string | false | The name of the channel to receive push events notifications. | +| `push_channel` | string | false | The name of the channel to receive notifications for push events. | | `push_events` | boolean | false | Enable notifications for push events. | -| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications. | +| `tag_push_channel` | string | false | The name of the channel to receive notifications for tag push events. | | `tag_push_events` | boolean | false | Enable notifications for tag push events. | -| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications. | +| `wiki_page_channel` | string | false | The name of the channel to receive notifications for wiki page events. | | `wiki_page_events` | boolean | false | Enable notifications for wiki page events. | ### Disable the GitLab for Slack app @@ -768,7 +768,7 @@ Parameters: | --------- | ---- | -------- | ----------- | | `webhook` | string | true | The Hangouts Chat webhook (for example, `https://chat.googleapis.com/v1/spaces...`). | | `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines. | -| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified`. | +| `notify_only_default_branch` | boolean | false | **Deprecated:** This parameter has been replaced with `branches_to_be_notified`. | | `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is `default`. | | `push_events` | boolean | false | Enable notifications for push events. | | `issues_events` | boolean | false | Enable notifications for issue events. | @@ -810,11 +810,11 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `recipients` | string | true | Recipients/channels separated by whitespaces | -| `default_irc_uri` | string | false | `irc://irc.network.net:6697/` | -| `server_host` | string | false | localhost | -| `server_port` | integer | false | 6659 | -| `colorize_messages` | boolean | false | Colorize messages | +| `recipients` | string | true | Recipients or channels separated by whitespaces. | +| `default_irc_uri` | string | false | `irc://irc.network.net:6697/`. | +| `server_host` | string | false | localhost. | +| `server_port` | integer | false | 6659. | +| `colorize_messages` | boolean | false | Colorize messages. | ### Disable irker @@ -938,7 +938,7 @@ Parameters: | `jira_issue_transition_id` | string | no | The ID of one or more transitions for [custom issue transitions](../integration/jira/issues.md#custom-issue-transitions). Ignored if `jira_issue_transition_automatic` is enabled. Defaults to a blank string, which disables custom transitions. | | `commit_events` | boolean | false | Enable notifications for commit events. | | `merge_requests_events` | boolean | false | Enable notifications for merge request events. | -| `comment_on_event_enabled` | boolean | false | Enable comments inside Jira issues on each GitLab event (commit / merge request). | +| `comment_on_event_enabled` | boolean | false | Enable comments in Jira issues on each GitLab event (commit or merge request). | ### Disable Jira @@ -974,7 +974,7 @@ Parameters: | `username` | string | false | username. | | `channel` | string | false | Default channel to use if others are not configured. | | `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines. | -| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified`. | +| `notify_only_default_branch` | boolean | false | **Deprecated:** This parameter has been replaced with `branches_to_be_notified`. | | `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is `default`. | | `push_events` | boolean | false | Enable notifications for push events. | | `issues_events` | boolean | false | Enable notifications for issue events. | @@ -985,15 +985,15 @@ Parameters: | `confidential_note_events` | boolean | false | Enable notifications for confidential note events. | | `pipeline_events` | boolean | false | Enable notifications for pipeline events. | | `wiki_page_events` | boolean | false | Enable notifications for wiki page events. | -| `push_channel` | string | false | The name of the channel to receive push events notifications. | -| `issue_channel` | string | false | The name of the channel to receive issues events notifications. | -| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications. | -| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications. | -| `note_channel` | string | false | The name of the channel to receive note events notifications. | -| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications. | -| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications. | -| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications. | -| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications. | +| `push_channel` | string | false | The name of the channel to receive notifications for push events. | +| `issue_channel` | string | false | The name of the channel to receive notifications for issue events. | +| `confidential_issue_channel` | string | false | The name of the channel to receive notifications for confidential issue events. | +| `merge_request_channel` | string | false | The name of the channel to receive notifications for merge request events. | +| `note_channel` | string | false | The name of the channel to receive notifications for note events. | +| `confidential_note_channel` | string | false | The name of the channel to receive notifications for confidential note events. | +| `tag_push_channel` | string | false | The name of the channel to receive notifications for tag push events. | +| `pipeline_channel` | string | false | The name of the channel to receive notifications for pipeline events. | +| `wiki_page_channel` | string | false | The name of the channel to receive notifications for wiki page events. | ### Disable Mattermost notifications @@ -1059,7 +1059,7 @@ Parameters: | --------- | ---- | -------- | ----------- | | `webhook` | string | true | The Microsoft Teams webhook (for example, `https://outlook.office.com/webhook/...`). | | `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines. | -| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified`. | +| `notify_only_default_branch` | boolean | false | **Deprecated:** This parameter has been replaced with `branches_to_be_notified`. | | `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is `default`. | | `push_events` | boolean | false | Enable notifications for push events. | | `issues_events` | boolean | false | Enable notifications for issue events. | @@ -1175,9 +1175,9 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `recipients` | string | yes | Comma-separated list of recipient email addresses. | -| `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines. | +| `notify_only_broken_pipelines` | boolean | no | Send notifications for broken pipelines. | | `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are `all`, `default`, `protected`, and `default_and_protected`. The default value is `default`. | -| `notify_only_default_branch` | boolean | no | Send notifications only for the default branch. | +| `notify_only_default_branch` | boolean | no | Send notifications for the default branch. | | `pipeline_events` | boolean | false | Enable notifications for pipeline events. | ### Disable pipeline status emails @@ -1322,7 +1322,7 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `new_issue_url` | string | true | New Issue URL. | +| `new_issue_url` | string | true | New issue URL. | | `project_url` | string | true | Project URL. | | `issues_url` | string | true | Issue URL. | @@ -1416,7 +1416,7 @@ Parameters: | Parameter | Type | Required | Description | |-------------------------|--------|----------|-------------------------------| | `url` | string | yes | URL of the Squash TM webhook. | -| `token` | string | no | Optional token | +| `token` | string | no | Optional token. | ### Disable Squash TM @@ -1449,17 +1449,17 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `token` | string | true | The Telegram bot token (for example, `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`). | -| `room` | string | true | Unique identifier for the target chat or the username of the target channel (in the format `@channelusername`) | -| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines | -| `push_events` | boolean | true | Enable notifications for push events | -| `issues_events` | boolean | true | Enable notifications for issue events | -| `confidential_issues_events` | boolean | true | Enable notifications for confidential issue events | -| `merge_requests_events` | boolean | true | Enable notifications for merge request events | -| `tag_push_events` | boolean | true | Enable notifications for tag push events | -| `note_events` | boolean | true | Enable notifications for note events | -| `confidential_note_events` | boolean | true | Enable notifications for confidential note events | -| `pipeline_events` | boolean | true | Enable notifications for pipeline events | -| `wiki_page_events` | boolean | true | Enable notifications for wiki page events | +| `room` | string | true | Unique identifier for the target chat or the username of the target channel (in the format `@channelusername`). | +| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines. | +| `push_events` | boolean | true | Enable notifications for push events. | +| `issues_events` | boolean | true | Enable notifications for issue events. | +| `confidential_issues_events` | boolean | true | Enable notifications for confidential issue events. | +| `merge_requests_events` | boolean | true | Enable notifications for merge request events. | +| `tag_push_events` | boolean | true | Enable notifications for tag push events. | +| `note_events` | boolean | true | Enable notifications for note events. | +| `confidential_note_events` | boolean | true | Enable notifications for confidential note events. | +| `pipeline_events` | boolean | true | Enable notifications for pipeline events. | +| `wiki_page_events` | boolean | true | Enable notifications for wiki page events. | ### Disable Telegram diff --git a/doc/api/notes.md b/doc/api/notes.md index 3df8b787667..18f2c08869e 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -8,10 +8,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w Notes are comments on: -- Snippets -- Issues -- Merge requests +- [Commits](../user/project/repository/index.md#commit-changes-to-a-repository) - [Epics](../user/group/epics/index.md) +- [Issues](../user/project/issues/index.md) +- [Merge requests](../user/project/merge_requests/index.md) +- [Snippets](../user/snippets.md) This includes system notes, which are notes about changes to the object (for example, when an assignee changes, GitLab posts a system note). diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md index 0af1cc84878..0a530cc60fe 100644 --- a/doc/ci/migration/jenkins.md +++ b/doc/ci/migration/jenkins.md @@ -7,81 +7,469 @@ type: index, howto # Migrating from Jenkins **(FREE ALL)** -If you're migrating from Jenkins to GitLab CI/CD, you should be able -to create CI/CD pipelines that do everything you need. +If you're migrating from Jenkins to GitLab CI/CD, you are able to create CI/CD +pipelines that replicate and enhance your Jenkins workflows. -You can start by watching the [Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF5Y) -video for examples of: +## Key similarities and differences -- Converting a Jenkins pipeline into a GitLab CI/CD pipeline. -- Using Auto DevOps to test your code automatically. +GitLab CI/CD and Jenkins are CI/CD tools with some similarities. Both GitLab +and Jenkins: -## Get started +- Use stages for collections of jobs. +- Support container-based builds. -The following list of recommended steps was created after observing organizations -that were able to quickly complete this migration. +Additionally, there are some important differences between the two: -Before doing any migration work, you should [start with a migration plan](plan_a_migration.md). +- GitLab CI/CD pipelines are all configured in a YAML format configuration file. + Jenkins uses either a Groovy format configuration file (declarative pipelines) + or Jenkins DSL (scripted pipelines). +- GitLab can run either on SaaS (cloud) or self-managed deployments. Jenkins deployments must be self-managed. +- GitLab provides source code management (SCM) out of the box. Jenkins requires a separate + SCM solution to store code. +- GitLab provides a built-in container registry. Jenkins requires a separate solution + for storing container images. +- GitLab provides built-in templates for scanning code. Jenkins requires 3rd party plugins + for scanning code. -Engineers that need to migrate projects to GitLab CI/CD should first review general -information about GitLab CI/CD: +## Comparison of features and concepts -- Read about some [key GitLab CI/CD features](#key-gitlab-cicd-features). -- Follow tutorials to create: - - [Your first GitLab pipeline](../quick_start/index.md). - - [A more complex pipeline](../quick_start/tutorial.md) that builds, tests, - and deploys a static site. -- Review the [`.gitlab-ci.yml` keyword reference](../yaml/index.md). +Many Jenkins features and concepts have equivalents in GitLab that offer the same +functionality. -Then you can begin the process of migrating to GitLab CI/CD: +### Configuration file -- Ensure [runners](../runners/index.md) are available, either by using shared GitLab.com runners - or installing new runners. -- Migrate build and CI jobs and configure them to show results directly in merge requests. - You can use [Auto DevOps](../../topics/autodevops/index.md) as a starting point, - and [customize](../../topics/autodevops/customize.md) or [decompose](../../topics/autodevops/customize.md#use-individual-components-of-auto-devops) - the configuration as needed. -- Migrate deployment jobs by using [cloud deployment templates](../cloud_deployment/index.md), - [environments](../environments/index.md), and the [GitLab agent for Kubernetes](../../user/clusters/agent/index.md). -- Check if any CI/CD configuration can be reused across different projects, then create - and share [templates](#templates). -- Check the [pipeline efficiency documentation](../pipelines/pipeline_efficiency.md) - to learn how to make your GitLab CI/CD pipelines faster and more efficient. +Jenkins can be configured with a [`Jenkinsfile` in the Groovy format](https://www.jenkins.io/doc/book/pipeline/jenkinsfile/). GitLab CI/CD uses a [`.gitlab-ci.yml` YAML file](../../ci/yaml/gitlab_ci_yaml.md) by default. -If you have questions that are not answered here, the [GitLab community forum](https://forum.gitlab.com/) -can be a great resource. +Example of a `Jenkinsfile`: -### Key GitLab CI/CD features +```groovy +pipeline { + agent any -GitLab CI/CD key features might be different or not exist in Jenkins. For example, -in GitLab: + stages { + stage('hello') { + steps { + echo "Hello World" + } + } + } +} +``` -- Pipelines can be triggered with: - - A Git push - - A [Schedule](../pipelines/schedules.md) - - The [GitLab UI](../pipelines/index.md#run-a-pipeline-manually) - - An [API call](../triggers/index.md) - - A [webhook](../triggers/index.md#use-a-webhook) -- You can control which jobs run in which cases with the [`rules` syntax](../yaml/index.md#rules). -- You can reuse pipeline configurations: - - Use the [`extends` keyword](../yaml/index.md#extends) to reuse configuration - in a single pipeline configuration. - - Use the [`include` keyword](../yaml/index.md#include) to reuse configuration across - multiple pipelines and projects. -- Jobs are grouped into stages, and jobs in the same stage can run at the same time. - Stages run in sequence. Jobs can be configured to run outside of the stage ordering with the - [`needs` keyword](../yaml/index.md#needs). -- The [`parallel`](../yaml/index.md#parallel) keyword can automatically parallelize tasks, - especially tests that support parallelization. -- Jobs run independently of each other and have a fresh environment for each job. - Passing artifacts between jobs is controlled using the [`artifacts`](../yaml/index.md#artifacts) - and [`dependencies`](../yaml/index.md#dependencies) keywords. -- The `.gitlab-ci.yml` configuration file exists in your Git repository, like a `Jenkinsfile`, - but is [a YAML file](#yaml-configuration-file), not Groovy. -- GitLab comes with a [container registry](../../user/packages/container_registry/index.md). - You can build and store custom container images to run your jobs in. +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: -## Runners +```yaml +stages: + - hello + +hello-job: + stage: hello + script: + - echo "Hello World" +``` + +### Jenkins pipeline syntax + +A Jenkins configuration is composed of a `pipeline` block with "sections" and "directives". +GitLab CI/CD has similar functionality, configured with YAML keywords. + +#### Sections + +| Jenkins | GitLab | Explanation | +|----------|----------------|-------------| +| `agent` | `image` | Jenkins pipelines execute on agents, and the `agent` section defines how the pipeline executes, and the Docker container to use. GitLab jobs execute on _runners_, and the `image` keyword defines the container to use. You can configure your own runners in Kubernetes or on any host. | +| `post` | `after_script` or `stage` | The Jenkins `post` section defines actions that should be performed at the end of a stage or pipeline. In GitLab, use `after_script` for commands to run at the end of a job, and `before_script` for actions to run before the other commands in a job. Use `stage` to select the exact stage a job should run in. GitLab supports both `.pre` and `.post` stages that always run before or after all other defined stages. | +| `stages` | `stages` | Jenkins stages are groups of jobs. GitLab CI/CD also uses stages, but it is more flexible. You can have multiple stages each with multiple independent jobs. Use `stages` at the top level to the stages and their execution order, and use `stage` at the job level to define the stage for that job. | +| `steps` | `script` | Jenkins `steps` define what to execute. GitLab CI/CD uses a `script` section which is similar. The `script` section is a YAML array with separate entries for each command to run in sequence. | + +#### Directives + +| Jenkins | GitLab | Explanation | +|---------------|----------------|-------------| +| `environment` | `variables` | Jenkins uses `environment` for environment variables. GitLab CI/CD uses the `variables` keyword to define CI/CD variables that can be used during job execution, but also for more dynamic pipeline configuration. These can also be set in the GitLab UI, under CI/CD settings. | +| `options` | Not applicable | Jenkins uses `options` for additional configuration, including timeouts and retry values. GitLab does not need a separate section for options, all configuration is added as CI/CD keywords at the job or pipeline level, for example `timeout` or `retry`. | +| `parameters` | Not applicable | In Jenkins, parameters can be required when triggering a pipeline. Parameters are handled in GitLab with CI/CD variables, which can be defined in many places, including the pipeline configuration, project settings, at runtime manually through the UI, or API. | +| `triggers` | `rules` | In Jenkins, `triggers` defines when a pipeline should run again, for example through cron notation. GitLab CI/CD can run pipelines automatically for many reasons, including Git changes and merge request updates. Use the `rules` keyword to control which events to run jobs for. Scheduled pipelines are defined in the project settings. | +| `tools` | Not applicable | In Jenkins, `tools` defines additional tools to install in the environment. GitLab does not have a similar keyword, as the recommendation is to use container images prebuilt with the exact tools required for your jobs. These images can be cached and can be built to already contain the tools you need for your pipelines. If a job needs additional tools, they can be installed as part of a `before_script` section. | +| `input` | Not applicable | In Jenkins, `input` adds a prompt for user input. Similar to `parameters`, inputs are handled in GitLab through CI/CD variables. | +| `when` | `rules` | In Jenkins, `when` defines when a stage should be executed. GitLab also has a `when` keyword, which defines whether a job should start running based on the status of earlier jobs, for example if jobs passed or failed. To control when to add jobs to specific pipelines, use `rules`. | + +### Common configurations + +This section goes over commonly used CI/CD configurations, showing how they can be converted +from Jenkins to GitLab CI/CD. + +[Jenkins pipelines](https://www.jenkins.io/doc/book/pipeline/) generate automated CI/CD jobs +that are triggered when certain event take place, such as a new commit being pushed. +A Jenkins pipeline is defined in a `Jenkinsfile`. The GitLab equivalent is the [`.gitlab-ci.yml` configuration file](../../ci/yaml/gitlab_ci_yaml.md). + +Jenkins does not provide a place to store source code, so the `Jenkinsfile` must be stored +in a separate source control repository. + +#### Jobs + +Jobs are a set of commands that run in a set sequence to achieve a particular result. + +For example, build a container then deploy it to production, in a `Jenkinsfile`: + +```groovy +pipeline { + agent any + stages { + stage('build') { + agent { docker 'golang:alpine' } + steps { + apk update + go build -o bin/hello + } + post { + always { + archiveArtifacts artifacts: 'bin/hello' + onlyIfSuccessful: true + } + } + } + stage('deploy') { + agent { docker 'golang:alpine' } + when { + branch 'staging' + } + steps { + echo "Deploying to staging" + scp bin/hello remoteuser@remotehost:/remote/directory + } + } + } +} +``` + +This example: + +- Uses the `golang:alpine` container image. +- Runs a job for building code. + - Stores the built executable as an artifact. +- Adds a second job to deploy to `staging`, which: + - Only exists if the commit targets the `staging` branch. + - Starts after the build stage succeeds. + - Uses the built executable artifact from the earlier job. + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +default: + image: golang:alpine + +stages: + - build + - deploy + +build-job: + stage: build + script: + - apk update + - go build -o bin/hello + artifacts: + paths: + - bin/hello + expire_in: 1 week + +deploy-job: + stage: deploy + script: + - echo "Deploying to Staging" + - scp bin/hello remoteuser@remotehost:/remote/directory + rules: + - if: $CI_COMMIT_BRANCH == 'staging' + artifacts: + paths: + - bin/hello +``` + +##### Parallel + +In Jenkins, jobs that are not dependent on previous jobs can run in parallel when +added to a `parallel` section. + +For example, in a `Jenkinsfile`: + +```groovy +pipeline { + agent any + stages { + stage('Parallel') { + parallel { + stage('Python') { + agent { docker 'python:latest' } + steps { + sh "python --version" + } + } + stage('Java') { + agent { docker 'openjdk:latest' } + when { + branch 'staging' + } + steps { + sh "java -version" + } + } + } + } + } +} +``` + +This example runs a Python and a Java job in parallel, using different container images. +The Java job only runs when the `staging` branch is changed. + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +python-version: + image: python:latest + script: + - python --version + +java-version: + image: openjdk:latest + rules: + - if: $CI_COMMIT_BRANCH == 'staging' + script: + - java -version +``` + +In this case, no extra configuration is needed to make the jobs run in parallel. +Jobs run in parallel by default, each on a different runner assuming there are enough runners +for all the jobs. The Java job is set to only run when the `staging` branch is changed. + +##### Matrix + +In GitLab you can use a matrix to run a job multiple times in parallel in a single pipeline, +but with different variable values for each instance of the job. Jenkins runs the matrix sequentially. + +For example, in a `Jenkinsfile`: + +```groovy +matrix { + axes { + axis { + name 'PLATFORM' + values 'linux', 'mac', 'windows' + } + axis { + name 'ARCH' + values 'x64', 'x86' + } + } + stages { + stage('build') { + echo "Building $PLATFORM for $ARCH" + } + stage('test') { + echo "Building $PLATFORM for $ARCH" + } + stage('deploy') { + echo "Building $PLATFORM for $ARCH" + } + } +} +``` + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +stages: + - build + - test + - deploy + +.parallel-hidden-job: + parallel: + matrix: + - PLATFORM: [linux, mac, windows] + ARCH: [x64, x86] + +build-job: + extends: .parallel-hidden-job + stage: build + script: + - echo "Building $PLATFORM for $ARCH" + +test-job: + extends: .parallel-hidden-job + stage: test + script: + - echo "Testing $PLATFORM for $ARCH" + +deploy-job: + extends: .parallel-hidden-job + stage: deploy + script: + - echo "Testing $PLATFORM for $ARCH" +``` + +#### Container Images + +In GitLab you can [run your CI/CD jobs in separate, isolated Docker containers](../../ci/docker/using_docker_images.md) +using the [image](../../ci/yaml/index.md#image) keyword. + +For example, in a `Jenkinsfile`: + +```groovy +stage('Version') { + agent { docker 'python:latest' } + steps { + echo 'Hello Python' + sh 'python --version' + } +} +``` + +This example shows commands running in a `python:latest` container. + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +version-job: + image: python:latest + script: + - echo "Hello Python" + - python --version +``` + +#### Variables + +In GitLab, use the `variables` keyword to define [CI/CD variables](../variables/index.md). +Use variables to reuse configuration data, have more dynamic configuration, or store important values. +Variables can be defined either globally or per job. + +For example, in a `Jenkinsfile`: + +```groovy +pipeline { + agent any + environment { + NAME = 'Fern' + } + stages { + stage('English') { + environment { + GREETING = 'Hello' + } + steps { + sh 'echo "$GREETING $NAME"' + } + } + stage('Spanish') { + environment { + GREETING = 'Hola' + } + steps { + sh 'echo "$GREETING $NAME"' + } + } + } +} +``` + +This example shows how variables can be used to pass values to commands in jobs. + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +default: + image: alpine:latest + +stages: + - greet + +variables: + NAME: "Fern" + +english: + stage: greet + variables: + GREETING: "Hello" + script: + - echo "$GREETING $NAME" + +spanish: + stage: greet + variables: + GREETING: "Hola" + script: + - echo "$GREETING $NAME" +``` + +Variables can also be [set in the GitLab UI, in the CI/CD settings](../variables/index.md#define-a-cicd-variable-in-the-ui). +In some cases, you can use [protected](../variables/index.md#protect-a-cicd-variable) +and [masked](../variables/index.md#mask-a-cicd-variable) variables for secret values. +These variables can be accessed in pipeline jobs the same as variables defined in the +configuration file. + +For example, in a `Jenkinsfile`: + +```groovy +pipeline { + agent any + stages { + stage('Example Username/Password') { + environment { + AWS_ACCESS_KEY = credentials('aws-access-key') + } + steps { + sh 'my-login-script.sh $AWS_ACCESS_KEY' + } + } + } +} +``` + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +login-job: + script: + - my-login-script.sh $AWS_ACCESS_KEY +``` + +Additionally, GitLab CI/CD makes [predefined variables](../variables/predefined_variables.md) +available to every pipeline and job which contain values relevant to the pipeline and repository. + +#### Expressions and conditionals + +When a new pipeline starts, GitLab checks which jobs should run in that pipeline. +You can configure jobs to run depending on factors like the status of variables, +or the pipeline type. + +For example, in a `Jenkinsfile`: + +```groovy +stage('deploy_staging') { + agent { docker 'alpine:latest' } + when { + branch 'staging' + } + steps { + echo "Deploying to staging" + } +} +``` + +In this example, the job only runs when the branch we are committing to is named `staging`. + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +deploy_staging: + stage: deploy + script: + - echo "Deploy to staging server" + rules: + - if: '$CI_COMMIT_BRANCH == staging' +``` + +#### Runners Like Jenkins agents, GitLab runners are the hosts that run jobs. If you are using GitLab.com, you can use the [shared runner fleet](../runners/index.md) to run jobs without provisioning @@ -99,233 +487,144 @@ Some key details about runners: for finer control, and associate runners with specific jobs. For example, you can use a tag for jobs that require dedicated, more powerful, or specific hardware. - GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/configuration/autoscale.html). - Use autoscaling to provision runners only when needed and scale down when not needed, - similar to ephemeral agents in Jenkins. + Use autoscaling to provision runners only when needed and scale down when not needed. -## YAML configuration file +For example, in a `Jenkinsfile`: -GitLab pipeline configuration files use the [YAML](https://yaml.org/) format instead of -the [Groovy](https://groovy-lang.org/) format that Jenkins uses. - -Using YAML is a strength of GitLab CI/CD, as it is a simple format to understand -and start using. For example, a small configuration file with two jobs and some -shared configuration in a hidden job: - -```yaml -.test-config: - tags: - - docker-runners - stage: test - -test-job: - extends: - - .test-config - script: - - bundle exec rake rspec - -lint-job: - extends: - - .test-config - script: - - yarn run prettier +```groovy +pipeline { + agent none + stages { + stage('Linux') { + agent { + label 'linux' + } + steps { + echo "Hello, $USER" + } + } + stage('Windows') { + agent { + label 'windows' + } + steps { + echo "Hello, %USERNAME%" + } + } + } +} ``` -In this example: - -- The commands to run in jobs are added with the [`script` keyword](../yaml/index.md#script). -- The [`extends` keyword](../yaml/index.md#extends) reduces duplication in the configuration - by adding the same `tags` and `stage` configuration defined in `.test-config` to both jobs. - -### Artifacts - -In GitLab, any job can use the [`artifacts` keyword](../yaml/index.md#artifacts) -to define a set of [artifacts](../jobs/job_artifacts.md) to be stored when a job completes. -Artifacts are files that can be used in later jobs, for example for testing or deployment. - -For example: +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: ```yaml -pdf: - script: xelatex mycv.tex +linux_job: + stage: build + tags: + - linux + script: + - echo "Hello, $USER" + +windows_job: + stage: build + tags: + - windows + script: + - echo "Hello, %USERNAME%" +``` + +#### Artifacts + +In GitLab, any job can use the [`artifacts`](../../ci/yaml/index.md#artifacts) keyword to define a set of artifacts to +be stored when a job completes. [Artifacts](../../ci/jobs/job_artifacts.md) are files that can be used in later jobs, +for example for testing or deployment. + +For example, in a `Jenkinsfile`: + +```groovy +stages { + stage('Generate Cat') { + steps { + sh 'touch cat.txt' + sh 'echo "meow" > cat.txt' + } + post { + always { + archiveArtifacts artifacts: 'cat.txt' + onlyIfSuccessful: true + } + } + } + stage('Use Cat') { + steps { + sh 'cat cat.txt' + } + } + } +``` + +The equivalent GitLab CI/CD `.gitlab-ci.yml` file would be: + +```yaml +stages: + - generate + - use + +generate_cat: + stage: generate + script: + - touch cat.txt + - echo "meow" > cat.txt artifacts: paths: - - mycv.pdf - - output/ + - cat.txt expire_in: 1 week -``` -In this example: - -- The `mycv.pdf` file and all the files in `output/` are stored and could be used - in later jobs. -- To save resources, the artifacts expire and are deleted after one week. - -### Scanning features - -You might have used plugins for things like code quality, security, or static application scanning -in Jenkins. Tools like these are already available in GitLab and can be used in your -pipeline. - -GitLab features including [code quality](../testing/code_quality.md), [security scanning](../../user/application_security/index.md), -[SAST](../../user/application_security/sast/index.md), and many others generate reports -when they complete. These reports can be displayed in merge requests and pipeline details pages. - -### Templates - -For organizations with many CI/CD pipelines, you can use project templates to configure -custom CI/CD configuration templates and reuse them across projects. - -Group maintainers can configure a group to use as the source for [custom project templates](../../administration/custom_project_templates.md). -These templates can be used by all projects in the group. - -An instance administrator can set a group as the source for [instance project templates](../../user/group/custom_project_templates.md), -which can be used by all projects in that instance. - -## Convert a declarative Jenkinsfile - -A declarative Jenkinsfile contains "Sections" and "Directives" which are used to control the behavior of your -pipelines. Equivalents for all of these exist in GitLab, which we've documented below. - -This section is based on the [Jenkinsfile syntax documentation](https://www.jenkins.io/doc/book/pipeline/syntax/) -and is meant to be a mapping of concepts there to concepts in GitLab. - -### Sections - -#### `agent` - -The agent section is used to define how a pipeline executes. For GitLab, we use [runners](../runners/index.md) -to provide this capability. You can configure your own runners in Kubernetes or on any host. You can also take advantage -of our shared runner fleet (the shared runner fleet is only available for GitLab.com users). -We also support using [tags](../runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) to direct different jobs -to different runners (execution agents). - -The `agent` section also allows you to define which Docker images should be used for execution, for which we use -the [`image`](../yaml/index.md#image) keyword. The `image` can be set on a single job or at the top level, in which -case it applies to all jobs in the pipeline: - -```yaml -my_job: - image: alpine -``` - -#### `post` - -The `post` section defines the actions that should be performed at the end of the pipeline. GitLab also supports -this through the use of stages. You can define your stages as follows, and any jobs assigned to the `before_pipeline` -or `after_pipeline` stages run as expected. You can call these stages anything you like: - -```yaml -stages: - - before_pipeline - - build - - test - - deploy - - after_pipeline -``` - -Setting a step to be performed before and after any job can be done via the -[`before_script`](../yaml/index.md#before_script) and [`after_script`](../yaml/index.md#after_script) keywords: - -```yaml -default: - before_script: - - echo "I run before any jobs starts in the entire pipeline, and can be responsible for setting up the environment." -``` - -#### `stages` - -GitLab CI/CD also lets you define stages, but is a little bit more free-form to configure. The GitLab [`stages` keyword](../yaml/index.md#stages) -is a top level setting that enumerates the list of stages. You are not required to nest individual jobs underneath -the `stages` section. Any job defined in the `.gitlab-ci.yml` can be made a part of any stage through use of the -[`stage` keyword](../yaml/index.md#stage). - -Unless otherwise specified, every pipeline is instantiated with a `build`, `test`, and `deploy` stage -which are run in that order. Jobs that have no `stage` defined are placed by default in the `test` stage. -Of course, each job that refers to a stage must refer to a stage that exists in the pipeline configuration. - -```yaml -stages: - - build - - test - - deploy - -my_job: - stage: build -``` - -#### `steps` - -The `steps` section is equivalent to the [`script` section](../yaml/index.md#script) of an individual job. The `steps` section is a YAML array -with each line representing an individual command to be run: - -```yaml -my_job: +use_cat: + stage: use script: - - echo "hello! the current time is:" - - time + - cat cat.txt + artifacts: + paths: + - cat.txt ``` -### Directives +#### Caching -#### `environment` +A [cache](../../ci/caching/index.md) is created when a job downloads one or more files and +saves them for faster access in the future. Subsequent jobs that use the same cache don't have to download the files again, +so they execute more quickly. The cache is stored on the runner and uploaded to S3 if +[distributed cache is enabled](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching). +Jenkins core does not provide caching. -In GitLab, we use the [`variables` keyword](../yaml/index.md#variables) to define different variables at runtime. -These can also be set up through the GitLab UI, under CI/CD settings. See also our [general documentation on variables](../variables/index.md), -including the section on [protected variables](../variables/index.md#protect-a-cicd-variable). This can be used -to limit access to certain variables to certain environments or runners: +For example, in a `.gitlab-ci.yml` file: ```yaml -variables: - POSTGRES_USER: user - POSTGRES_PASSWORD: testing_password -``` - -#### `options` - -Here, options for different things exist associated with the object in question itself. For example, options related -to jobs are defined in relation to the job itself. If you're looking for a certain option, you should be able to find -where it's located by searching our [complete configuration reference](../yaml/index.md) page. - -#### `parameters` - -GitLab does not require you to define which variables you want to be available when starting a manual job. A user -can provide any variables they like. - -#### `triggers` / `cron` - -Because GitLab is integrated tightly with Git, SCM polling options for triggers are not needed. We support a -[syntax for scheduling pipelines](../pipelines/schedules.md). - -#### `tools` - -GitLab does not support a separate `tools` directive. Our best-practice recommendation is to use pre-built -container images. These images can be cached and can be built to already contain the tools you need for your pipelines. Pipelines can -be set up to automatically build these images as needed and deploy them to the [container registry](../../user/packages/container_registry/index.md). - -If you don't use container images with Docker or Kubernetes, but use the `shell` executor on your own system, -you must set up your environment. You can set up the environment in advance, or as part of the jobs -with a `before_script` action that handles this for you. - -#### `input` - -Similar to the `parameters` keyword, this is not needed because a manual job can always be provided runtime -variable entry. - -#### `when` - -GitLab does support a [`when` keyword](../yaml/index.md#when) which is used to indicate when a job should be -run in case of (or despite) failure. Most of the logic for controlling pipelines can be found in -our very powerful [`rules` system](../yaml/index.md#rules): - -```yaml -my_job: +cache-job: script: - - echo - rules: - - if: $CI_COMMIT_BRANCH + - echo "This job uses a cache." + cache: + key: binaries-cache-$CI_COMMIT_REF_SLUG + paths: + - binaries/ ``` -## Secrets Management +### Security Scanning features + +You might have used plugins for things like code quality, security, or static application scanning in Jenkins. +GitLab provides [security scanners](../../user/application_security/index.md) out-of-the-box to detect +vulnerabilities in all parts of the SDLC. You can add these plugins in GitLab using templates, for example to add +SAST scanning to your pipeline, add the following to your `.gitlab-ci.yml`: + +```yaml +include: + - template: Security/SAST.gitlab-ci.yml +``` + +You can customize the behavior of security scanners by using CI/CD variables, for example +with the [SAST scanners](../../user/application_security/sast/index.md#available-cicd-variables). + +### Secrets Management Privileged information, often referred to as "secrets", is sensitive information or credentials you need in your CI/CD workflow. You might use secrets to unlock protected resources @@ -358,15 +657,59 @@ only be done in [the project, group, or instance settings](../variables/index.md Review the [security guidelines](../variables/index.md#cicd-variable-security) to improve the safety of your CI/CD variables. -## Additional resources +## Planning and Performing a Migration + +The following list of recommended steps was created after observing organizations +that were able to quickly complete this migration. + +### Create a Migration Plan + +Before starting a migration you should create a [migration plan](plan_a_migration.md) to make preparations for the migration. For a migration from Jenkins, ask yourself the following questions in preparation: + +- What plugins are used by jobs in Jenkins today? + - Do you know what these plugins do exactly? + - Do any plugins wrap a common build tool? For example, Maven, Gradle, or NPM? +- What is installed on the Jenkins agents? +- Are there any shared libraries in use? +- How are you authenticating from Jenkins? Are you using SSH keys, API tokens, or other secrets? +- Are there other projects that you need to access from your pipeline? +- Are there credentials in Jenkins to access outside services? For example Ansible Tower, + Artifactory, or other Cloud Providers or deployment targets? + +### Prerequisites + +Before doing any migration work, you should first: + +1. Get familiar with GitLab. + - Read about the [key GitLab CI/CD features](../../ci/index.md). + - Follow tutorials to create [your first GitLab pipeline](../quick_start/index.md) and [more complex pipelines](../quick_start/tutorial.md) that build, test, and deploys a static site. + - Review the [`.gitlab-ci.yml` keyword reference](../yaml/index.md). +1. Set up and configure GitLab. +1. Test your GitLab instance. + - Ensure [runners](../runners/index.md) are available, either by using shared GitLab.com runners or installing new runners. + +### Migration Steps + +1. Migrate projects from your SCM solution to GitLab. + - (Recommended) You can use the available [importers](../../user/project/import/index.md) + to automate mass imports from external SCM providers. + - You can [import repositories by URL](../../user/project/import/repo_by_url.md). +1. Create a `.gitlab-ci.yml` file in each project. +1. Migrate Jenkins configuration to GitLab CI/CD jobs and configure them to show results directly in merge requests. +1. Migrate deployment jobs by using [cloud deployment templates](../cloud_deployment/index.md), + [environments](../environments/index.md), and the [GitLab agent for Kubernetes](../../user/clusters/agent/index.md). +1. Check if any CI/CD configuration can be reused across different projects, then create + and share CI/CD templates. +1. Check the [pipeline efficiency documentation](../pipelines/pipeline_efficiency.md) + to learn how to make your GitLab CI/CD pipelines faster and more efficient. + +### Additional Resources - You can use the [JenkinsFile Wrapper](https://gitlab.com/gitlab-org/jfr-container-builder/) - to run a complete Jenkins instance inside of a GitLab CI/CD job, including plugins. Use this tool to - help ease the transition to GitLab CI/CD, by delaying the migration of less urgent pipelines. + to run a complete Jenkins instance inside of a GitLab CI/CD job, including plugins. Use this tool to help ease the transition to GitLab CI/CD, by delaying the migration of less urgent pipelines. NOTE: The JenkinsFile Wrapper is not packaged with GitLab and falls outside of the scope of support. For more information, see the [Statement of Support](https://about.gitlab.com/support/statement-of-support/). -- If your tooling outputs packages that you want to make accessible, you can store them - in a [package registry](../../user/packages/index.md). -- Use [review Apps](../review_apps/index.md) to preview changes before merging them. + +If you have questions that are not answered here, the [GitLab community forum](https://forum.gitlab.com/) can be a great resource. diff --git a/doc/ci/migration/plan_a_migration.md b/doc/ci/migration/plan_a_migration.md index 85775d882f3..22c4645d6c2 100644 --- a/doc/ci/migration/plan_a_migration.md +++ b/doc/ci/migration/plan_a_migration.md @@ -55,21 +55,6 @@ the migration requirements: - How do you deploy your code? - Where do you deploy your code? -### Jenkins - -If you are [migrating from Jenkins](jenkins.md), these additional questions can help with planning -the migration: - -- What plugins are used by jobs in Jenkins today? - - Do you know what these plugins do exactly? - - Do any plugin wrap a common build tool? For example, Maven, Gradle, or NPM? -- What is installed on the Jenkins agents? -- Are there any shared libraries in use? -- How are you authenticating from Jenkins? Are you using SSH keys, API tokens, or other secrets? -- Are there other projects that you need to access from your pipeline? -- Are there credentials in Jenkins to access outside services? For example Ansible Tower, - Artifactory, or other Cloud Providers or deployment targets? - ## Related topics - How to migrate Atlassian Bamboo Server's CI/CD infrastructure to GitLab CI/CD, [part one](https://about.gitlab.com/blog/2022/07/06/migration-from-atlassian-bamboo-server-to-gitlab-ci/) diff --git a/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md b/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md index a42f2ec8d61..7eaa25b7b1f 100644 --- a/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md +++ b/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md @@ -31,6 +31,11 @@ bin/rails g gitlab:analytics:internal_events \ --mr=https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 ``` +Where: + +- `time_frames`: Valid options are `7d` and `28d` if you provide a `unique` value and `all` for metrics without `unique`. We are working to make `7d` and `28d` work for metrics with `all` time frame in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/411264). +- `unique`: Valid options are `user.id`, `project.id`, and `namespace.id`, as they are logged as part of the standard context. We [are actively working](https://gitlab.com/gitlab-org/gitlab/-/issues/411255) on a way to define uniqueness on arbitrary properties sent with the event, such as `merge_request.id`. + ## Trigger events Triggering an event and thereby updating a metric is slightly different on backend and frontend. Please refer to the relevant section below. @@ -135,7 +140,3 @@ Sometimes we want to send internal events when the component is rendered or load = render Pajamas::ButtonComponent.new(button_options: { data: { event_tracking_load: 'true', event_tracking: 'i_devops' } }) do = _("New project") ``` - -### Limitations - -The only values we allow for `unique` are `user.id`, `project.id`, and `namespace.id`, as they are logged as part of the standard context. We currently don't have anywhere to put a value like `merge_request.id`. That will change with self-describing events. diff --git a/doc/install/installation.md b/doc/install/installation.md index 84e41b2dd8f..e6c65eaddb2 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -293,20 +293,20 @@ In GitLab 12.1 and later, only PostgreSQL is supported. In GitLab 16.0 and later 1. Install the database packages. - For Ubuntu 20.04 and later: + For Ubuntu 22.04 and later: ```shell sudo apt install -y postgresql postgresql-client libpq-dev postgresql-contrib ``` - For Ubuntu 18.04 and earlier, the available PostgreSQL doesn't meet the minimum + For Ubuntu 20.04 and earlier, the available PostgreSQL doesn't meet the minimum version requirement. You must add PostgreSQL's repository: ```shell + sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - - sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' - sudo apt update - sudo apt -y install postgresql-12 postgresql-client-12 libpq-dev + sudo apt-get update + sudo apt-get -y install postgresql-14 ``` 1. Verify the PostgreSQL version you have is supported by the version of GitLab you're diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index b5860666e27..f919f584c82 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -6,6 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Dependency Scanning **(ULTIMATE ALL)** + +For an interactive reading and how-to demo of this Dependency Scanning doc, see [How to use dependency scanning tutorial hands-on GitLab Application Security part 3](https://youtu.be/ii05cMbJ4xQ?feature=shared) + +For an interactive reading and how-to demo playlist, see [Get Started With GitLab Application Security Playlist](https://www.youtube.com/playlist?list=PL05JrBw4t0KrUrjDoefSkgZLx5aJYFaF9) + Dependency Scanning analyzes your application's dependencies for known vulnerabilities. All dependencies are scanned, including transitive dependencies, also known as nested dependencies. diff --git a/doc/user/application_security/get-started-security.md b/doc/user/application_security/get-started-security.md index ad49f00a1bd..3e73fbc5955 100644 --- a/doc/user/application_security/get-started-security.md +++ b/doc/user/application_security/get-started-security.md @@ -8,6 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w For an overview, see [Adopting GitLab application security](https://www.youtube.com/watch?v=5QlxkiKR04k). + +For an interactive reading and how-to demo playlist, see [Get Started With GitLab Application Security Playlist](https://www.youtube.com/playlist?list=PL05JrBw4t0KrUrjDoefSkgZLx5aJYFaF9) The following steps help you get the most from GitLab application security tools. These steps are a recommended order of operations. You can choose to implement capabilities in a different order or omit features that do not apply to your specific needs. diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md index a10c029bec6..18016f6f342 100644 --- a/doc/user/application_security/secret_detection/index.md +++ b/doc/user/application_security/secret_detection/index.md @@ -15,6 +15,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w > `secret_detection_default_branch` and `secret_detection` were consolidated into one job, > `secret_detection`. + +For an interactive reading and how-to demo of this Secret Detection doc, see [How to enable secret detection in GitLab Application Security Part 1/2](https://youtu.be/dbMxeO6nJCE?feature=shared) and [How to enable secret detection in GitLab Application Security Part 2/2](https://youtu.be/VL-_hdiTazo?feature=shared) + +For an interactive reading and how-to demo playlist, see [Get Started With GitLab Application Security Playlist](https://www.youtube.com/playlist?list=PL05JrBw4t0KrUrjDoefSkgZLx5aJYFaF9) + People sometimes accidentally commit secrets like keys or API tokens to Git repositories. After a sensitive value is pushed to a remote repository, anyone with access to the repository can impersonate the authorized user of the secret for malicious purposes. Most organizations require exposed secrets to be revoked and replaced to address this risk. diff --git a/doc/user/group/access_and_permissions.md b/doc/user/group/access_and_permissions.md index 428c87143f6..560656c1224 100644 --- a/doc/user/group/access_and_permissions.md +++ b/doc/user/group/access_and_permissions.md @@ -134,9 +134,11 @@ can be added to the group. The most popular public email domains cannot be restricted, such as: -- `gmail.com`, `yahoo.com`, `aol.com`, `icloud.com` -- `hotmail.com`, `hotmail.co.uk`, `hotmail.fr` -- `msn.com`, `live.com`, `outlook.com` +- `aol.com`, `gmail.com`, `hotmail.co.uk`, `hotmail.com`, +- `hotmail.fr`, `icloud.com`, `live.com`, `mail.com`, +- `me.com`, `msn.com`, `outlook.com`, +- `proton.me`, `protonmail.com`, `tutanota.com`, +- `yahoo.com`, `yandex.com`, `zohomail.com` When you share a group, both the source and target namespaces must allow the domains of the members' email addresses. diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md index cf6ee61660f..3ed33f9c947 100644 --- a/doc/user/profile/account/delete_account.md +++ b/doc/user/profile/account/delete_account.md @@ -61,13 +61,18 @@ When deleting users, you can either: - Delete the user and their contributions, including: - Abuse reports. - Emoji reactions. - - Epics. - Groups of which the user is the only user with the Owner role. + - Personal access tokens. + - Epics. - Issues. - Merge requests. - - Notes and comments. - - Personal access tokens. - Snippets. + - [Notes and comments](../../../api/notes.md) + on other users' [commits](../../project/repository/index.md#commit-changes-to-a-repository), + [epics](../../group/epics/index.md), + [issues](../../project/issues/index.md), + [merge requests](../../project/merge_requests/index.md) + and [snippets](../../snippets.md). An alternative to deleting is [blocking a user](../../../administration/moderate_users.md#block-a-user). diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md index d8e4fce41ef..1c2b342f5d3 100644 --- a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md +++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md @@ -19,6 +19,9 @@ To use one or more custom domain names: - Add a [custom **root domain** or a **subdomain**](#set-up-a-custom-domain). - Add [SSL/TLS certification](#adding-an-ssltls-certificate-to-pages). +WARNING: +You cannot verify the [most popular public email domains](../../../../user/group/access_and_permissions.md#restrict-group-access-by-domain). + ## Set up a custom domain To set up Pages with a custom domain name, read the requirements and steps below. diff --git a/lib/backup/database_model.rb b/lib/backup/database_model.rb index 6129a3ce891..b2202ad7794 100644 --- a/lib/backup/database_model.rb +++ b/lib/backup/database_model.rb @@ -16,6 +16,8 @@ module Backup sslcompression: 'PGSSLCOMPRESSION' }.freeze + OVERRIDE_PREFIX = "GITLAB_BACKUP_" + attr_reader :config def initialize(name) @@ -35,7 +37,7 @@ module Backup original_config = source_model.connection_db_config.configuration_hash.dup - @config = config_for_backup(original_config) + @config = config_for_backup(name, original_config) @model.establish_connection( ActiveRecord::DatabaseConfigurations::HashConfig.new( @@ -56,7 +58,7 @@ module Backup self.class.const_set(klass_name, Class.new(ApplicationRecord)) end - def config_for_backup(config) + def config_for_backup(name, config) db_config = { activerecord: config, pg_env: {} @@ -65,8 +67,9 @@ module Backup # This enables the use of different PostgreSQL settings in # case PgBouncer is used. PgBouncer clears the search path, # which wreaks havoc on Rails if connections are reused. - override = "GITLAB_BACKUP_#{arg}" - val = ENV[override].presence || config[opt].to_s.presence + override_all = "#{OVERRIDE_PREFIX}#{arg}" + override_db = "#{OVERRIDE_PREFIX}#{name.upcase}_#{arg}" + val = ENV[override_db].presence || ENV[override_all].presence || config[opt].to_s.presence next unless val diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fcfe1ff6060..db45e3087a8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -32760,6 +32760,9 @@ msgstr "" msgid "Only SSH" msgstr "" +msgid "Only SSH Certificates" +msgstr "" + msgid "Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user." msgstr "" @@ -54473,6 +54476,9 @@ msgstr "" msgid "You cannot set yourself to awaiting" msgstr "" +msgid "You cannot verify %{value} because it is a popular public email domain." +msgstr "" + msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead." msgstr "" diff --git a/qa/qa/page/component/import/gitlab.rb b/qa/qa/page/component/import/gitlab.rb index 5831a713ae5..1cb8a099a70 100644 --- a/qa/qa/page/component/import/gitlab.rb +++ b/qa/qa/page/component/import/gitlab.rb @@ -32,7 +32,7 @@ module QA click_element(:import_project_button) wait_until(reload: false) do - has_notice?("The project was successfully imported.") || has_element?(:project_name_content) + has_notice?("The project was successfully imported.") || has_element?('project-name-content') end end end diff --git a/qa/qa/page/project/web_ide/vscode.rb b/qa/qa/page/project/web_ide/vscode.rb index cde311db3af..74194b85ebe 100644 --- a/qa/qa/page/project/web_ide/vscode.rb +++ b/qa/qa/page/project/web_ide/vscode.rb @@ -12,12 +12,12 @@ module QA end def has_file_explorer? - page.has_css?('.explorer-folders-view', visible: true) - page.has_css?('[aria-label="Files Explorer"]', visible: true) + has_element?('div[aria-label="Files Explorer"]') end def right_click_file_explorer - page.find('.explorer-folders-view', visible: true).right_click + has_element?('div.monaco-list-rows') + find_element('div[aria-label="Files Explorer"]').right_click end def open_file_from_explorer(file_name) @@ -32,78 +32,69 @@ module QA within_element('.monaco-editor', &block) end - def has_new_folder_menu_item? - page.has_css?('[aria-label="New Folder..."]', visible: true) + def has_right_click_menu_item? + has_element?('div.menu-item-check') end def click_new_folder_menu_item - page.find('[aria-label="New Folder..."]').click - end - - def enter_new_folder_text_input(name) - page.find('.explorer-item-edited', visible: true) - send_keys(name, :enter) - end - - def has_upload_menu_item? - page.has_css?('.menu-item-check', visible: true) + click_element('span[aria-label="New Folder..."]') end def click_upload_menu_item - page.find('[aria-label="Upload..."]', visible: true).click + click_element('span[aria-label="Upload..."]') + end + + def enter_new_folder_text_input(name) + find_element('input[type="text"]') + send_keys(name, :enter) end def enter_file_input(file) - page.find('input[type="file"]', visible: false).send_keys(file) + find_element('input[type="file"]', visible: false).send_keys(file) end def has_commit_pending_tab? - page.has_css?('.scm-viewlet-label', visible: true) + has_element?('.scm-viewlet-label') end def click_commit_pending_tab - page.find('.scm-viewlet-label', visible: true).click + click_element('.scm-viewlet-label', visible: true) end def click_commit_tab - page.find('a.codicon-source-control-view-icon', visible: true).click + click_element('.codicon-source-control-view-icon') end def has_commit_message_box? - page.has_css?('div.view-lines.monaco-mouse-cursor-text', visible: true) + has_element?('div[aria-label="Source Control Input"]') end def enter_commit_message(message) - page.find('div.view-lines.monaco-mouse-cursor-text', visible: true).send_keys(message) - end - - def click_commit_button - page.find('a.monaco-text-button', visible: true).click - end - - def has_notification_box? - page.has_css?('a.monaco-text-button', visible: true) - end - - def create_merge_request - Support::Waiter.wait_until(max_duration: 10, retry_on_exception: true) do - within_vscode_editor do - page.find('.monaco-button[title="Create MR"]').click - end + within_element('div[aria-label="Source Control Input"]') do + find_element('.view-line').click + send_keys(message) end end + def click_commit_button + click_element('div[aria-label="Commit to \'main\'"]') + end + + def has_notification_box? + has_element?('.monaco-dialog-box') + end + def click_new_branch - page.find('.monaco-button[title="Create new branch"]').click + click_element('.monaco-button[title="Create new branch"]') end def has_branch_input_field? - page.has_css?('.monaco-findInput', visible: true) + has_element?('input[aria-label="input"]') end def has_message?(content) within_vscode_editor do - page.has_content?(content) + has_text?(content) end end @@ -133,15 +124,14 @@ module QA def create_new_folder(name) within_vscode_editor do - right_click_file_explorer - has_new_folder_menu_item? - # Use for stability, WebIDE inside an iframe is finnicky, webdriver sometimes moves too fast Support::Waiter.wait_until(max_duration: 20, retry_on_exception: true) do + right_click_file_explorer + has_right_click_menu_item? click_new_folder_menu_item # Verify New Folder button is triggered and textbox is waiting for input enter_new_folder_text_input(name) - page.has_content?(name) + has_text?(name) end end end @@ -160,8 +150,8 @@ module QA end has_commit_message_box? - send_keys(message) - page.has_content?(message) + enter_commit_message(message) + has_text?(message) click_commit_button has_notification_box? end @@ -176,8 +166,14 @@ module QA end end + def create_merge_request + within_vscode_editor do + has_element?('div[title="GitLab Web IDE Extension (Extension)"]') + click_element('.monaco-button[title="Create MR"]') + end + end + def upload_file(file_path) - wait_for_ide_to_load within_vscode_editor do # VSCode eagerly removes the input[type='file'] from click on Upload. # We need to execute a script on the iframe to stub out the iframes body.removeChild to add it back in. @@ -186,32 +182,40 @@ module QA # Use for stability, WebIDE inside an iframe is finnicky, webdriver sometimes moves too fast Support::Waiter.wait_until(max_duration: 20, retry_on_exception: true) do right_click_file_explorer - has_upload_menu_item? + has_right_click_menu_item? click_upload_menu_item enter_file_input(file_path) end + # Wait for the file to be uploaded + has_text?(file_path) end end def add_file_content(prompt_data) - click_inside_editor_frame - within_file_editor do - send_keys(:enter, :enter, prompt_data) + within_vscode_editor do + click_inside_editor_frame + within_file_editor do + send_keys(:enter, :enter, prompt_data) + end end end def verify_prompt_appears_and_accept(pattern) - within_file_editor do - Support::Waiter.wait_until(max_duration: 30) do - page.text.match?(pattern) + within_vscode_editor do + within_file_editor do + Support::Waiter.wait_until(max_duration: 30) do + page.text.match?(pattern) + end + send_keys(:tab) end - send_keys(:tab) end end def validate_prompt(pattern) - within_file_editor do - page.text.match?(pattern) + within_vscode_editor do + within_file_editor do + page.text.match?(pattern) + end end end end diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb index 02e69544250..4d1cad4c4f9 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb @@ -8,7 +8,7 @@ module QA type: :flaky } do describe 'Add a directory in Web IDE' do - let(:project) { create(:project, :with_readme, name: 'add-directory-project') } + let(:project) { create(:project, :with_readme, name: 'webide-add-directory-project') } before do Flow::Login.sign_in @@ -24,14 +24,15 @@ module QA ]) project.visit! + Page::Project::Show.perform(&:open_web_ide!) + Page::Project::WebIDE::VSCode.perform(&:wait_for_ide_to_load) end it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/386760' do - Page::Project::Show.perform(&:open_web_ide!) Page::Project::WebIDE::VSCode.perform do |ide| - ide.wait_for_ide_to_load ide.create_new_folder(directory_name) - ide.has_message?('A file or folder first_directory already exists at this location.') + + expect(ide).to have_message('A file or folder first_directory already exists at this location.') end end end @@ -41,15 +42,16 @@ module QA before do Page::Project::Show.perform(&:open_web_ide!) + Page::Project::WebIDE::VSCode.perform(&:wait_for_ide_to_load) end it 'shows successfully but not able to be committed', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/386761' do Page::Project::WebIDE::VSCode.perform do |ide| - ide.wait_for_ide_to_load ide.create_new_folder(directory_name) ide.commit_toggle(directory_name) - ide.has_message?('No changes found. Not able to commit.') + + expect(ide).to have_message('No changes found. Not able to commit.') end end end diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb index afed95e18f9..2049ef78962 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb @@ -9,38 +9,42 @@ module QA } do describe 'Upload a file in Web IDE' do let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', file_name)) } - let(:project) { create(:project, :with_readme, name: 'upload-file-project') } + let(:project) { create(:project, :with_readme, name: 'webide-upload-file-project') } before do Flow::Login.sign_in project.visit! + Page::Project::Show.perform(&:open_web_ide!) + Page::Project::WebIDE::VSCode.perform(&:wait_for_ide_to_load) end context 'when a file with the same name already exists' do let(:file_name) { 'README.md' } it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/390005' do - Page::Project::Show.perform(&:open_web_ide!) Page::Project::WebIDE::VSCode.perform do |ide| ide.upload_file(file_path) - ide.has_message?("A file or folder with the name 'README.md' already exists in the destination folder") + + expect(ide) + .to have_message("A file or folder with the name 'README.md' already exists in the destination folder") end end end shared_examples 'upload a file' do it "verifies it successfully uploads and commits to a MR" do - Page::Project::Show.perform(&:open_web_ide!) Page::Project::WebIDE::VSCode.perform do |ide| ide.upload_file(file_path) - ide.has_message?(file_name) ide.commit_and_push(file_name) - ide.has_message?('Success! Your changes have been committed.') + + expect(ide).to have_message('Success! Your changes have been committed.') + ide.create_merge_request end # Opens the MR in new tab and verify the file is in the MR page.driver.browser.switch_to.window(page.driver.browser.window_handles.last) + Page::MergeRequest::Show.perform do |merge_request| expect(merge_request).to have_content(file_name) end diff --git a/scripts/build_gdk_image b/scripts/build_gdk_image index cb1dbd03adb..3401c8df86c 100755 --- a/scripts/build_gdk_image +++ b/scripts/build_gdk_image @@ -26,6 +26,7 @@ docker buildx build \ --platform=${ARCH:-amd64} \ --tag="${IMAGE}:${SHA_TAG}" \ --tag="${IMAGE}:${BRANCH_TAG}" \ + --provenance=false \ ${OUTPUT_OPTION} \ . diff --git a/scripts/build_qa_image b/scripts/build_qa_image index 9c401718336..23a003e2b01 100755 --- a/scripts/build_qa_image +++ b/scripts/build_qa_image @@ -90,5 +90,6 @@ docker buildx build \ --build-arg=QA_BUILD_TARGET="${QA_BUILD_TARGET}" \ --file="${CI_PROJECT_DIR}/qa/Dockerfile" \ --push \ + --provenance=false \ ${DESTINATIONS} \ ${CI_PROJECT_DIR} diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index dce64280988..0db15541b99 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GroupsHelper do +RSpec.describe GroupsHelper, feature_category: :groups_and_projects do include ApplicationHelper include AvatarsHelper @@ -97,23 +97,11 @@ RSpec.describe GroupsHelper do end end - context 'recursive' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - include_examples 'correct ancestor order' + before do + very_deep_nested_group.reload # make sure traversal_ids are reloaded end - context 'linear' do - before do - stub_feature_flags(use_traversal_ids: true) - - very_deep_nested_group.reload # make sure traversal_ids are reloaded - end - - include_examples 'correct ancestor order' - end + include_examples 'correct ancestor order' end it 'enqueues the elements in the breadcrumb schema list' do @@ -269,21 +257,7 @@ RSpec.describe GroupsHelper do end end - context 'recursive' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - include_examples 'correct ancestor order' - end - - context 'linear' do - before do - stub_feature_flags(use_traversal_ids: true) - end - - include_examples 'correct ancestor order' - end + include_examples 'correct ancestor order' end end @@ -558,22 +532,24 @@ RSpec.describe GroupsHelper do end describe "#enabled_git_access_protocol_options_for_group" do - subject { helper.enabled_git_access_protocol_options_for_group } + let_it_be(:group) { create(:group) } + + subject { helper.enabled_git_access_protocol_options_for_group(group) } before do - expect(::Gitlab::CurrentSettings).to receive(:enabled_git_access_protocol).and_return(instance_setting) + allow(::Gitlab::CurrentSettings).to receive(:enabled_git_access_protocol).and_return(instance_setting) end context "instance setting is nil" do let(:instance_setting) { nil } - it { is_expected.to contain_exactly([_("Both SSH and HTTP(S)"), "all"], [_("Only SSH"), "ssh"], [_("Only HTTP(S)"), "http"]) } + it { is_expected.to include([_("Both SSH and HTTP(S)"), "all"], [_("Only SSH"), "ssh"], [_("Only HTTP(S)"), "http"]) } end context "instance setting is blank" do - let(:instance_setting) { nil } + let(:instance_setting) { '' } - it { is_expected.to contain_exactly([_("Both SSH and HTTP(S)"), "all"], [_("Only SSH"), "ssh"], [_("Only HTTP(S)"), "http"]) } + it { is_expected.to include([_("Both SSH and HTTP(S)"), "all"], [_("Only SSH"), "ssh"], [_("Only HTTP(S)"), "http"]) } end context "instance setting is ssh" do diff --git a/spec/lib/backup/database_model_spec.rb b/spec/lib/backup/database_model_spec.rb index 5758ad2c1aa..c9d036b37f8 100644 --- a/spec/lib/backup/database_model_spec.rb +++ b/spec/lib/backup/database_model_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Backup::DatabaseModel, :reestablished_active_record_base, feature_category: :backup_restore do + using RSpec::Parameterized::TableSyntax + let(:gitlab_database_name) { 'main' } describe '#connection' do @@ -30,7 +32,7 @@ RSpec.describe Backup::DatabaseModel, :reestablished_active_record_base, feature ).to receive(:configuration_hash).and_return(application_config) end - context 'when no GITLAB_BACKUP_PG* variables are set' do + shared_examples 'no configuration is overridden' do it 'ActiveRecord backup configuration is expected to equal application configuration' do expect(subject[:activerecord]).to eq(application_config) end @@ -45,9 +47,23 @@ RSpec.describe Backup::DatabaseModel, :reestablished_active_record_base, feature end end - context 'when GITLAB_BACKUP_PG* variables are set' do - using RSpec::Parameterized::TableSyntax + shared_examples 'environment variables override application configuration' do + let(:active_record_key) { described_class::SUPPORTED_OVERRIDES.invert[pg_env] } + it 'ActiveRecord backup configuration overrides application configuration' do + expect(subject[:activerecord]).to eq(application_config.merge(active_record_key => overridden_value)) + end + + it 'PostgreSQL ENV overrides application configuration' do + expect(subject[:pg_env]).to include({ pg_env => overridden_value }) + end + end + + context 'when no GITLAB_BACKUP_PG* variables are set' do + it_behaves_like 'no configuration is overridden' + end + + context 'when GITLAB_BACKUP_PG* variables are set' do where(:env_variable, :overridden_value) do 'GITLAB_BACKUP_PGHOST' | 'test.invalid.' 'GITLAB_BACKUP_PGUSER' | 'some_user' @@ -63,18 +79,76 @@ RSpec.describe Backup::DatabaseModel, :reestablished_active_record_base, feature with_them do let(:pg_env) { env_variable[/GITLAB_BACKUP_(\w+)/, 1] } - let(:active_record_key) { described_class::SUPPORTED_OVERRIDES.invert[pg_env] } before do stub_env(env_variable, overridden_value) end - it 'ActiveRecord backup configuration overrides application configuration' do - expect(subject[:activerecord]).to eq(application_config.merge(active_record_key => overridden_value)) + it_behaves_like 'environment variables override application configuration' + end + end + + context 'when GITLAB_BACKUP__PG* variables are set' do + context 'and environment variables are for the current database name' do + where(:env_variable, :overridden_value) do + 'GITLAB_BACKUP_MAIN_PGHOST' | 'test.invalid.' + 'GITLAB_BACKUP_MAIN_PGUSER' | 'some_user' + 'GITLAB_BACKUP_MAIN_PGPORT' | '1543' + 'GITLAB_BACKUP_MAIN_PGPASSWORD' | 'secret' + 'GITLAB_BACKUP_MAIN_PGSSLMODE' | 'allow' + 'GITLAB_BACKUP_MAIN_PGSSLKEY' | 'some_key' + 'GITLAB_BACKUP_MAIN_PGSSLCERT' | '/path/to/cert' + 'GITLAB_BACKUP_MAIN_PGSSLROOTCERT' | '/path/to/root/cert' + 'GITLAB_BACKUP_MAIN_PGSSLCRL' | '/path/to/crl' + 'GITLAB_BACKUP_MAIN_PGSSLCOMPRESSION' | '1' end - it 'PostgreSQL ENV overrides application configuration' do - expect(subject[:pg_env]).to include({ pg_env => overridden_value }) + with_them do + let(:pg_env) { env_variable[/GITLAB_BACKUP_MAIN_(\w+)/, 1] } + + before do + stub_env(env_variable, overridden_value) + end + + it_behaves_like 'environment variables override application configuration' + end + end + + context 'and environment variables are for another database' do + where(:env_variable, :overridden_value) do + 'GITLAB_BACKUP_CI_PGHOST' | 'test.invalid.' + 'GITLAB_BACKUP_CI_PGUSER' | 'some_user' + 'GITLAB_BACKUP_CI_PGPORT' | '1543' + 'GITLAB_BACKUP_CI_PGPASSWORD' | 'secret' + 'GITLAB_BACKUP_CI_PGSSLMODE' | 'allow' + 'GITLAB_BACKUP_CI_PGSSLKEY' | 'some_key' + 'GITLAB_BACKUP_CI_PGSSLCERT' | '/path/to/cert' + 'GITLAB_BACKUP_CI_PGSSLROOTCERT' | '/path/to/root/cert' + 'GITLAB_BACKUP_CI_PGSSLCRL' | '/path/to/crl' + 'GITLAB_BACKUP_CI_PGSSLCOMPRESSION' | '1' + end + + with_them do + let(:pg_env) { env_variable[/GITLAB_BACKUP_CI_(\w+)/, 1] } + + before do + stub_env(env_variable, overridden_value) + end + + it_behaves_like 'no configuration is overridden' + end + end + + context 'when both GITLAB_BACKUP_PGUSER and GITLAB_BACKUP_MAIN_PGUSER variable are present' do + before do + stub_env('GITLAB_BACKUP_PGUSER', 'generic_user') + stub_env('GITLAB_BACKUP_MAIN_PGUSER', 'specfic_user') + end + + it 'prefers more specific GITLAB_BACKUP_MAIN_PGUSER' do + config = subject + expect(config.dig(:activerecord, :username)).to eq('specfic_user') + expect(config.dig(:pg_env, 'PGUSER')).to eq('specfic_user') end end end diff --git a/spec/lib/gitlab/ci/variables/builder/group_spec.rb b/spec/lib/gitlab/ci/variables/builder/group_spec.rb index c3743ebd2d7..004e63f424f 100644 --- a/spec/lib/gitlab/ci/variables/builder/group_spec.rb +++ b/spec/lib/gitlab/ci/variables/builder/group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Variables::Builder::Group do +RSpec.describe Gitlab::Ci::Variables::Builder::Group, feature_category: :secrets_management do let_it_be(:group) { create(:group) } let(:builder) { described_class.new(group) } @@ -185,21 +185,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder::Group do end end - context 'recursive' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - include_examples 'correct ancestor order' - end - - context 'linear' do - before do - stub_feature_flags(use_traversal_ids: true) - end - - include_examples 'correct ancestor order' - end + include_examples 'correct ancestor order' end end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index d337a37c69f..4a6924b43a8 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -250,6 +250,7 @@ merge_requests: - created_environments - predictions - user_agent_detail +- scan_result_policy_violations external_pull_requests: - project merge_request_diff: @@ -823,6 +824,7 @@ project: - design_management_repository_state - compliance_standards_adherence - scan_result_policy_reads +- scan_result_policy_violations - project_state - security_policy_bots - target_branch_rules diff --git a/spec/lib/gitlab/protocol_access_spec.rb b/spec/lib/gitlab/protocol_access_spec.rb index 4722ea99608..cae14c3d7cf 100644 --- a/spec/lib/gitlab/protocol_access_spec.rb +++ b/spec/lib/gitlab/protocol_access_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Gitlab::ProtocolAccess do +RSpec.describe Gitlab::ProtocolAccess, feature_category: :source_code_management do using RSpec::Parameterized::TableSyntax let_it_be(:group) { create(:group) } @@ -10,25 +10,34 @@ RSpec.describe Gitlab::ProtocolAccess do describe ".allowed?" do where(:protocol, :project, :admin_setting, :namespace_setting, :expected_result) do - "web" | nil | nil | nil | true - "ssh" | nil | nil | nil | true - "http" | nil | nil | nil | true - "ssh" | nil | "" | nil | true - "http" | nil | "" | nil | true - "ssh" | nil | "ssh" | nil | true - "http" | nil | "http" | nil | true - "ssh" | nil | "http" | nil | false - "http" | nil | "ssh" | nil | false - "ssh" | ref(:p1) | nil | "all" | true - "http" | ref(:p1) | nil | "all" | true - "ssh" | ref(:p1) | nil | "ssh" | true - "http" | ref(:p1) | nil | "http" | true - "ssh" | ref(:p1) | nil | "http" | false - "http" | ref(:p1) | nil | "ssh" | false - "ssh" | ref(:p1) | "" | "all" | true - "http" | ref(:p1) | "" | "all" | true - "ssh" | ref(:p1) | "ssh" | "ssh" | true - "http" | ref(:p1) | "http" | "http" | true + "web" | nil | nil | nil | true + "ssh" | nil | nil | nil | true + "http" | nil | nil | nil | true + "ssh_certificates" | nil | nil | nil | true + "ssh" | nil | "" | nil | true + "http" | nil | "" | nil | true + "ssh_certificates" | nil | "" | nil | true + "ssh" | nil | "ssh" | nil | true + "http" | nil | "http" | nil | true + "ssh_certificates" | nil | "ssh_certificates" | nil | true + "ssh" | nil | "http" | nil | false + "http" | nil | "ssh" | nil | false + "ssh_certificates" | nil | "ssh" | nil | false + "ssh" | ref(:p1) | nil | "all" | true + "http" | ref(:p1) | nil | "all" | true + "ssh_certificates" | ref(:p1) | nil | "all" | true + "ssh" | ref(:p1) | nil | "ssh" | true + "http" | ref(:p1) | nil | "http" | true + "ssh_certificates" | ref(:p1) | nil | "ssh_certificates" | true + "ssh" | ref(:p1) | nil | "http" | false + "http" | ref(:p1) | nil | "ssh" | false + "ssh_certificates" | ref(:p1) | nil | "ssh" | false + "ssh" | ref(:p1) | "" | "all" | true + "http" | ref(:p1) | "" | "all" | true + "ssh_certificates" | ref(:p1) | "" | "all" | true + "ssh" | ref(:p1) | "ssh" | "ssh" | true + "http" | ref(:p1) | "http" | "http" | true + "ssh_certificates" | ref(:p1) | "ssh_certificates" | "ssh_certificates" | true end with_them do diff --git a/spec/migrations/20230913071219_delete_pages_domain_with_reserved_domains_spec.rb b/spec/migrations/20230913071219_delete_pages_domain_with_reserved_domains_spec.rb new file mode 100644 index 00000000000..a70bed53615 --- /dev/null +++ b/spec/migrations/20230913071219_delete_pages_domain_with_reserved_domains_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe DeletePagesDomainWithReservedDomains, feature_category: :pages do + describe 'migrates' do + context 'when a reserved domain is provided' do + it 'delete the domain' do + table(:pages_domains).create!(domain: 'gmail.com', verification_code: 'gmail') + expect { migrate! }.to change { PagesDomain.count }.by(-1) + end + end + + context 'when a reserved domain is provided with non standard case' do + it 'delete the domain' do + table(:pages_domains).create!(domain: 'AOl.com', verification_code: 'aol') + expect { migrate! }.to change { PagesDomain.count }.by(-1) + end + end + + context 'when a non reserved domain is provided' do + it 'does not delete the domain' do + table(:pages_domains).create!(domain: 'example.com', verification_code: 'example') + expect { migrate! }.not_to change { PagesDomain.count } + expect(table(:pages_domains).find_by(domain: 'example.com')).not_to be_nil + end + end + end +end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index a8e9d36a3a7..3a3ef072b28 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -309,19 +309,7 @@ RSpec.describe Ci::Runner, type: :model, feature_category: :runner do end end - context 'when use_traversal_ids* are enabled' do - it_behaves_like '.belonging_to_parent_groups_of_project' - end - - context 'when use_traversal_ids* are disabled' do - before do - stub_feature_flags( - use_traversal_ids: false - ) - end - - it_behaves_like '.belonging_to_parent_groups_of_project' - end + it_behaves_like '.belonging_to_parent_groups_of_project' context 'with instance runners sharing enabled' do # group specific diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 294b356b53d..ec5f90064f1 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -683,160 +683,126 @@ RSpec.describe Group, feature_category: :groups_and_projects do context 'traversal queries' do let_it_be(:group, reload: true) { create(:group, :nested) } - context 'recursive' do - before do - stub_feature_flags(use_traversal_ids: false) - end + it_behaves_like 'namespace traversal' - it_behaves_like 'namespace traversal' + describe '#self_and_descendants' do + it { expect(group.self_and_descendants.to_sql).to include 'traversal_ids @>' } + end - describe '#self_and_descendants' do - it { expect(group.self_and_descendants.to_sql).not_to include 'traversal_ids @>' } - end + describe '#self_and_descendant_ids' do + it { expect(group.self_and_descendant_ids.to_sql).to include 'traversal_ids @>' } + end - describe '#self_and_descendant_ids' do - it { expect(group.self_and_descendant_ids.to_sql).not_to include 'traversal_ids @>' } - end + describe '#descendants' do + it { expect(group.descendants.to_sql).to include 'traversal_ids @>' } + end - describe '#descendants' do - it { expect(group.descendants.to_sql).not_to include 'traversal_ids @>' } - end + describe '#self_and_hierarchy' do + it { expect(group.self_and_hierarchy.to_sql).to include 'traversal_ids @>' } + end - describe '#self_and_hierarchy' do - it { expect(group.self_and_hierarchy.to_sql).not_to include 'traversal_ids @>' } - end + describe '#ancestors' do + it { expect(group.ancestors.to_sql).to include "\"namespaces\".\"id\" = #{group.parent_id}" } - describe '#ancestors' do - it { expect(group.ancestors.to_sql).not_to include 'traversal_ids <@' } - end - - describe '.shortest_traversal_ids_prefixes' do - it { expect { described_class.shortest_traversal_ids_prefixes }.to raise_error /Feature not supported since the `:use_traversal_ids` is disabled/ } + it 'hierarchy order' do + expect(group.ancestors(hierarchy_order: :asc).to_sql).to include 'ORDER BY "depth" ASC' end end - context 'linear' do - it_behaves_like 'namespace traversal' + describe '#ancestors_upto' do + it { expect(group.ancestors_upto.to_sql).to include "WITH ORDINALITY" } + end - describe '#self_and_descendants' do - it { expect(group.self_and_descendants.to_sql).to include 'traversal_ids @>' } - end + describe '.shortest_traversal_ids_prefixes' do + subject { filter.shortest_traversal_ids_prefixes } - describe '#self_and_descendant_ids' do - it { expect(group.self_and_descendant_ids.to_sql).to include 'traversal_ids @>' } - end + context 'for many top-level namespaces' do + let!(:top_level_groups) { create_list(:group, 4) } - describe '#descendants' do - it { expect(group.descendants.to_sql).to include 'traversal_ids @>' } - end + context 'when querying all groups' do + let(:filter) { described_class.id_in(top_level_groups) } - describe '#self_and_hierarchy' do - it { expect(group.self_and_hierarchy.to_sql).to include 'traversal_ids @>' } - end - - describe '#ancestors' do - it { expect(group.ancestors.to_sql).to include "\"namespaces\".\"id\" = #{group.parent_id}" } - - it 'hierarchy order' do - expect(group.ancestors(hierarchy_order: :asc).to_sql).to include 'ORDER BY "depth" ASC' - end - end - - describe '#ancestors_upto' do - it { expect(group.ancestors_upto.to_sql).to include "WITH ORDINALITY" } - end - - describe '.shortest_traversal_ids_prefixes' do - subject { filter.shortest_traversal_ids_prefixes } - - context 'for many top-level namespaces' do - let!(:top_level_groups) { create_list(:group, 4) } - - context 'when querying all groups' do - let(:filter) { described_class.id_in(top_level_groups) } - - it "returns all traversal_ids" do - is_expected.to contain_exactly( - *top_level_groups.map { |group| [group.id] } - ) - end - end - - context 'when querying selected groups' do - let(:filter) { described_class.id_in(top_level_groups.first) } - - it "returns only a selected traversal_ids" do - is_expected.to contain_exactly([top_level_groups.first.id]) - end + it "returns all traversal_ids" do + is_expected.to contain_exactly( + *top_level_groups.map { |group| [group.id] } + ) end end - context 'for namespace hierarchy' do - let!(:group_a) { create(:group) } - let!(:group_a_sub_1) { create(:group, parent: group_a) } - let!(:group_a_sub_2) { create(:group, parent: group_a) } - let!(:group_b) { create(:group) } - let!(:group_b_sub_1) { create(:group, parent: group_b) } - let!(:group_c) { create(:group) } + context 'when querying selected groups' do + let(:filter) { described_class.id_in(top_level_groups.first) } - context 'when querying all groups' do - let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_a_sub_2, group_b, group_b_sub_1, group_c]) } - - it 'returns only shortest prefixes of top-level groups' do - is_expected.to contain_exactly( - [group_a.id], - [group_b.id], - [group_c.id] - ) - end - end - - context 'when sub-group is reparented' do - let(:filter) { described_class.id_in([group_b_sub_1, group_c]) } - - before do - group_b_sub_1.update!(parent: group_c) - end - - it 'returns a proper shortest prefix of a new group' do - is_expected.to contain_exactly( - [group_c.id] - ) - end - end - - context 'when querying sub-groups' do - let(:filter) { described_class.id_in([group_a_sub_1, group_b_sub_1, group_c]) } - - it 'returns sub-groups as they are shortest prefixes' do - is_expected.to contain_exactly( - [group_a.id, group_a_sub_1.id], - [group_b.id, group_b_sub_1.id], - [group_c.id] - ) - end - end - - context 'when querying group and sub-group of this group' do - let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_c]) } - - it 'returns parent groups as this contains all sub-groups' do - is_expected.to contain_exactly( - [group_a.id], - [group_c.id] - ) - end + it "returns only a selected traversal_ids" do + is_expected.to contain_exactly([top_level_groups.first.id]) end end end - context 'when project namespace exists in the group' do - let!(:project) { create(:project, group: group) } - let!(:project_namespace) { project.project_namespace } + context 'for namespace hierarchy' do + let!(:group_a) { create(:group) } + let!(:group_a_sub_1) { create(:group, parent: group_a) } + let!(:group_a_sub_2) { create(:group, parent: group_a) } + let!(:group_b) { create(:group) } + let!(:group_b_sub_1) { create(:group, parent: group_b) } + let!(:group_c) { create(:group) } - it 'filters out project namespace' do - expect(group.descendants.find_by_id(project_namespace.id)).to be_nil + context 'when querying all groups' do + let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_a_sub_2, group_b, group_b_sub_1, group_c]) } + + it 'returns only shortest prefixes of top-level groups' do + is_expected.to contain_exactly( + [group_a.id], + [group_b.id], + [group_c.id] + ) + end end + + context 'when sub-group is reparented' do + let(:filter) { described_class.id_in([group_b_sub_1, group_c]) } + + before do + group_b_sub_1.update!(parent: group_c) + end + + it 'returns a proper shortest prefix of a new group' do + is_expected.to contain_exactly( + [group_c.id] + ) + end + end + + context 'when querying sub-groups' do + let(:filter) { described_class.id_in([group_a_sub_1, group_b_sub_1, group_c]) } + + it 'returns sub-groups as they are shortest prefixes' do + is_expected.to contain_exactly( + [group_a.id, group_a_sub_1.id], + [group_b.id, group_b_sub_1.id], + [group_c.id] + ) + end + end + + context 'when querying group and sub-group of this group' do + let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_c]) } + + it 'returns parent groups as this contains all sub-groups' do + is_expected.to contain_exactly( + [group_a.id], + [group_c.id] + ) + end + end + end + end + + context 'when project namespace exists in the group' do + let!(:project) { create(:project, group: group) } + let!(:project_namespace) { project.project_namespace } + + it 'filters out project namespace' do + expect(group.descendants.find_by_id(project_namespace.id)).to be_nil end end end @@ -1683,6 +1649,14 @@ RSpec.describe Group, feature_category: :groups_and_projects do it 'returns correct access level' do expect(group.max_member_access_for_user(group_user)).to eq(Gitlab::Access::OWNER) end + + context 'when user is not active' do + let_it_be(:group_user) { create(:user, :deactivated) } + + it 'returns NO_ACCESS' do + expect(group.max_member_access_for_user(group_user)).to eq(Gitlab::Access::NO_ACCESS) + end + end end context 'when user is nil' do diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb index 67e12092e1a..484bd6fd18e 100644 --- a/spec/models/integration_spec.rb +++ b/spec/models/integration_spec.rb @@ -598,23 +598,7 @@ RSpec.describe Integration, feature_category: :integrations do end end - context 'recursive' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - include_examples 'correct ancestor order' - end - - context 'linear' do - before do - stub_feature_flags(use_traversal_ids: true) - - sub_subgroup.reload # make sure traversal_ids are reloaded - end - - include_examples 'correct ancestor order' - end + include_examples 'correct ancestor order' end end end diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb index a937a3e8988..e9822d97447 100644 --- a/spec/models/namespace_setting_spec.rb +++ b/spec/models/namespace_setting_spec.rb @@ -12,7 +12,7 @@ RSpec.describe NamespaceSetting, feature_category: :groups_and_projects, type: : end it { is_expected.to define_enum_for(:jobs_to_be_done).with_values([:basics, :move_repository, :code_storage, :exploring, :ci, :other]).with_suffix } - it { is_expected.to define_enum_for(:enabled_git_access_protocol).with_values([:all, :ssh, :http]).with_suffix } + it { is_expected.to define_enum_for(:enabled_git_access_protocol).with_suffix } describe 'default values' do subject(:setting) { described_class.new } diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 77a863e81e1..9974aac3c6c 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -661,23 +661,7 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do end context 'traversal scopes' do - context 'recursive' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - it_behaves_like 'namespace traversal scopes' - end - - context 'linear' do - it_behaves_like 'namespace traversal scopes' - end - - shared_examples 'makes recursive queries' do - specify do - expect { subject }.to make_queries_matching(/WITH RECURSIVE/) - end - end + it_behaves_like 'namespace traversal scopes' shared_examples 'does not make recursive queries' do specify do @@ -691,14 +675,6 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do subject { described_class.where(id: namespace).self_and_descendants.load } it_behaves_like 'does not make recursive queries' - - context 'when feature flag :use_traversal_ids is disabled' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - it_behaves_like 'makes recursive queries' - end end describe '.self_and_descendant_ids' do @@ -707,14 +683,6 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do subject { described_class.where(id: namespace).self_and_descendant_ids.load } it_behaves_like 'does not make recursive queries' - - context 'when feature flag :use_traversal_ids is disabled' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - it_behaves_like 'makes recursive queries' - end end end @@ -1292,30 +1260,6 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do it { is_expected.to eq false } end - describe '#use_traversal_ids?' do - let_it_be(:namespace, reload: true) { create(:namespace) } - - subject { namespace.use_traversal_ids? } - - context 'when use_traversal_ids feature flag is true' do - before do - stub_feature_flags(use_traversal_ids: true) - end - - it { is_expected.to eq true } - - it_behaves_like 'disabled feature flag when traversal_ids is blank' - end - - context 'when use_traversal_ids feature flag is false' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - it { is_expected.to eq false } - end - end - describe '#users_with_descendants' do let(:user_a) { create(:user) } let(:user_b) { create(:user) } @@ -1419,28 +1363,14 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do end describe '#all_projects' do - context 'with use_traversal_ids feature flag enabled' do - before do - stub_feature_flags(use_traversal_ids: true) - end + include_examples '#all_projects' - include_examples '#all_projects' - - # Using #self_and_descendant instead of #self_and_descendant_ids can produce - # very slow queries. - it 'calls self_and_descendant_ids' do - namespace = create(:group) - expect(namespace).to receive(:self_and_descendant_ids) - namespace.all_projects - end - end - - context 'with use_traversal_ids feature flag disabled' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - include_examples '#all_projects' + # Using #self_and_descendant instead of #self_and_descendant_ids can produce + # very slow queries. + it 'calls self_and_descendant_ids' do + namespace = create(:group) + expect(namespace).to receive(:self_and_descendant_ids) + namespace.all_projects end end diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index cd740bca502..5a4eca11f71 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe PagesDomain do +RSpec.describe PagesDomain, feature_category: :pages do using RSpec::Parameterized::TableSyntax subject(:pages_domain) { described_class.new } diff --git a/spec/models/preloaders/project_root_ancestor_preloader_spec.rb b/spec/models/preloaders/project_root_ancestor_preloader_spec.rb index 2462e305597..b690bd3162c 100644 --- a/spec/models/preloaders/project_root_ancestor_preloader_spec.rb +++ b/spec/models/preloaders/project_root_ancestor_preloader_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Preloaders::ProjectRootAncestorPreloader do +RSpec.describe Preloaders::ProjectRootAncestorPreloader, feature_category: :system_access do let_it_be(:root_parent1) { create(:group, :private, name: 'root-1', path: 'root-1') } let_it_be(:root_parent2) { create(:group, name: 'root-2', path: 'root-2') } let_it_be(:guest_project) { create(:project, name: 'public guest', path: 'public-guest') } @@ -43,87 +43,47 @@ RSpec.describe Preloaders::ProjectRootAncestorPreloader do end end - context 'when use_traversal_ids FF is enabled' do - context 'when the preloader is used' do - context 'when no additional preloads are provided' do - before do - preload_ancestors(:group) - end - - it_behaves_like 'executes N matching DB queries', 0 - end - - context 'when additional preloads are provided' do - let(:additional_preloads) { [:route] } - let(:root_query_regex) { /\ASELECT.+FROM "routes" WHERE "routes"."source_id" = \d+/ } - - before do - preload_ancestors - end - - it_behaves_like 'executes N matching DB queries', 0, :full_path - end - - context 'when projects are an array and not an ActiveRecord::Relation' do - before do - described_class.new(projects, :namespace, additional_preloads).execute - end - - it_behaves_like 'executes N matching DB queries', 4 - end - end - - context 'when the preloader is not used' do - it_behaves_like 'executes N matching DB queries', 4 - end - - context 'when using a :group sti name and passing projects in a user namespace' do - let(:projects) { [private_developer_project] } - let(:additional_preloads) { [:ip_restrictions, :saml_provider] } - - it 'does not load a nil value for root_ancestor' do + context 'when the preloader is used' do + context 'when no additional preloads are provided' do + before do preload_ancestors(:group) - - expect(pristine_projects.first.root_ancestor).to eq(private_developer_project.root_ancestor) end - end - end - context 'when use_traversal_ids FF is disabled' do - before do - stub_feature_flags(use_traversal_ids: false) + it_behaves_like 'executes N matching DB queries', 0 end - context 'when the preloader is used' do + context 'when additional preloads are provided' do + let(:additional_preloads) { [:route] } + let(:root_query_regex) { /\ASELECT.+FROM "routes" WHERE "routes"."source_id" = \d+/ } + before do preload_ancestors end - context 'when no additional preloads are provided' do - it_behaves_like 'executes N matching DB queries', 4 - end - - context 'when additional preloads are provided' do - let(:additional_preloads) { [:route] } - let(:root_query_regex) { /\ASELECT.+FROM "routes" WHERE "routes"."source_id" = \d+/ } - - it_behaves_like 'executes N matching DB queries', 4, :full_path - end + it_behaves_like 'executes N matching DB queries', 0, :full_path end - context 'when the preloader is not used' do + context 'when projects are an array and not an ActiveRecord::Relation' do + before do + described_class.new(projects, :namespace, additional_preloads).execute + end + it_behaves_like 'executes N matching DB queries', 4 end + end - context 'when using a :group sti name and passing projects in a user namespace' do - let(:projects) { [private_developer_project] } - let(:additional_preloads) { [:ip_restrictions, :saml_provider] } + context 'when the preloader is not used' do + it_behaves_like 'executes N matching DB queries', 4 + end - it 'does not load a nil value for root_ancestor' do - preload_ancestors(:group) + context 'when using a :group sti name and passing projects in a user namespace' do + let(:projects) { [private_developer_project] } + let(:additional_preloads) { [:ip_restrictions, :saml_provider] } - expect(pristine_projects.first.root_ancestor).to eq(private_developer_project.root_ancestor) - end + it 'does not load a nil value for root_ancestor' do + preload_ancestors(:group) + + expect(pristine_projects.first.root_ancestor).to eq(private_developer_project.root_ancestor) end end diff --git a/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb b/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb index 5befa3ab66f..3dc409cbcc2 100644 --- a/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb +++ b/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb @@ -34,46 +34,31 @@ RSpec.describe Preloaders::UserMaxAccessLevelInGroupsPreloader, feature_category let(:groups) { [group1, group2, group3, child_maintainer, child_indirect_access] } - context 'when traversal_ids feature flag is disabled' do - it_behaves_like 'executes N max member permission queries to the DB' do - before do - stub_feature_flags(use_traversal_ids: false) - described_class.new(groups, user).execute - end - - # One query for group with no access and another one per group where the user is not a direct member - let(:expected_query_count) { 2 } + it_behaves_like 'executes N max member permission queries to the DB' do + before do + described_class.new(groups, user).execute end + + let(:expected_query_count) { 0 } end - context 'when traversal_ids feature flag is enabled' do - it_behaves_like 'executes N max member permission queries to the DB' do - before do - stub_feature_flags(use_traversal_ids: true) - described_class.new(groups, user).execute - end + context 'for groups arising from group shares' do + let_it_be(:group4) { create(:group, :private) } + let_it_be(:group4_subgroup) { create(:group, :private, parent: group4) } - let(:expected_query_count) { 0 } + let(:groups) { [group4, group4_subgroup] } + + before do + create(:group_group_link, :guest, shared_with_group: group1, shared_group: group4) end - context 'for groups arising from group shares' do - let_it_be(:group4) { create(:group, :private) } - let_it_be(:group4_subgroup) { create(:group, :private, parent: group4) } + it 'sets the right access level in cache for groups arising from group shares' do + described_class.new(groups, user).execute - let(:groups) { [group4, group4_subgroup] } + groups.each do |group| + cached_access_level = group.max_member_access_for_user(user) - before do - create(:group_group_link, :guest, shared_with_group: group1, shared_group: group4) - end - - it 'sets the right access level in cache for groups arising from group shares' do - described_class.new(groups, user).execute - - groups.each do |group| - cached_access_level = group.max_member_access_for_user(user) - - expect(cached_access_level).to eq(Gitlab::Access::GUEST) - end + expect(cached_access_level).to eq(Gitlab::Access::GUEST) end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d35449ca8c8..947d83badf6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -5073,14 +5073,6 @@ RSpec.describe User, feature_category: :user_profile do describe '#ci_owned_runners' do it_behaves_like '#ci_owned_runners' - - context 'when FF use_traversal_ids is disabled fallbacks to inefficient implementation' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - it_behaves_like '#ci_owned_runners' - end end describe '#projects_with_reporter_access_limited_to' do diff --git a/spec/services/ci/process_sync_events_service_spec.rb b/spec/services/ci/process_sync_events_service_spec.rb index ff9bcd0f8e9..c58d73815b0 100644 --- a/spec/services/ci/process_sync_events_service_spec.rb +++ b/spec/services/ci/process_sync_events_service_spec.rb @@ -145,14 +145,6 @@ RSpec.describe Ci::ProcessSyncEventsService, feature_category: :continuous_integ end end - context 'when the use_traversal_ids FF is disabled' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - it_behaves_like 'event consuming' - end - it_behaves_like 'event consuming' it 'enqueues Namespaces::ProcessSyncEventsWorker if any left' do diff --git a/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb b/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb index 64f811771ec..799f82a9ec5 100644 --- a/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb +++ b/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb @@ -14,9 +14,21 @@ RSpec.shared_context 'exposing regular notes on a noteable in GraphQL' do let(:user) { note.author } context 'for regular notes' do + let!(:system_note) do + create( + :note, + system: true, + noteable: noteable, + project: (noteable.project if noteable.respond_to?(:project)) + ) + end + + let(:filters) { "" } + let(:query) do note_fields = <<~NOTES - notes { + notes #{filters} { + count edges { node { #{all_graphql_fields_for('Note', max_depth: 1)} @@ -42,11 +54,12 @@ RSpec.shared_context 'exposing regular notes on a noteable in GraphQL' do end end - it 'includes the note' do + it 'includes all notes' do post_graphql(query, current_user: user) - expect(noteable_data['notes']['edges'].first['node']['body']) - .to eq(note.note) + expect(noteable_data['notes']['count']).to eq(2) + expect(noteable_data['notes']['edges'][0]['node']['body']).to eq(system_note.note) + expect(noteable_data['notes']['edges'][1]['node']['body']).to eq(note.note) end it 'avoids N+1 queries' do @@ -69,6 +82,42 @@ RSpec.shared_context 'exposing regular notes on a noteable in GraphQL' do expect { post_graphql(query, current_user: user) }.not_to exceed_query_limit(control) expect_graphql_errors_to_be_empty end + + context 'when filter is provided' do + context 'when filter is set to ALL_NOTES' do + let(:filters) { "(filter: ALL_NOTES)" } + + it 'returns all the notes' do + post_graphql(query, current_user: user) + + expect(noteable_data['notes']['count']).to eq(2) + expect(noteable_data['notes']['edges'][0]['node']['body']).to eq(system_note.note) + expect(noteable_data['notes']['edges'][1]['node']['body']).to eq(note.note) + end + end + + context 'when filter is set to ONLY_COMMENTS' do + let(:filters) { "(filter: ONLY_COMMENTS)" } + + it 'returns only the comments' do + post_graphql(query, current_user: user) + + expect(noteable_data['notes']['count']).to eq(1) + expect(noteable_data['notes']['edges'][0]['node']['body']).to eq(note.note) + end + end + + context 'when filter is set to ONLY_ACTIVITY' do + let(:filters) { "(filter: ONLY_ACTIVITY)" } + + it 'returns only the activity notes' do + post_graphql(query, current_user: user) + + expect(noteable_data['notes']['count']).to eq(1) + expect(noteable_data['notes']['edges'][0]['node']['body']).to eq(system_note.note) + end + end + end end context "for discussions" do diff --git a/spec/support/shared_examples/namespaces/traversal_examples.rb b/spec/support/shared_examples/namespaces/traversal_examples.rb index 4dff4f68995..960160395f8 100644 --- a/spec/support/shared_examples/namespaces/traversal_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_examples.rb @@ -240,14 +240,6 @@ RSpec.shared_examples 'namespace traversal' do describe '#ancestors_upto' do include_examples '#ancestors_upto' - - context 'with use_traversal_ids disabled' do - before do - stub_feature_flags(use_traversal_ids: false) - end - - include_examples '#ancestors_upto' - end end describe '#descendants' do diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb index 627d1b33489..637068c5c8a 100644 --- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb @@ -70,28 +70,10 @@ RSpec.shared_examples 'namespace traversal scopes' do end describe '.roots' do - context "use_traversal_ids feature flag is true" do - before do - stub_feature_flags(use_traversal_ids: true) - end + it_behaves_like '.roots' - it_behaves_like '.roots' - - it 'not make recursive queries' do - expect { described_class.where(id: [nested_group_1]).roots.load }.not_to make_queries_matching(/WITH RECURSIVE/) - end - end - - context "use_traversal_ids feature flag is false" do - before do - stub_feature_flags(use_traversal_ids: false) - end - - it_behaves_like '.roots' - - it 'makes recursive queries' do - expect { described_class.where(id: [nested_group_1]).roots.load }.to make_queries_matching(/WITH RECURSIVE/) - end + it 'not make recursive queries' do + expect { described_class.where(id: [nested_group_1]).roots.load }.not_to make_queries_matching(/WITH RECURSIVE/) end end