diff options
| author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-16 18:10:00 +0000 |
|---|---|---|
| committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-16 18:10:00 +0000 |
| commit | 8d68ae988d62d732523354b1892988e31c53cb54 (patch) | |
| tree | ad87af650dd6dd9c59bbfdcf9fb74ddf1651f1ba | |
| parent | f0718f2099663226531b8916e14d5582eb28e453 (diff) | |
| download | gitlab-ce-8d68ae988d62d732523354b1892988e31c53cb54.tar.gz | |
Add latest changes from gitlab-org/gitlab@master
74 files changed, 1195 insertions, 592 deletions
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json index 3a6ff9bbc19..db11afbf382 100644 --- a/app/assets/javascripts/editor/schema/ci.json +++ b/app/assets/javascripts/editor/schema/ci.json @@ -239,6 +239,10 @@ } ] }, + "browser_performance": { + "type": "string", + "description": "Path to a single file with browser performance metric report(s)." + }, "coverage_report": { "type": [ "object", @@ -295,10 +299,6 @@ "$ref": "#/definitions/string_file_list", "description": "Path to file or list of files with license report(s)." }, - "performance": { - "$ref": "#/definitions/string_file_list", - "description": "Path to file or list of files with performance metrics report(s)." - }, "requirements": { "$ref": "#/definitions/string_file_list", "description": "Path to file or list of files with requirements report(s)." diff --git a/app/assets/javascripts/language_switcher/components/app.vue b/app/assets/javascripts/language_switcher/components/app.vue index 8ab0d02d6f0..4d3fe22e247 100644 --- a/app/assets/javascripts/language_switcher/components/app.vue +++ b/app/assets/javascripts/language_switcher/components/app.vue @@ -1,11 +1,17 @@ <script> -import { GlCollapsibleListbox } from '@gitlab/ui'; +import { GlCollapsibleListbox, GlLink } from '@gitlab/ui'; +import { __ } from '~/locale'; import { setCookie } from '~/lib/utils/common_utils'; +import { helpPagePath } from '~/helpers/help_page_helper'; import { PREFERRED_LANGUAGE_COOKIE_KEY } from '../constants'; +const HELP_TRANSLATE_MSG = __('Help translate to your language'); +const HELP_TRANSLATE_HREF = helpPagePath('/development/i18n/translation.md'); + export default { components: { GlCollapsibleListbox, + GlLink, }, inject: { locales: { @@ -29,6 +35,8 @@ export default { return `language_switcher_lang_${locale}`; }, }, + HELP_TRANSLATE_MSG, + HELP_TRANSLATE_HREF, }; </script> <template> @@ -51,5 +59,13 @@ export default { {{ locale.text }} </span> </template> + <template #footer> + <div + class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-100 gl-display-flex gl-justify-content-center gl-p-3" + data-testid="footer" + > + <gl-link :href="$options.HELP_TRANSLATE_HREF">{{ $options.HELP_TRANSLATE_MSG }}</gl-link> + </div> + </template> </gl-collapsible-listbox> </template> diff --git a/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue b/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue index 6feb4c2188f..da54b2eb084 100644 --- a/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue +++ b/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue @@ -1,5 +1,13 @@ <script> -import { GlButton, GlEmptyState, GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui'; +import { + GlButton, + GlEmptyState, + GlIcon, + GlLink, + GlLoadingIcon, + GlTableLite, + GlTooltipDirective as GlTooltip, +} from '@gitlab/ui'; import { s__, __ } from '~/locale'; import { createAlert } from '~/flash'; @@ -34,15 +42,20 @@ export default { components: { GlButton, GlEmptyState, + GlIcon, GlLink, GlLoadingIcon, - GlTable, + GlTableLite, PaginationBar, ImportStatus, TimeAgo, LocalStorageSync, }, + directives: { + GlTooltip, + }, + data() { return { loading: true, @@ -58,12 +71,12 @@ export default { fields: [ tableCell({ key: 'source_full_path', - label: s__('BulkImport|Source group'), + label: s__('BulkImport|Source'), thClass: `${DEFAULT_TH_CLASSES} gl-w-30p`, }), tableCell({ key: 'destination_name', - label: s__('BulkImport|Destination group'), + label: s__('BulkImport|Destination'), thClass: `${DEFAULT_TH_CLASSES} gl-w-40p`, }), tableCell({ @@ -113,12 +126,24 @@ export default { } }, - getDestinationUrl({ destination_name: name, destination_namespace: namespace }) { - return [namespace, name].filter(Boolean).join('/'); + getFullDestinationUrl(params) { + return joinPaths(gon.relative_url_root || '', '/', params.destination_full_path); }, - getFullDestinationUrl(params) { - return joinPaths(gon.relative_url_root || '', '/', this.getDestinationUrl(params)); + getPresentationUrl(item) { + const suffix = item.entity_type === 'group' ? '/' : ''; + return `${item.destination_full_path}${suffix}`; + }, + + getEntityTooltip(item) { + switch (item.entity_type) { + case 'project': + return __('Project'); + case 'group': + return __('Group'); + default: + return ''; + } }, }, @@ -134,25 +159,32 @@ export default { > <h1 class="gl-my-0 gl-py-4 gl-font-size-h1"> <img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" /> - {{ s__('BulkImport|Group import history') }} + {{ s__('BulkImport|GitLab Migration history') }} </h1> </div> <gl-loading-icon v-if="loading" size="lg" class="gl-mt-5" /> <gl-empty-state v-else-if="!hasHistoryItems" :title="s__('BulkImport|No history is available')" - :description="s__('BulkImport|Your imported groups will appear here.')" + :description="s__('BulkImport|Your imported groups and projects will appear here.')" /> <template v-else> - <gl-table + <gl-table-lite :fields="$options.fields" :items="historyItems" data-qa-selector="import_history_table" class="gl-w-full" > <template #cell(destination_name)="{ item }"> + <gl-icon + v-gl-tooltip + :name="item.entity_type" + :title="getEntityTooltip(item)" + :aria-label="getEntityTooltip(item)" + class="gl-text-gray-500" + /> <gl-link :href="getFullDestinationUrl(item)" target="_blank"> - {{ getDestinationUrl(item) }} + {{ getPresentationUrl(item) }} </gl-link> </template> <template #cell(created_at)="{ value }"> @@ -171,7 +203,7 @@ export default { <template #row-details="{ item }"> <pre><code>{{ item.failures }}</code></pre> </template> - </gl-table> + </gl-table-lite> <pagination-bar :page-info="pageInfo" class="gl-m-0 gl-mt-3" diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue index 6a03e38a31d..47b96934420 100644 --- a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue +++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue @@ -2,6 +2,7 @@ import { s__, sprintf } from '~/locale'; import SegmentedControlButtonGroup from '~/vue_shared/components/segmented_control_button_group.vue'; import CiCdAnalyticsAreaChart from './ci_cd_analytics_area_chart.vue'; +import { DEFAULT_SELECTED_CHART } from './constants'; export default { components: { @@ -20,7 +21,7 @@ export default { }, data() { return { - selectedChart: 0, + selectedChart: DEFAULT_SELECTED_CHART, }; }, computed: { diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js index 1561674c0ad..3ac632b4690 100644 --- a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js +++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js @@ -1 +1,2 @@ export const CHART_CONTAINER_HEIGHT = 300; +export const DEFAULT_SELECTED_CHART = 2; diff --git a/app/events/pages_domains/pages_domain_created_event.rb b/app/events/pages_domains/pages_domain_created_event.rb index a86718f4681..430a3e0100c 100644 --- a/app/events/pages_domains/pages_domain_created_event.rb +++ b/app/events/pages_domains/pages_domain_created_event.rb @@ -9,6 +9,7 @@ module PagesDomains 'project_id' => { 'type' => 'integer' }, 'namespace_id' => { 'type' => 'integer' }, 'root_namespace_id' => { 'type' => 'integer' }, + 'domain_id' => { 'type' => 'integer' }, 'domain' => { 'type' => 'string' } }, 'required' => %w[project_id namespace_id root_namespace_id] diff --git a/app/events/pages_domains/pages_domain_deleted_event.rb b/app/events/pages_domains/pages_domain_deleted_event.rb index 7fe165a7249..3f32f5abe2a 100644 --- a/app/events/pages_domains/pages_domain_deleted_event.rb +++ b/app/events/pages_domains/pages_domain_deleted_event.rb @@ -9,6 +9,7 @@ module PagesDomains 'project_id' => { 'type' => 'integer' }, 'namespace_id' => { 'type' => 'integer' }, 'root_namespace_id' => { 'type' => 'integer' }, + 'domain_id' => { 'type' => 'integer' }, 'domain' => { 'type' => 'string' } }, 'required' => %w[project_id namespace_id root_namespace_id] diff --git a/app/events/pages_domains/pages_domain_updated_event.rb b/app/events/pages_domains/pages_domain_updated_event.rb index 641fb2f6a53..f7211420355 100644 --- a/app/events/pages_domains/pages_domain_updated_event.rb +++ b/app/events/pages_domains/pages_domain_updated_event.rb @@ -9,6 +9,7 @@ module PagesDomains 'project_id' => { 'type' => 'integer' }, 'namespace_id' => { 'type' => 'integer' }, 'root_namespace_id' => { 'type' => 'integer' }, + 'domain_id' => { 'type' => 'integer' }, 'domain' => { 'type' => 'string' } }, 'required' => %w[project_id namespace_id root_namespace_id] diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb index a3de875c196..83ed8c37250 100644 --- a/app/graphql/resolvers/work_items_resolver.rb +++ b/app/graphql/resolvers/work_items_resolver.rb @@ -55,7 +55,7 @@ module Resolvers last_edited_by: :last_edited_by, assignees: :assignees, parent: :work_item_parent, - children: { work_item_children: [:author, { project: :project_feature }] }, + children: { work_item_children_by_created_at: [:author, { project: :project_feature }] }, labels: :labels, milestone: :milestone } diff --git a/app/models/bulk_imports/entity.rb b/app/models/bulk_imports/entity.rb index e49c4e09a50..ebca5e90313 100644 --- a/app/models/bulk_imports/entity.rb +++ b/app/models/bulk_imports/entity.rb @@ -152,6 +152,10 @@ class BulkImports::Entity < ApplicationRecord "::#{pluralized_name.capitalize}::UpdateService".constantize end + def full_path + project? ? project&.full_path : group&.full_path + end + private def validate_parent_is_a_group diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 4e3f4b0c328..909658214fd 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -78,6 +78,10 @@ class PagesDomain < ApplicationRecord find_by("LOWER(domain) = LOWER(?)", domain) end + def self.ids_for_project(project_id) + where(project_id: project_id).ids + end + def verified? !!verified_at end @@ -209,7 +213,7 @@ class PagesDomain < ApplicationRecord return unless pages_deployed? cache = if Feature.enabled?(:cache_pages_domain_api, project.root_namespace) - ::Gitlab::Pages::CacheControl.for_project(project.id) + ::Gitlab::Pages::CacheControl.for_domain(id) end Pages::VirtualDomain.new( diff --git a/app/models/work_item.rb b/app/models/work_item.rb index 0810c520f7e..f94e831437a 100644 --- a/app/models/work_item.rb +++ b/app/models/work_item.rb @@ -13,6 +13,8 @@ class WorkItem < Issue has_many :child_links, class_name: '::WorkItems::ParentLink', foreign_key: :work_item_parent_id has_many :work_item_children, through: :child_links, class_name: 'WorkItem', foreign_key: :work_item_id, source: :work_item + has_many :work_item_children_by_created_at, -> { order(:created_at) }, through: :child_links, class_name: 'WorkItem', + foreign_key: :work_item_id, source: :work_item scope :inc_relations_for_permission_check, -> { includes(:author, project: :project_feature) } diff --git a/app/models/work_items/widgets/hierarchy.rb b/app/models/work_items/widgets/hierarchy.rb index d0819076efd..ee10c631bcc 100644 --- a/app/models/work_items/widgets/hierarchy.rb +++ b/app/models/work_items/widgets/hierarchy.rb @@ -8,7 +8,7 @@ module WorkItems end def children - work_item.work_item_children + work_item.work_item_children_by_created_at end end end diff --git a/app/services/pages_domains/create_service.rb b/app/services/pages_domains/create_service.rb index 1f771ca3a05..17194fbe5e4 100644 --- a/app/services/pages_domains/create_service.rb +++ b/app/services/pages_domains/create_service.rb @@ -24,6 +24,7 @@ module PagesDomains project_id: project.id, namespace_id: project.namespace_id, root_namespace_id: project.root_namespace.id, + domain_id: domain.id, domain: domain.domain } ) diff --git a/app/services/pages_domains/delete_service.rb b/app/services/pages_domains/delete_service.rb index af69e1845a9..89e598acee0 100644 --- a/app/services/pages_domains/delete_service.rb +++ b/app/services/pages_domains/delete_service.rb @@ -22,6 +22,7 @@ module PagesDomains project_id: project.id, namespace_id: project.namespace_id, root_namespace_id: project.root_namespace.id, + domain_id: domain.id, domain: domain.domain } ) diff --git a/app/services/pages_domains/retry_acme_order_service.rb b/app/services/pages_domains/retry_acme_order_service.rb index 6251c9d3615..01647a8ecf5 100644 --- a/app/services/pages_domains/retry_acme_order_service.rb +++ b/app/services/pages_domains/retry_acme_order_service.rb @@ -30,6 +30,7 @@ module PagesDomains project_id: domain.project.id, namespace_id: domain.project.namespace_id, root_namespace_id: domain.project.root_namespace.id, + domain_id: domain.id, domain: domain.domain } ) diff --git a/app/services/pages_domains/update_service.rb b/app/services/pages_domains/update_service.rb index b038aaa95b6..1887441d8b8 100644 --- a/app/services/pages_domains/update_service.rb +++ b/app/services/pages_domains/update_service.rb @@ -24,6 +24,7 @@ module PagesDomains project_id: project.id, namespace_id: project.namespace_id, root_namespace_id: project.root_namespace.id, + domain_id: domain.id, domain: domain.domain } ) diff --git a/app/views/admin/runners/edit.html.haml b/app/views/admin/runners/edit.html.haml index ccdfe67ea77..e586a7a965e 100644 --- a/app/views/admin/runners/edit.html.haml +++ b/app/views/admin/runners/edit.html.haml @@ -24,7 +24,8 @@ dismissible: false, title: project.full_name) do |c| = c.actions do - = link_to _('Disable'), admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete, class: 'btn gl-alert-action btn-confirm btn-md gl-button' + = render Pajamas::ButtonComponent.new(variant: :confirm, href: admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete) do + = _('Disable') %table.table{ data: { testid: 'unassigned-projects' } } %thead @@ -47,7 +48,8 @@ = project.full_name %td .float-right - = form_for project.runner_projects.new, url: admin_namespace_project_runner_projects_path(project.namespace, project), method: :post do |f| + = gitlab_ui_form_for project.runner_projects.new, url: admin_namespace_project_runner_projects_path(project.namespace, project), method: :post do |f| = f.hidden_field :runner_id, value: @runner.id - = f.submit _('Enable'), class: 'gl-button btn btn-sm' + = render Pajamas::ButtonComponent.new(size: :small, type: :submit) do + = _('Enable') = paginate_without_count @projects diff --git a/app/workers/pages/invalidate_domain_cache_worker.rb b/app/workers/pages/invalidate_domain_cache_worker.rb index 97e8966b342..1700b681b94 100644 --- a/app/workers/pages/invalidate_domain_cache_worker.rb +++ b/app/workers/pages/invalidate_domain_cache_worker.rb @@ -9,9 +9,9 @@ module Pages feature_category :pages def handle_event(event) - if event.data[:project_id] + domain_ids(event).each do |domain_id| ::Gitlab::Pages::CacheControl - .for_project(event.data[:project_id]) + .for_domain(domain_id) .clear_cache end @@ -25,5 +25,13 @@ module Pages .clear_cache end end + + def domain_ids(event) + ids = PagesDomain.ids_for_project(event.data[:project_id]) + + ids << event.data[:domain_id] if event.data[:domain_id] + + ids + end end end diff --git a/data/deprecations/15-8-deprecate-disabled-with-override-runner-setting-value.yml b/data/deprecations/15-8-deprecate-disabled-with-override-runner-setting-value.yml new file mode 100644 index 00000000000..db93f0cadcc --- /dev/null +++ b/data/deprecations/15-8-deprecate-disabled-with-override-runner-setting-value.yml @@ -0,0 +1,18 @@ +# +# REQUIRED FIELDS +# +- title: "GraphQL: The `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` enum is deprecated. Use `DISABLED_AND_OVERRIDABLE` instead" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead. + announcement_milestone: "15.8" # (required) The milestone when this feature was first announced as deprecated. + announcement_date: "2023-01-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post. + removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed + removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post. + breaking_change: true # (required) If this deprecation is a breaking change, set this value to true + reporter: tschmitke # (required) GitLab username of the person reporting the deprecation + stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385636 # (required) Link to the deprecation issue in GitLab + body: | # (required) Do not modify this line, instead modify the lines below. + In GitLab 16.0, the `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` GraphQL enum type will be replaced with the value, `DISABLED_AND_OVERRIDABLE`. + # + # OTHER OPTIONAL FIELDS + # + documentation_url: "https://docs.gitlab.com/ee/api/graphql/reference/#sharedrunnerssetting" # (optional) This is a link to the current documentation page diff --git a/db/migrate/20230112104253_add_tmp_index_to_ci_build_runner_session.rb b/db/migrate/20230112104253_add_tmp_index_to_ci_build_runner_session.rb new file mode 100644 index 00000000000..62f202c1ee2 --- /dev/null +++ b/db/migrate/20230112104253_add_tmp_index_to_ci_build_runner_session.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddTmpIndexToCiBuildRunnerSession < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + INDEX_NAME = :tmp_index_ci_builds_runner_session_on_partition_id_and_id + TABLE_NAME = :ci_builds_runner_session + + def up + return unless Gitlab.com? + + add_concurrent_index( + TABLE_NAME, + [:partition_id, :id], + where: 'partition_id = 101', + name: INDEX_NAME + ) + end + + def down + remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME) + end +end diff --git a/db/migrate/20230112104526_add_tmp_index_to_ci_pending_build.rb b/db/migrate/20230112104526_add_tmp_index_to_ci_pending_build.rb new file mode 100644 index 00000000000..3666e03a8b5 --- /dev/null +++ b/db/migrate/20230112104526_add_tmp_index_to_ci_pending_build.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddTmpIndexToCiPendingBuild < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + INDEX_NAME = :tmp_index_ci_pending_builds_on_partition_id_and_id + TABLE_NAME = :ci_pending_builds + + def up + return unless Gitlab.com? + + add_concurrent_index( + TABLE_NAME, + [:partition_id, :id], + where: 'partition_id = 101', + name: INDEX_NAME + ) + end + + def down + remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME) + end +end diff --git a/db/migrate/20230112104636_add_tmp_index_to_ci_running_build.rb b/db/migrate/20230112104636_add_tmp_index_to_ci_running_build.rb new file mode 100644 index 00000000000..66c66cc1cd9 --- /dev/null +++ b/db/migrate/20230112104636_add_tmp_index_to_ci_running_build.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddTmpIndexToCiRunningBuild < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + INDEX_NAME = :tmp_index_ci_running_builds_on_partition_id_and_id + TABLE_NAME = :ci_running_builds + + def up + return unless Gitlab.com? + + add_concurrent_index( + TABLE_NAME, + [:partition_id, :id], + where: 'partition_id = 101', + name: INDEX_NAME + ) + end + + def down + remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME) + end +end diff --git a/db/schema_migrations/20230112104253 b/db/schema_migrations/20230112104253 new file mode 100644 index 00000000000..dd1f7237448 --- /dev/null +++ b/db/schema_migrations/20230112104253 @@ -0,0 +1 @@ +e15e005b840e8d6037548d2abec3ddbae9698ce29e41ac63b134cfc57361d311
\ No newline at end of file diff --git a/db/schema_migrations/20230112104526 b/db/schema_migrations/20230112104526 new file mode 100644 index 00000000000..6f7dc070f72 --- /dev/null +++ b/db/schema_migrations/20230112104526 @@ -0,0 +1 @@ +7824b001286975b284a1cc9dafd58e8959d9f9560a283b139551542bea82b128
\ No newline at end of file diff --git a/db/schema_migrations/20230112104636 b/db/schema_migrations/20230112104636 new file mode 100644 index 00000000000..ed9fbf67d97 --- /dev/null +++ b/db/schema_migrations/20230112104636 @@ -0,0 +1 @@ +ec9497e49f2b1289c144abf50aeb288fdbdf9543cf87bb874054bf6bb51c645f
\ No newline at end of file diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md index 96d4240b3ef..6b702ad7e03 100644 --- a/doc/api/protected_branches.md +++ b/doc/api/protected_branches.md @@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Protected branches API **(FREE)** -**Valid access levels** +## Valid access levels The access levels are defined in the `ProtectedRefAccess.allowed_access_levels` method. Currently, these levels are recognized: diff --git a/doc/api/scim.md b/doc/api/scim.md index 0ee9779ccbd..46896d7a4c8 100644 --- a/doc/api/scim.md +++ b/doc/api/scim.md @@ -14,7 +14,7 @@ and provides the `/Users` endpoint. The base URL is `/api/scim/v2/groups/:group_ To use this API, [Group SSO](../user/group/saml_sso/index.md) must be enabled for the group. This API is only in use where [SCIM for Group SSO](../user/group/saml_sso/scim_setup.md) is enabled. It's a prerequisite to the creation of SCIM identities. -Not to be confused with the [internal SCIM API](../development/internal_api/index.md#scim-api). +Not to be confused with the [internal group SCIM API](../development/internal_api/index.md#group-scim-api). ## Get SCIM identities for a group diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md index c0feb08389a..7d21dd594ad 100644 --- a/doc/architecture/blueprints/runner_tokens/index.md +++ b/doc/architecture/blueprints/runner_tokens/index.md @@ -314,33 +314,34 @@ using PAT tokens for example - such that every runner is associated with an owne | Component | Milestone | Changes | |------------------|----------:|---------| -| GitLab Runner | `15.x` | Ensure a sidecar TOML file exists with a `system_id` value.<br/>Log new system ID values with `INFO` level as they get assigned. | -| GitLab Runner | `15.x` | Log unique system ID in the build logs. | -| GitLab Runner | `15.x` | Label Prometheus metrics with unique system ID. | -| GitLab Runner | `15.x` | Prepare `register` command to fail if runner server-side configuration options are passed together with a new `glrt-` token. | +| GitLab Runner | `15.7` | Ensure a sidecar TOML file exists with a `system_id` value.<br/>Log new system ID values with `INFO` level as they get assigned. | +| GitLab Runner | `15.7` | Log unique system ID in the build logs. | +| GitLab Runner | `15.9` | Label Prometheus metrics with unique system ID. | +| GitLab Runner | `15.8` | Prepare `register` command to fail if runner server-side configuration options are passed together with a new `glrt-` token. | ### Stage 3 - Database changes | Component | Milestone | Changes | |------------------|----------:|---------| -| GitLab Rails app | | Create database migration to add columns to `ci_runners` table. | -| GitLab Rails app | | Create database migration to add `ci_runner_machines` table. | -| GitLab Rails app | | Create database migration to add `ci_runner_machines.id` foreign key to `ci_builds_metadata` table. | -| GitLab Rails app | | Create database migrations to add `allow_runner_registration_token` setting to `application_settings` and `namespace_settings` tables (default: `true`). | -| GitLab Rails app | | Create `ci_runner_machines` record in `POST /runners/verify` request if the runner token is prefixed with `glrt-`. | -| GitLab Rails app | | Use runner token + `system_id` JSON parameters in `POST /jobs/request` request in the [heartbeat request](https://gitlab.com/gitlab-org/gitlab/blob/c73c96a8ffd515295842d72a3635a8ae873d688c/lib/api/ci/helpers/runner.rb#L14-20) to update the `ci_runner_machines` cache/table. | +| GitLab Rails app | `%15.8` | Create database migration to add columns to `ci_runners` table. | +| GitLab Rails app | `%15.8` | Create database migration to add `ci_runner_machines` table. | +| GitLab Rails app | `%15.9` | Create database migration to add `ci_runner_machines.id` foreign key to `ci_builds_metadata` table. | +| GitLab Rails app | `%15.8` | Create database migrations to add `allow_runner_registration_token` setting to `application_settings` and `namespace_settings` tables (default: `true`). | +| GitLab Rails app | `%15.8` | Create database migration to add config column to `ci_runner_machines` table. | | GitLab Runner | | Start sending `system_id` value in `POST /jobs/request` request and other follow-up requests that require identifying the unique system. | | GitLab Rails app | | Create service similar to `StaleGroupRunnersPruneCronWorker` service to clean up `ci_runner_machines` records instead of `ci_runners` records.<br/>Existing service continues to exist but focuses only on legacy runners. | +| GitLab Rails app | | Create `ci_runner_machines` record in `POST /runners/verify` request if the runner token is prefixed with `glrt-`. | +| GitLab Rails app | | Use runner token + `system_id` JSON parameters in `POST /jobs/request` request in the [heartbeat request](https://gitlab.com/gitlab-org/gitlab/blob/c73c96a8ffd515295842d72a3635a8ae873d688c/lib/api/ci/helpers/runner.rb#L14-20) to update the `ci_runner_machines` cache/table. | ### Stage 4 - New UI | Component | Milestone | Changes | |------------------|----------:|---------| -| GitLab Runner | | Implement new GraphQL user-authenticated API to create a new runner. | -| GitLab Runner | | [Add prefix to newly generated runner authentication tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/383198). | -| GitLab Rails app | | Implement UI to create new runner. | -| GitLab Rails app | | GraphQL changes to `CiRunner` type. | -| GitLab Rails app | | UI changes to runner details view (listing of platform, architecture, IP address, etc.) (?) | +| GitLab Rails app | `%15.10` | Implement new GraphQL user-authenticated API to create a new runner. | +| GitLab Rails app | `%15.10` | [Add prefix to newly generated runner authentication tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/383198). | +| GitLab Rails app | `%15.10` | Implement UI to create new runner. | +| GitLab Rails app | `%15.10` | GraphQL changes to `CiRunner` type. | +| GitLab Rails app | `%15.10` | UI changes to runner details view (listing of platform, architecture, IP address, etc.) (?) | ### Stage 5 - Optional disabling of registration token diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md index 3c73030aceb..be504d41a32 100644 --- a/doc/development/documentation/workflow.md +++ b/doc/development/documentation/workflow.md @@ -21,7 +21,7 @@ If you are not a GitLab team member, or do not have the Developer role for the G 1. Select an [issue](https://about.gitlab.com/handbook/product/ux/technical-writing/#community-contribution-opportunities) you'd like to work on. - You don't need an issue to open a merge request. - - For a Hackathon, mention `@docs-hackathon` in a comment and ask for the issue to be assigned to you. + - For a Hackathon, mention `@gitlab-org/docs-hackathon` in a comment and ask for the issue to be assigned to you. To be fair to other contributors, if you see someone has already asked to work on the issue, choose another issue. If you are looking for issues to work on and don't see any that suit you, you can always fix [Vale](testing.md#vale) issues. 1. Go to the [GitLab repository](https://gitlab.com/gitlab-org/gitlab). diff --git a/doc/development/internal_api/index.md b/doc/development/internal_api/index.md index 464cb692790..f0fdedd801f 100644 --- a/doc/development/internal_api/index.md +++ b/doc/development/internal_api/index.md @@ -964,17 +964,17 @@ Example response: - CustomersDot -## SCIM API **(PREMIUM SAAS)** +## Group SCIM API **(PREMIUM SAAS)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9388) in GitLab 11.10. -The SCIM API implements the [RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644). As this API is for +The group SCIM API implements the [RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644). As this API is for **system** use for SCIM provider integration, it is subject to change without notice. -To use this API, [Group SSO](../../user/group/saml_sso/index.md) must be enabled for the group. +To use this API, enable [Group SSO](../../user/group/saml_sso/index.md) for the group. This API is only in use where [SCIM for Group SSO](../../user/group/saml_sso/scim_setup.md) is enabled. It's a prerequisite to the creation of SCIM identities. -Not to be confused with the [main SCIM API](../../api/scim.md). +This API is different to the [main SCIM API](../../api/scim.md) and the [instance SCIM API](#instance-scim-api). ### Get a list of SCIM provisioned users @@ -991,7 +991,7 @@ Parameters: |:----------|:--------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------| | `filter` | string | no | A [filter](#available-filters) expression. | | `group_path` | string | yes | Full path to the group. | -| `startIndex` | integer | no | The 1-based index indicating where to start returning results from. A value of less than one will be interpreted as 1. | +| `startIndex` | integer | no | The 1-based index indicating where to start returning results from. A value of less than one is interpreted as 1. | | `count` | integer | no | Desired maximum number of query results. | NOTE: @@ -1185,6 +1185,219 @@ curl --verbose --request DELETE "https://gitlab.example.com/api/scim/v2/groups/t Returns an empty response with a `204` status code if successful. +## Instance SCIM API **(PREMIUM SELF)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378599) in GitLab 15.8. + +The Instance SCIM API implements the [RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644). As this API is for +**system** use for SCIM provider integration, it is subject to change without notice. + +To use this API, enable [SAML SSO](../../integration/saml.md) for the instance. + +This API is different to the [main SCIM API](../../api/scim.md) and the [group SCIM API](#group-scim-api). + +### Get a list of SCIM provisioned users + +This endpoint is used as part of the SCIM syncing mechanism. It only returns +a single user based on a unique ID which should match the `extern_uid` of the user. + +```plaintext +GET /api/scim/v2/application/Users +``` + +Parameters: + +| Attribute | Type | Required | Description | +|:----------|:--------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------| +| `filter` | string | no | A [filter](#available-filters) expression. | +| `startIndex` | integer | no | The 1-based index indicating where to start returning results from. A value of less than one is interpreted as 1. | +| `count` | integer | no | Desired maximum number of query results. | + +NOTE: +Pagination follows the [SCIM spec](https://www.rfc-editor.org/rfc/rfc7644#section-3.4.2.4) rather than GitLab pagination as used elsewhere. If records change between requests it is possible for a page to either be missing records that have moved to a different page or repeat records from a previous request. + +Example request: + +```shell +curl "https://gitlab.example.com/api/scim/v2/application/Users?filter=id%20eq%20%220b1d561c-21ff-4092-beab-8154b17f82f2%22" \ + --header "Authorization: Bearer <your_scim_token>" \ + --header "Content-Type: application/scim+json" +``` + +Example response: + +```json +{ + "schemas": [ + "urn:ietf:params:scim:api:messages:2.0:ListResponse" + ], + "totalResults": 1, + "itemsPerPage": 20, + "startIndex": 1, + "Resources": [ + { + "schemas": [ + "urn:ietf:params:scim:schemas:core:2.0:User" + ], + "id": "0b1d561c-21ff-4092-beab-8154b17f82f2", + "active": true, + "name.formatted": "Test User", + "userName": "username", + "meta": { "resourceType":"User" }, + "emails": [ + { + "type": "work", + "value": "name@example.com", + "primary": true + } + ] + } + ] +} +``` + +### Get a single SCIM provisioned user + +```plaintext +GET /api/scim/v2/application/Users/:id +``` + +Parameters: + +| Attribute | Type | Required | Description | +|:----------|:--------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------| +| `id` | string | yes | External UID of the user. | + +Example request: + +```shell +curl "https://gitlab.example.com/api/scim/v2/application/Users/f0b1d561c-21ff-4092-beab-8154b17f82f2" \ + --header "Authorization: Bearer <your_scim_token>" --header "Content-Type: application/scim+json" +``` + +Example response: + +```json +{ + "schemas": [ + "urn:ietf:params:scim:schemas:core:2.0:User" + ], + "id": "0b1d561c-21ff-4092-beab-8154b17f82f2", + "active": true, + "name.formatted": "Test User", + "userName": "username", + "meta": { "resourceType":"User" }, + "emails": [ + { + "type": "work", + "value": "name@example.com", + "primary": true + } + ] +} +``` + +### Create a SCIM provisioned user + +```plaintext +POST /api/scim/v2/application/Users +``` + +Parameters: + +| Attribute | Type | Required | Description | +|:---------------|:----------|:----|:--------------------------| +| `externalId` | string | yes | External UID of the user. | +| `userName` | string | yes | Username of the user. | +| `emails` | JSON string | yes | Work email. | +| `name` | JSON string | yes | Name of the user. | +| `meta` | string | no | Resource type (`User`). | + +Example request: + +```shell +curl --verbose --request POST "https://gitlab.example.com/api/scim/v2/application/Users" \ + --data '{"externalId":"test_uid","active":null,"userName":"username","emails":[{"primary":true,"type":"work","value":"name@example.com"}],"name":{"formatted":"Test User","familyName":"User","givenName":"Test"},"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"meta":{"resourceType":"User"}}' \ + --header "Authorization: Bearer <your_scim_token>" --header "Content-Type: application/scim+json" +``` + +Example response: + +```json +{ + "schemas": [ + "urn:ietf:params:scim:schemas:core:2.0:User" + ], + "id": "0b1d561c-21ff-4092-beab-8154b17f82f2", + "active": true, + "name.formatted": "Test User", + "userName": "username", + "meta": { "resourceType":"User" }, + "emails": [ + { + "type": "work", + "value": "name@example.com", + "primary": true + } + ] +} +``` + +Returns a `201` status code if successful. + +### Update a single SCIM provisioned user + +Fields that can be updated are: + +| SCIM/IdP field | GitLab field | +|:---------------------------------|:-----------------------------------------------------------------------------| +| `id/externalId` | `extern_uid` | +| `active` | Identity removal if `active` = `false` | + +```plaintext +PATCH /api/scim/v2/application/Users/:id +``` + +Parameters: + +| Attribute | Type | Required | Description | +|:----------|:--------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------| +| `id` | string | yes | External UID of the user. | +| `Operations` | JSON string | yes | An [operations](#available-operations) expression. | + +Example request: + +```shell +curl --verbose --request PATCH "https://gitlab.example.com/api/scim/v2/application/Users/f0b1d561c-21ff-4092-beab-8154b17f82f2" \ + --data '{ "Operations": [{"op":"Add","path":"name.formatted","value":"New Name"}] }' \ + --header "Authorization: Bearer <your_scim_token>" --header "Content-Type: application/scim+json" +``` + +Returns an empty response with a `204` status code if successful. + +### Remove a single SCIM provisioned user + +Removes the user's SSO identity. + +```plaintext +DELETE /api/scim/v2/application/Users/:id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| ------------ | ------ | -------- | ------------------------- | +| `id` | string | yes | External UID of the user. | + +Example request: + +```shell +curl --verbose --request DELETE "https://gitlab.example.com/api/scim/v2/application/Users/f0b1d561c-21ff-4092-beab-8154b17f82f2" \ + --header "Authorization: Bearer <your_scim_token>" --header "Content-Type: application/scim+json" +``` + +Returns an empty response with a `204` status code if successful. + ### Available filters They match an expression as specified in [the RFC7644 filtering section](https://www.rfc-editor.org/rfc/rfc7644#section-3.4.2.2). diff --git a/doc/integration/jira/connect-app.md b/doc/integration/jira/connect-app.md index 85c0adc6100..859810e43c0 100644 --- a/doc/integration/jira/connect-app.md +++ b/doc/integration/jira/connect-app.md @@ -16,7 +16,7 @@ the GitLab.com for Jira Cloud app. ## Install the GitLab.com for Jira Cloud app **(FREE SAAS)** If you use GitLab.com and Jira Cloud, you can install the GitLab.com for Jira Cloud app. -If you do not use both of these environments, use the [Jira DVCS Connector](dvcs.md) or +If you do not use both of these environments, use the [Jira DVCS Connector](dvcs/index.md) or [install GitLab.com for Jira Cloud app for self-managed instances](#install-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances). We recommend the GitLab.com for Jira Cloud app, because data is synchronized in real time. The DVCS connector updates data only once per hour. @@ -24,7 +24,7 @@ synchronized in real time. The DVCS connector updates data only once per hour. To configure the GitLab.com for Jira Cloud app, you must have at least the Maintainer role in the GitLab.com namespace. -This integration method supports [Smart Commits](dvcs.md#smart-commits). +This integration method supports [Smart Commits](dvcs/index.md#smart-commits). <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> For a walkthrough of the integration with GitLab.com for Jira Cloud app, watch diff --git a/doc/integration/jira/development_panel.md b/doc/integration/jira/development_panel.md index 03fea57bcc7..ee671dde3a8 100644 --- a/doc/integration/jira/development_panel.md +++ b/doc/integration/jira/development_panel.md @@ -69,8 +69,8 @@ To simplify administration, we recommend that a GitLab group maintainer or group | Jira usage | GitLab.com customers need | GitLab self-managed customers need | |------------|---------------------------|------------------------------------| -| [Atlassian cloud](https://www.atlassian.com/migration/assess/why-cloud) | The [GitLab.com for Jira Cloud app](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud?hosting=cloud&tab=overview) installed from the [Atlassian Marketplace](https://marketplace.atlassian.com). This method offers real-time sync between GitLab.com and Jira. For more information, see [GitLab.com for Jira Cloud app](connect-app.md). | The [GitLab.com for Jira Cloud app](https://marketplace.atlassian.com/). For more information, see [Connect the GitLab.com for Jira Cloud app for self-managed instances](connect-app.md#connect-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances). | -| Your own server | The [Jira DVCS (distributed version control system) connector](dvcs.md). This syncs data hourly. | The [Jira DVCS (distributed version control system) connector](dvcs.md). This syncs data hourly. | +| [Atlassian cloud](https://www.atlassian.com/migration/assess/why-cloud) | The [GitLab.com for Jira Cloud app](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud?hosting=cloud&tab=overview) installed from the [Atlassian Marketplace](https://marketplace.atlassian.com). This method offers real-time sync between GitLab.com and Jira. For more information, see [GitLab.com for Jira Cloud app](connect-app.md). | The GitLab.com for Jira Cloud app [using a workaround](connect-app.md#install-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances). When the `jira_connect_oauth_self_managed` feature flag is enabled, you can install the app from the [Atlassian Marketplace](https://marketplace.atlassian.com/). For more information, see [Connect the GitLab.com for Jira Cloud app for self-managed instances](connect-app.md#connect-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances). | +| Your own server | The [Jira DVCS (distributed version control system) connector](dvcs/index.md). This syncs data hourly. | The [Jira DVCS (distributed version control system) connector](dvcs/index.md). This syncs data hourly. | Each GitLab project can be configured to connect to an entire Jira instance. That means after configuration, one GitLab project can interact with all Jira projects in that instance. For: diff --git a/doc/integration/jira/dvcs.md b/doc/integration/jira/dvcs.md deleted file mode 100644 index 1300eb21ced..00000000000 --- a/doc/integration/jira/dvcs.md +++ /dev/null @@ -1,298 +0,0 @@ ---- -stage: Manage -group: Integrations -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments ---- - -# Jira DVCS connector **(FREE)** - -Use the Jira DVCS (distributed version control system) connector if you self-host -your Jira instance, and you want to sync information -between GitLab and Jira. If you use Jira Cloud, you should use the -[GitLab.com for Jira Cloud app](connect-app.md) unless you specifically need the -DVCS connector. - -When you configure the Jira DVCS connector, make sure your GitLab and Jira instances -are accessible. - -- **Self-managed GitLab**: Your GitLab instance must be accessible by Jira. -- **Jira Server**: Your network must allow access to your instance. -- **Jira Cloud**: Your instance must be accessible through the internet. - -NOTE: -When using GitLab 15.0 and later (including GitLab.com) with Jira Server, you might experience a [session token bug in Jira](https://jira.atlassian.com/browse/JSWSERVER-21389). As a workaround, ensure Jira Server is version 9.1.0 and later or 8.20.11 and later. - -## Smart Commits - -When connecting GitLab with Jira with DVCS, you can process your Jira issues using -special commands, called -[Smart Commits](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/), -in your commit messages. With Smart Commits, you can: - -- Comment on issues. -- Record time-tracking information against issues. -- Transition issues to any status defined in the Jira project's workflow. - -Commands must be in the first line of the commit message. The -[Jira Software documentation](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/) -contains more information about how Smart Commits work, and what commands are available -for your use. - -For Smart Commits to work, the committing user on GitLab must have a corresponding -user on Jira with the same email address or username. - -### Smart Commit syntax - -Smart Commits should follow the pattern of: - -```plaintext -<ISSUE_KEY> <ignored text> #<command> <optional command parameters> -``` - -Some examples: - -- Add a comment to a Jira issue: `KEY-123 fixes a bug #comment Bug is fixed.` -- Record time tracking: `KEY-123 #time 2w 4d 10h 52m Tracking work time.` -- Close an issue: `KEY-123 #close Closing issue` - -A Smart Commit message must not span more than one line (no carriage returns) but -you can still perform multiple actions in a single commit. For example: - -- Add time tracking, add a comment, and transition to **Closed**: - - ```plaintext - KEY-123 #time 2d 5h #comment Task completed ahead of schedule #close - ``` - -- Add a comment, transition to **In-progress**, and add time tracking: - - ```plaintext - KEY-123 #comment started working on the issue #in-progress #time 12d 5h - ``` - -## Configure a GitLab application for DVCS - -For projects in a single group we recommend you create a [group application](../oauth_provider.md#create-a-group-owned-application). -For projects across multiple groups we recommend you create and use a `jira` user in GitLab, and use the account -only for integration work. A separate account ensures regular account -maintenance does not affect your integration. If a `jira` user or group application is not feasible, -you can set up this integration as an [instance-wide application](../oauth_provider.md#create-an-instance-wide-application) -or with a [user owned application](../oauth_provider.md#create-a-user-owned-application) instead. - -1. Navigate to the [appropriate **Applications** section](../oauth_provider.md). -1. In the **Name** field, enter a descriptive name for the integration, such as `Jira`. -1. In the **Redirect URI** field, enter the URI appropriate for your version of GitLab, - replacing `<gitlab.example.com>` with your GitLab instance domain: - - *For GitLab versions 13.0 and later* **and** *Jira versions 8.14 and later,* use the - generated `Redirect URL` from - [Linking GitLab accounts with Jira](https://confluence.atlassian.com/adminjiraserver/linking-gitlab-accounts-1027142272.html). - - *For GitLab versions 13.0 and later* **and** *Jira Cloud,* use `https://<gitlab.example.com>/login/oauth/callback`. - - *For GitLab versions 11.3 and later* **and** *Jira versions 8.13 and earlier,* use `https://<gitlab.example.com>/login/oauth/callback`. - If you use GitLab.com, the URL is `https://gitlab.com/login/oauth/callback`. - - *For GitLab versions 11.2 and earlier,* use - `https://<gitlab.example.com>/-/jira/login/oauth/callback`. - -1. For **Scopes**, select `api` and clear any other checkboxes. - - The DVCS connector requires a _write-enabled_ `api` scope to automatically create and manage required webhooks. -1. Select **Submit**. -1. Copy the **Application ID** and **Secret** values. - You need them to configure Jira. - -## Configure Jira for DVCS - -Configure this connection when you want to import all GitLab commits and branches, -for the groups you specify, into Jira. This import takes a few minutes and, after -it completes, refreshes every 60 minutes: - -1. Complete the [GitLab configuration](#configure-a-gitlab-application-for-dvcs). -1. Go to your DVCS accounts: - - *For Jira Server,* select **Settings (gear) > Applications > DVCS accounts**. - - *For Jira Cloud,* select **Settings (gear) > Products > DVCS accounts**. -1. To create a new integration, select the appropriate value for **Host**: - - *For Jira versions 8.14 and later:* Select **GitLab** or - **GitLab Self-Managed**. - - *For Jira Cloud or Jira versions 8.13 and earlier:* Select **GitHub Enterprise**. -1. For **Team or User Account**, enter either: - - *For Jira versions 8.14 and later:* - - The relative path of a top-level GitLab group that - [the GitLab user](#configure-a-gitlab-application-for-dvcs) has access to. - - *For Jira Cloud or Jira versions 8.13 and earlier:* - - The relative path of a top-level GitLab group that - [the GitLab user](#configure-a-gitlab-application-for-dvcs) has access to. - - The relative path of your personal namespace. - -1. In the **Host URL** field, enter the URI appropriate for your version of GitLab, - replacing `<gitlab.example.com>` with your GitLab instance domain: - - *For GitLab versions 11.3 and later,* use `https://<gitlab.example.com>`. - - *For GitLab versions 11.2 and earlier,* use - `https://<gitlab.example.com>/-/jira`. - -1. For **Client ID**, use the **Application ID** value from the previous section. -1. For **Client Secret**, use the **Secret** value from the previous section. -1. Ensure that the rest of the checkboxes are selected. -1. To create the DVCS account, select **Add** and then **Continue**. -1. Jira redirects to GitLab where you have to confirm the authorization. - GitLab then redirects back to Jira where the synced - projects should display in the new account. - -To connect additional GitLab projects from other GitLab top-level groups or -personal namespaces, repeat the previous steps with additional Jira DVCS accounts. - -After you configure the integration, read more about [how to test and use it](development_panel.md). - -## Refresh data imported to Jira - -Jira imports the commits and branches every 60 minutes for your projects. You -can refresh the data manually from the Jira interface: - -1. Sign in to your Jira instance as the user you configured the integration with. -1. Go to **Settings (gear) > Applications**. -1. Select **DVCS accounts**. -1. In the table, for the repository you want to refresh, in the **Last Activity** - column, select the icon: -  - -## Troubleshoot your DVCS connection - -Refer to the items in this section if you're having problems with your DVCS connector. - -### Jira cannot access GitLab server - -If you complete the **Add New Account** form, authorize access, and you receive -this error, Jira and GitLab cannot connect. No other error messages -appear in any logs: - -```plaintext -Error obtaining access token. Cannot access https://gitlab.example.com from Jira. -``` - -### SSL and TLS problems - -Problems with SSL and TLS can cause this error message: - -```plaintext -Error obtaining access token. Cannot access https://gitlab.example.com from Jira. -``` - -- The [GitLab Jira integration](index.md) requires - GitLab to connect to Jira. Any TLS issues that arise from a private certificate - authority or self-signed certificate are resolved - [on the GitLab server](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates), - as GitLab is the TLS client. -- The Jira Development panel integration requires Jira to connect to GitLab, which - causes Jira to be the TLS client. If your GitLab server's certificate is not - issued by a public certificate authority, add the appropriate certificate - (such as your organization's root certificate) to the Java Truststore on Jira's server. - -Refer to Atlassian's documentation and Atlassian Support for assistance setting -up Jira correctly: - -- [Add a certificate](https://confluence.atlassian.com/kb/how-to-import-a-public-ssl-certificate-into-a-jvm-867025849.html) - to the trust store. - - The simplest approach is [`keytool`](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html). - - Add additional roots to Java's default Truststore (`cacerts`) to allow Jira to - also trust public certificate authorities. - - If the integration stops working after upgrading Jira's Java runtime, the - `cacerts` Truststore may have been replaced during the upgrade. - -- Troubleshoot connectivity [up to and including TLS handshaking](https://confluence.atlassian.com/kb/unable-to-connect-to-ssl-services-due-to-pkix-path-building-failed-error-779355358.html), - using the `SSLPoke` Java class. -- Download the class from Atlassian's knowledge base to a directory on Jira's server, such as `/tmp`. -- Use the same Java runtime as Jira. -- Pass all networking-related parameters that Jira is called with, such as proxy - settings or an alternative root Truststore (`-Djavax.net.ssl.trustStore`): - -```shell -${JAVA_HOME}/bin/java -Djavax.net.ssl.trustStore=/var/atlassian/application-data/jira/cacerts -classpath /tmp SSLPoke gitlab.example.com 443 -``` - -The message `Successfully connected` indicates a successful TLS handshake. - -If there are problems, the Java TLS library generates errors that you can -look up for more detail. - -### Scope error when connecting to Jira using DVCS - -```plaintext -The requested scope is invalid, unknown, or malformed. -``` - -Potential resolutions: - -1. Verify that the URL shown in the browser after being redirected from Jira in the - [Jira DVCS connector setup](#configure-jira-for-dvcs) includes `scope=api` in - the query string. -1. If `scope=api` is missing from the URL, edit the - [GitLab account configuration](#configure-a-gitlab-application-for-dvcs). Review - the **Scopes** field and ensure the `api` checkbox is selected. - -### Jira error adding account and no repositories listed - -After you complete the **Add New Account** form in Jira and authorize access, you might -encounter these issues: - -- An `Error! Failed adding the account: [Error retrieving list of repositories]` error. -- An `Account is already integrated with JIRA` error when you select **Try Again**. -- An account is visible in the DVCS accounts view, but no repositories are listed. - -To resolve this issue: - -- If you're using GitLab Free, ensure you're using GitLab 13.4 or later. -- If you're using GitLab versions 11.10-12.7, upgrade to GitLab 12.8.10 or later - to resolve [an identified issue](https://gitlab.com/gitlab-org/gitlab/-/issues/37012). - -[Contact GitLab Support](https://about.gitlab.com/support/) if none of these reasons apply. - -### `410 : Gone` error when connecting to Jira - -When you connect to Jira and synchronize repositories, you may receive a `410 : Gone` error. - -This issue occurs when you use the Jira DVCS connector and your integration is configured to use **GitHub Enterprise**. - -For more information and possible fixes, see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340160). - -### Synchronization issues - -If Jira displays incorrect information, such as deleted branches, you may have to -resynchronize the information: - -1. In Jira, select **Jira Administration > Applications > DVCS accounts**. -1. For the account (group or subgroup), select - **Refresh repositories** from the **{ellipsis_h}** (ellipsis) menu. -1. For each project, next to the **Last activity** date: - - To perform a *soft resync*, select the sync icon. - - To complete a *full sync*, press `Shift` and select the sync icon. - -For more information, read -[Atlassian's documentation](https://support.atlassian.com/jira-cloud-administration/docs/integrate-with-development-tools/). - -### `Sync Failed` error when refreshing repository data - -If you get a `Sync Failed` error in Jira when [refreshing repository data](#refresh-data-imported-to-jira) for specific projects, check your DVCS connector logs. Look for errors that occur when executing requests to API resources in GitLab. For example: - -```plaintext -Failed to execute request [https://gitlab.com/api/v4/projects/:id/merge_requests?page=1&per_page=100 GET https://gitlab.com/api/v4/projects/:id/merge_requests?page=1&per_page=100 returned a response status of 403 Forbidden] errors: -{"message":"403 Forbidden"} -``` - -If you find a `{"message":"403 Forbidden"}` error, it is possible that this specific project has some [GitLab features disabled](../../user/project/settings/index.md#configure-project-visibility-features-and-permissions). -In the example above, the merge requests feature is disabled. - -To resolve the issue, enable the relevant feature: - -1. On the top bar, select **Main menu > Projects** and find your project. -1. On the left sidebar, select **Settings > General**. -1. Expand **Visibility, project features, permissions**. -1. Use the toggles to enable the features as needed. - -### Find webhook logs in a DVCS-linked project - -To find webhook logs in a DVCS-linked project: - -1. On the top bar, select **Main menu > Projects** and find your project. -1. On the left sidebar, select **Settings > Webhooks**. -1. Scroll down to **Project Hooks**. -1. Next to the log that points to your Jira instance, select **Edit**. -1. Scroll down to **Recent events**. - -If you can't find webhook logs in your project, check your DVCS setup for problems. diff --git a/doc/integration/jira/dvcs/index.md b/doc/integration/jira/dvcs/index.md new file mode 100644 index 00000000000..1fa96e20d01 --- /dev/null +++ b/doc/integration/jira/dvcs/index.md @@ -0,0 +1,152 @@ +--- +stage: Manage +group: Integrations +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Jira DVCS connector **(FREE)** + +Use the Jira DVCS (distributed version control system) connector if you self-host +your Jira instance, and you want to sync information +between GitLab and Jira. If you use Jira Cloud, you should use the +[GitLab.com for Jira Cloud app](../connect-app.md) unless you specifically need the +DVCS connector. + +When you configure the Jira DVCS connector, make sure your GitLab and Jira instances +are accessible. + +- **Self-managed GitLab**: Your GitLab instance must be accessible by Jira. +- **Jira Server**: Your network must allow access to your instance. +- **Jira Cloud**: Your instance must be accessible through the internet. + +NOTE: +When using GitLab 15.0 and later (including GitLab.com) with Jira Server, you might experience a [session token bug in Jira](https://jira.atlassian.com/browse/JSWSERVER-21389). As a workaround, ensure Jira Server is version 9.1.0 and later or 8.20.11 and later. + +## Smart Commits + +When connecting GitLab with Jira with DVCS, you can process your Jira issues using +special commands, called +[Smart Commits](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/), +in your commit messages. With Smart Commits, you can: + +- Comment on issues. +- Record time-tracking information against issues. +- Transition issues to any status defined in the Jira project's workflow. + +Commands must be in the first line of the commit message. The +[Jira Software documentation](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/) +contains more information about how Smart Commits work, and what commands are available +for your use. + +For Smart Commits to work, the committing user on GitLab must have a corresponding +user on Jira with the same email address or username. + +### Smart Commit syntax + +Smart Commits should follow the pattern of: + +```plaintext +<ISSUE_KEY> <ignored text> #<command> <optional command parameters> +``` + +Some examples: + +- Add a comment to a Jira issue: `KEY-123 fixes a bug #comment Bug is fixed.` +- Record time tracking: `KEY-123 #time 2w 4d 10h 52m Tracking work time.` +- Close an issue: `KEY-123 #close Closing issue` + +A Smart Commit message must not span more than one line (no carriage returns) but +you can still perform multiple actions in a single commit. For example: + +- Add time tracking, add a comment, and transition to **Closed**: + + ```plaintext + KEY-123 #time 2d 5h #comment Task completed ahead of schedule #close + ``` + +- Add a comment, transition to **In-progress**, and add time tracking: + + ```plaintext + KEY-123 #comment started working on the issue #in-progress #time 12d 5h + ``` + +## Configure a GitLab application for DVCS + +For projects in a single group we recommend you create a [group application](../../oauth_provider.md#create-a-group-owned-application). +For projects across multiple groups we recommend you create and use a `jira` user in GitLab, and use the account +only for integration work. A separate account ensures regular account +maintenance does not affect your integration. If a `jira` user or group application is not feasible, +you can set up this integration as an [instance-wide application](../../oauth_provider.md#create-an-instance-wide-application) +or with a [user owned application](../../oauth_provider.md#create-a-user-owned-application) instead. + +1. Navigate to the [appropriate **Applications** section](../../oauth_provider.md). +1. In the **Name** field, enter a descriptive name for the integration, such as `Jira`. +1. In the **Redirect URI** field, enter the URI appropriate for your version of GitLab, + replacing `<gitlab.example.com>` with your GitLab instance domain: + - *For GitLab versions 13.0 and later* **and** *Jira versions 8.14 and later,* use the + generated `Redirect URL` from + [Linking GitLab accounts with Jira](https://confluence.atlassian.com/adminjiraserver/linking-gitlab-accounts-1027142272.html). + - *For GitLab versions 13.0 and later* **and** *Jira Cloud,* use `https://<gitlab.example.com>/login/oauth/callback`. + - *For GitLab versions 11.3 and later* **and** *Jira versions 8.13 and earlier,* use `https://<gitlab.example.com>/login/oauth/callback`. + If you use GitLab.com, the URL is `https://gitlab.com/login/oauth/callback`. + - *For GitLab versions 11.2 and earlier,* use + `https://<gitlab.example.com>/-/jira/login/oauth/callback`. + +1. For **Scopes**, select `api` and clear any other checkboxes. + - The DVCS connector requires a _write-enabled_ `api` scope to automatically create and manage required webhooks. +1. Select **Submit**. +1. Copy the **Application ID** and **Secret** values. + You need them to configure Jira. + +## Configure Jira for DVCS + +Configure this connection when you want to import all GitLab commits and branches, +for the groups you specify, into Jira. This import takes a few minutes and, after +it completes, refreshes every 60 minutes: + +1. Complete the [GitLab configuration](#configure-a-gitlab-application-for-dvcs). +1. Go to your DVCS accounts: + - *For Jira Server,* select **Settings (gear) > Applications > DVCS accounts**. + - *For Jira Cloud,* select **Settings (gear) > Products > DVCS accounts**. +1. To create a new integration, select the appropriate value for **Host**: + - *For Jira versions 8.14 and later:* Select **GitLab** or + **GitLab Self-Managed**. + - *For Jira Cloud or Jira versions 8.13 and earlier:* Select **GitHub Enterprise**. +1. For **Team or User Account**, enter either: + - *For Jira versions 8.14 and later:* + - The relative path of a top-level GitLab group that + [the GitLab user](#configure-a-gitlab-application-for-dvcs) has access to. + - *For Jira Cloud or Jira versions 8.13 and earlier:* + - The relative path of a top-level GitLab group that + [the GitLab user](#configure-a-gitlab-application-for-dvcs) has access to. + - The relative path of your personal namespace. + +1. In the **Host URL** field, enter the URI appropriate for your version of GitLab, + replacing `<gitlab.example.com>` with your GitLab instance domain: + - *For GitLab versions 11.3 and later,* use `https://<gitlab.example.com>`. + - *For GitLab versions 11.2 and earlier,* use + `https://<gitlab.example.com>/-/jira`. + +1. For **Client ID**, use the **Application ID** value from the previous section. +1. For **Client Secret**, use the **Secret** value from the previous section. +1. Ensure that the rest of the checkboxes are selected. +1. To create the DVCS account, select **Add** and then **Continue**. +1. Jira redirects to GitLab where you have to confirm the authorization. + GitLab then redirects back to Jira where the synced + projects should display in the new account. + +To connect additional GitLab projects from other GitLab top-level groups or +personal namespaces, repeat the previous steps with additional Jira DVCS accounts. + +After you configure the integration, read more about [how to test and use it](../development_panel.md). + +## Refresh data imported to Jira + +Jira imports the commits and branches every 60 minutes for your projects. You +can refresh the data manually from the Jira interface: + +1. Sign in to your Jira instance as the user you configured the integration with. +1. Go to **Settings (gear) > Applications**. +1. Select **DVCS accounts**. +1. In the table, for the repository you want to refresh, in the **Last Activity** + column, select the icon. diff --git a/doc/integration/jira/dvcs/troubleshooting.md b/doc/integration/jira/dvcs/troubleshooting.md new file mode 100644 index 00000000000..53002f8dbf2 --- /dev/null +++ b/doc/integration/jira/dvcs/troubleshooting.md @@ -0,0 +1,150 @@ +--- +stage: Manage +group: Integrations +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Troubleshooting Jira DVCS connector **(FREE)** + +Refer to the items in this section if you're having problems with your DVCS connector. + +## Jira cannot access GitLab server + +If you complete the **Add New Account** form, authorize access, and you receive +this error, Jira and GitLab cannot connect. No other error messages +appear in any logs: + +```plaintext +Error obtaining access token. Cannot access https://gitlab.example.com from Jira. +``` + +## SSL and TLS problems + +Problems with SSL and TLS can cause this error message: + +```plaintext +Error obtaining access token. Cannot access https://gitlab.example.com from Jira. +``` + +- The [GitLab Jira integration](../index.md) requires + GitLab to connect to Jira. Any TLS issues that arise from a private certificate + authority or self-signed certificate are resolved + [on the GitLab server](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates), + as GitLab is the TLS client. +- The Jira Development panel integration requires Jira to connect to GitLab, which + causes Jira to be the TLS client. If your GitLab server's certificate is not + issued by a public certificate authority, add the appropriate certificate + (such as your organization's root certificate) to the Java Truststore on Jira's server. + +Refer to Atlassian's documentation and Atlassian Support for assistance setting +up Jira correctly: + +- [Add a certificate](https://confluence.atlassian.com/kb/how-to-import-a-public-ssl-certificate-into-a-jvm-867025849.html) + to the trust store. + - The simplest approach is [`keytool`](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html). + - Add additional roots to Java's default Truststore (`cacerts`) to allow Jira to + also trust public certificate authorities. + - If the integration stops working after upgrading Jira's Java runtime, the + `cacerts` Truststore may have been replaced during the upgrade. + +- Troubleshoot connectivity [up to and including TLS handshaking](https://confluence.atlassian.com/kb/unable-to-connect-to-ssl-services-due-to-pkix-path-building-failed-error-779355358.html), + using the `SSLPoke` Java class. +- Download the class from Atlassian's knowledge base to a directory on Jira's server, such as `/tmp`. +- Use the same Java runtime as Jira. +- Pass all networking-related parameters that Jira is called with, such as proxy + settings or an alternative root Truststore (`-Djavax.net.ssl.trustStore`): + +```shell +${JAVA_HOME}/bin/java -Djavax.net.ssl.trustStore=/var/atlassian/application-data/jira/cacerts -classpath /tmp SSLPoke gitlab.example.com 443 +``` + +The message `Successfully connected` indicates a successful TLS handshake. + +If there are problems, the Java TLS library generates errors that you can +look up for more detail. + +## Scope error when connecting to Jira using DVCS + +```plaintext +The requested scope is invalid, unknown, or malformed. +``` + +Potential resolutions: + +1. Verify that the URL shown in the browser after being redirected from Jira in the + [Jira DVCS connector setup](index.md#configure-jira-for-dvcs) includes `scope=api` in + the query string. +1. If `scope=api` is missing from the URL, edit the + [GitLab account configuration](index.md#configure-a-gitlab-application-for-dvcs). Review + the **Scopes** field and ensure the `api` checkbox is selected. + +## Jira error adding account and no repositories listed + +After you complete the **Add New Account** form in Jira and authorize access, you might +encounter these issues: + +- An `Error! Failed adding the account: [Error retrieving list of repositories]` error. +- An `Account is already integrated with JIRA` error when you select **Try Again**. +- An account is visible in the DVCS accounts view, but no repositories are listed. + +To resolve this issue: + +- If you're using GitLab Free, ensure you're using GitLab 13.4 or later. +- If you're using GitLab versions 11.10-12.7, upgrade to GitLab 12.8.10 or later + to resolve [an identified issue](https://gitlab.com/gitlab-org/gitlab/-/issues/37012). + +[Contact GitLab Support](https://about.gitlab.com/support/) if none of these reasons apply. + +## `410 : Gone` error when connecting to Jira + +When you connect to Jira and synchronize repositories, you may receive a `410 : Gone` error. + +This issue occurs when you use the Jira DVCS connector and your integration is configured to use **GitHub Enterprise**. + +For more information and possible fixes, see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340160). + +## Synchronization issues + +If Jira displays incorrect information, such as deleted branches, you may have to +resynchronize the information: + +1. In Jira, select **Jira Administration > Applications > DVCS accounts**. +1. For the account (group or subgroup), select + **Refresh repositories** from the **{ellipsis_h}** (ellipsis) menu. +1. For each project, next to the **Last activity** date: + - To perform a *soft resync*, select the sync icon. + - To complete a *full sync*, press `Shift` and select the sync icon. + +For more information, read +[Atlassian's documentation](https://support.atlassian.com/jira-cloud-administration/docs/integrate-with-development-tools/). + +## `Sync Failed` error when refreshing repository data + +If you get a `Sync Failed` error in Jira when [refreshing repository data](index.md#refresh-data-imported-to-jira) for specific projects, check your DVCS connector logs. Look for errors that occur when executing requests to API resources in GitLab. For example: + +```plaintext +Failed to execute request [https://gitlab.com/api/v4/projects/:id/merge_requests?page=1&per_page=100 GET https://gitlab.com/api/v4/projects/:id/merge_requests?page=1&per_page=100 returned a response status of 403 Forbidden] errors: +{"message":"403 Forbidden"} +``` + +If you find a `{"message":"403 Forbidden"}` error, it is possible that this specific project has some [GitLab features disabled](../../../user/project/settings/index.md#configure-project-visibility-features-and-permissions). +In the example above, the merge requests feature is disabled. + +To resolve the issue, enable the relevant feature: + +1. On the top bar, select **Main menu > Projects** and find your project. +1. On the left sidebar, select **Settings > General**. +1. Expand **Visibility, project features, permissions**. +1. Use the toggles to enable the features as needed. + +## Find webhook logs in a DVCS-linked project + +To find webhook logs in a DVCS-linked project: + +1. On the top bar, select **Main menu > Projects** and find your project. +1. On the left sidebar, select **Settings > Webhooks**. +1. Scroll down to **Project Hooks**. +1. Next to the log that points to your Jira instance, select **Edit**. +1. Scroll down to **Recent events**. + +If you can't find webhook logs in your project, check your DVCS setup for problems. diff --git a/doc/integration/jira/img/jira_dev_panel_manual_refresh.png b/doc/integration/jira/img/jira_dev_panel_manual_refresh.png Binary files differdeleted file mode 100644 index dc92d533bde..00000000000 --- a/doc/integration/jira/img/jira_dev_panel_manual_refresh.png +++ /dev/null diff --git a/doc/topics/gitlab_flow.md b/doc/topics/gitlab_flow.md index 2f3cd2b8588..89ab5e1cf2e 100644 --- a/doc/topics/gitlab_flow.md +++ b/doc/topics/gitlab_flow.md @@ -128,6 +128,7 @@ graph TD D ==> G[main branch] F ==> H[main branch] end +``` If you need to know what code is in production, you can check out the production branch to see. The approximate time of deployment is visible as the merge commit in the version control system. diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index 33fd9cdb7ce..76b43ff0f76 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -160,6 +160,20 @@ See [migrated group items](https://docs.gitlab.com/ee/user/group/import/#migrate <div class="deprecation removal-160 breaking-change"> +### GraphQL: The `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` enum is deprecated. Use `DISABLED_AND_OVERRIDABLE` instead + +Planned removal: GitLab <span class="removal-milestone">16.0</span> <span class="removal-date"></span> + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +In GitLab 16.0, the `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` GraphQL enum type will be replaced with the value, `DISABLED_AND_OVERRIDABLE`. + +</div> + +<div class="deprecation removal-160 breaking-change"> + ### Limit personal access token and deploy token's access with external authorization Planned removal: GitLab <span class="removal-milestone">16.0</span> <span class="removal-date"></span> diff --git a/doc/user/admin_area/reporting/git_abuse_rate_limit.md b/doc/user/admin_area/reporting/git_abuse_rate_limit.md index f700b8b1ea3..66d1173058e 100644 --- a/doc/user/admin_area/reporting/git_abuse_rate_limit.md +++ b/doc/user/admin_area/reporting/git_abuse_rate_limit.md @@ -4,12 +4,12 @@ group: Anti-Abuse info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Git abuse rate limit (administration) **(ULTIMATE SELF)** +# Git abuse rate limit (administration) **(ULTIMATE)** > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8066) in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `git_abuse_rate_limit_feature_flag`. Disabled by default. FLAG: -On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `git_abuse_rate_limit_feature_flag`. On GitLab.com, this feature is not available. +On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `git_abuse_rate_limit_feature_flag`. On GitLab.com, this feature is available. Git abuse rate limiting is a feature to automatically [ban users](../moderate_users.md#ban-and-unban-users) who download or clone more than a specified number of repositories in any project in the instance within a given time frame. Banned users cannot sign in to the instance and cannot access any non-public group via HTTP or SSH. diff --git a/doc/user/group/reporting/git_abuse_rate_limit.md b/doc/user/group/reporting/git_abuse_rate_limit.md index 1cf3a9dbe7d..a5515079294 100644 --- a/doc/user/group/reporting/git_abuse_rate_limit.md +++ b/doc/user/group/reporting/git_abuse_rate_limit.md @@ -4,12 +4,12 @@ group: Anti-Abuse info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Git abuse rate limit **(ULTIMATE SELF)** +# Git abuse rate limit **(ULTIMATE)** > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8066) in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `limit_unique_project_downloads_per_namespace_user`. Disabled by default. FLAG: -On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `limit_unique_project_downloads_per_namespace_user`. On GitLab.com, this feature is not available. +On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `limit_unique_project_downloads_per_namespace_user`. On GitLab.com, this feature is available. Git abuse rate limiting is a feature to automatically ban users who download or clone more than a specified number of repositories in a group or any of its subgroups within a given time frame. Banned users cannot access the main group or any of its non-public subgroups via HTTP or SSH. Access to unrelated groups is unaffected. @@ -31,6 +31,10 @@ If automatic banning is enabled, users with the Owner role for the main group re ## Unban a user +Prerequisites: + +- You must have the Owner role. + 1. On the left sidebar, select **Group information > Members**. 1. Select the **Banned** tab. 1. For the account you want to unban, select **Unban**. diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 18af39f4271..8c30c246566 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -15,7 +15,7 @@ GitLab SAML SSO SCIM doesn't support updating users. When SCIM is enabled for a GitLab group, membership of that group is synchronized between GitLab and an identity provider. -The [internal GitLab SCIM API](../../../development/internal_api/index.md#scim-api) implements part of [the RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644). +The [internal GitLab group SCIM API](../../../development/internal_api/index.md#group-scim-api) implements part of [the RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644). ## Configure GitLab @@ -121,7 +121,7 @@ attributes and modify them accordingly. In particular, the `objectId` source att target attribute. If a mapping is not listed in the table, use the Azure Active Directory defaults. For a list of required attributes, -refer to the [internal SCIM API](../../../development/internal_api/index.md#scim-api) documentation. +refer to the [internal group SCIM API](../../../development/internal_api/index.md#group-scim-api) documentation. ### Configure Okta diff --git a/doc/user/group/saml_sso/troubleshooting_scim.md b/doc/user/group/saml_sso/troubleshooting_scim.md index 22562c51e9e..939ed804a99 100644 --- a/doc/user/group/saml_sso/troubleshooting_scim.md +++ b/doc/user/group/saml_sso/troubleshooting_scim.md @@ -100,7 +100,7 @@ Changing the SAML or SCIM configuration or provider can cause the following prob GitLab.com administrators can search for SCIM requests in the `api_json.log` using the `pubsub-rails-inf-gprd-*` index in [Kibana](https://about.gitlab.com/handbook/support/workflows/kibana.html#using-kibana). Use the following filters based -on the internal [SCIM API](../../../development/internal_api/index.md#scim-api): +on the internal [group SCIM API](../../../development/internal_api/index.md#group-scim-api): - `json.path`: `/scim/v2/groups/<group-path>` - `json.params.value`: `<externalId>` diff --git a/lib/api/entities/bulk_imports/entity.rb b/lib/api/entities/bulk_imports/entity.rb index a7add31ff42..176d10b2580 100644 --- a/lib/api/entities/bulk_imports/entity.rb +++ b/lib/api/entities/bulk_imports/entity.rb @@ -9,7 +9,11 @@ module API expose :status_name, as: :status, documentation: { type: 'string', example: 'created', values: %w[created started finished timeout failed] } + expose :entity_type, documentation: { type: 'string', values: %w[group project] } expose :source_full_path, documentation: { type: 'string', example: 'source_group' } + expose :full_path, as: :destination_full_path, documentation: { + type: 'string', example: 'some_group/source_project' + } expose :destination_name, documentation: { type: 'string', example: 'destination_slug' } # deprecated expose :destination_slug, documentation: { type: 'string', example: 'destination_slug' } expose :destination_namespace, documentation: { type: 'string', example: 'destination_path' } diff --git a/lib/gitlab/ci/config/external/file/artifact.rb b/lib/gitlab/ci/config/external/file/artifact.rb index 21a57640aee..140cbfac5c1 100644 --- a/lib/gitlab/ci/config/external/file/artifact.rb +++ b/lib/gitlab/ci/config/external/file/artifact.rb @@ -38,10 +38,6 @@ module Gitlab private - def project - context&.parent_pipeline&.project - end - def validate_context! context.logger.instrument(:config_file_artifact_validate_context) do if !creating_child_pipeline? diff --git a/lib/gitlab/pages/cache_control.rb b/lib/gitlab/pages/cache_control.rb index be39e52b342..a24d958b7e5 100644 --- a/lib/gitlab/pages/cache_control.rb +++ b/lib/gitlab/pages/cache_control.rb @@ -16,8 +16,8 @@ module Gitlab PAYLOAD_CACHE_KEY = '%{settings_cache_key}_%{settings_hash}' class << self - def for_project(project_id) - new(type: :project, id: project_id) + def for_domain(domain_id) + new(type: :domain, id: domain_id) end def for_namespace(namespace_id) @@ -26,7 +26,7 @@ module Gitlab end def initialize(type:, id:) - raise(ArgumentError, "type must be :namespace or :project") unless %i[namespace project].include?(type) + raise(ArgumentError, "type must be :namespace or :domain") unless %i[namespace domain].include?(type) @type = type @id = id @@ -50,7 +50,9 @@ module Gitlab .map { |hash| payload_cache_key_for(hash) } .push(settings_cache_key) - Rails.cache.delete_multi(keys) + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do + Rails.cache.delete_multi(keys) + end end private diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5834db67125..da7029d383b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7417,9 +7417,6 @@ msgstr "" msgid "BulkImport|Destination" msgstr "" -msgid "BulkImport|Destination group" -msgstr "" - msgid "BulkImport|Existing groups" msgstr "" @@ -7429,7 +7426,7 @@ msgstr "" msgid "BulkImport|Following data will not be migrated: %{bullets} Contact system administrator of %{host} to upgrade GitLab if you need this data in your migration" msgstr "" -msgid "BulkImport|Group import history" +msgid "BulkImport|GitLab Migration history" msgstr "" msgid "BulkImport|History" @@ -7507,7 +7504,7 @@ msgstr "" msgid "BulkImport|Update of import statuses with realtime changes failed" msgstr "" -msgid "BulkImport|Your imported groups will appear here." +msgid "BulkImport|Your imported groups and projects will appear here." msgstr "" msgid "BulkImport|Your imported projects will appear here." @@ -20487,6 +20484,9 @@ msgstr "" msgid "Help translate GitLab into your language" msgstr "" +msgid "Help translate to your language" +msgstr "" + msgid "Helps prevent bots from brute-force attacks." msgstr "" @@ -24402,6 +24402,9 @@ msgstr[1] "" msgid "Last %{days} days" msgstr "" +msgid "Last 180 days" +msgstr "" + msgid "Last 2 weeks" msgstr "" diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb index b29bbef0c40..9cc740fcbef 100644 --- a/spec/controllers/projects/pages_domains_controller_spec.rb +++ b/spec/controllers/projects/pages_domains_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::PagesDomainsController do +RSpec.describe Projects::PagesDomainsController, feature_category: :pages do let(:user) { create(:user) } let(:project) { create(:project) } let!(:pages_domain) { create(:pages_domain, project: project) } @@ -70,6 +70,7 @@ RSpec.describe Projects::PagesDomainsController do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: kind_of(Numeric), domain: pages_domain_params[:domain] ) @@ -119,6 +120,7 @@ RSpec.describe Projects::PagesDomainsController do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: pages_domain.id, domain: pages_domain.domain ) end @@ -226,6 +228,7 @@ RSpec.describe Projects::PagesDomainsController do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: pages_domain.id, domain: pages_domain.domain ) @@ -251,6 +254,7 @@ RSpec.describe Projects::PagesDomainsController do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: pages_domain.id, domain: pages_domain.domain ) end diff --git a/spec/features/groups/import_export/migration_history_spec.rb b/spec/features/groups/import_export/migration_history_spec.rb index 4f3dba89c61..9fc9c7898d1 100644 --- a/spec/features/groups/import_export/migration_history_spec.rb +++ b/spec/features/groups/import_export/migration_history_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Import/Export - GitLab migration history', :js, feature_category wait_for_requests - expect(page).to have_content 'Group import history' + expect(page).to have_content 'GitLab Migration history' expect(page.find('tbody')).to have_css('tr', count: 2) end end diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci.json index 666a4852957..17a1b4474b6 100644 --- a/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci.json +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci.json @@ -107,7 +107,6 @@ "container_scanning": "scan2.json", "dast": "dast.json", "license_management": "license.json", - "performance": "performance.json", "metrics": "metrics.txt" } }, @@ -160,7 +159,6 @@ "container_scanning": ["scan2.json"], "dast": ["dast.json"], "license_management": ["license.json"], - "performance": ["performance.json"], "metrics": ["metrics.txt"] } }, diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml index 1902131de0a..996a48f7bc6 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml @@ -1,15 +1,28 @@ -# invalid artifacts:when -artifacts-when-unknown: +# invalid artifact:reports:browser_performance +browser_performance no paths: artifacts: - when: unknown + reports: + browser_performance: -artifacts-when-array: +## Lists (or globs) are not allowed! +browser_performance list of string paths: artifacts: - when: [always] + reports: + browser_performance: + - foo + - ./bar/baz -artifacts-when-boolean: +browser_performance mixed list of string paths and globs: artifacts: - when: true + reports: + browser_performance: + - ./foo + - "bar/*.baz" + +browser_performance string array: + artifacts: + reports: + browser_performance: ["foo", "blah"] # invalid artifact:reports:cyclonedx cyclonedx no paths: @@ -33,4 +46,24 @@ cyclonedx not an array or string: coverage-report-is-string: artifacts: reports: - coverage_report: cobertura
\ No newline at end of file + coverage_report: cobertura + +# invalid artifact:reports:performance +# Superceded by: artifact:reports:browser_performance +performance string path: + artifacts: + reports: + performance: foo + +# invalid artifacts:when +artifacts-when-unknown: + artifacts: + when: unknown + +artifacts-when-array: + artifacts: + when: [always] + +artifacts-when-boolean: + artifacts: + when: true diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml index 37de2d00382..70761a09b58 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml @@ -1,12 +1,8 @@ -# valid artifacts:when -artifacts-when-on-failure: - artifacts: - when: on_failure - -artifacts-no-when: +# valid artifact:reports:browser_performance +browser_performance string path: artifacts: - paths: - - binaries/ + reports: + browser_performance: foo # valid artifact:reports:cyclonedx cyclonedx string path: @@ -44,4 +40,14 @@ coverage-report-cobertura: coverage-report-null: artifacts: reports: - coverage_report: null
\ No newline at end of file + coverage_report: null + +# valid artifacts:when +artifacts-when-on-failure: + artifacts: + when: on_failure + +artifacts-no-when: + artifacts: + paths: + - binaries/ diff --git a/spec/frontend/language_switcher/components/app_spec.js b/spec/frontend/language_switcher/components/app_spec.js index 6a1b94cd813..effb71c2775 100644 --- a/spec/frontend/language_switcher/components/app_spec.js +++ b/spec/frontend/language_switcher/components/app_spec.js @@ -1,3 +1,4 @@ +import { GlLink } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import LanguageSwitcherApp from '~/language_switcher/components/app.vue'; import { PREFERRED_LANGUAGE_COOKIE_KEY } from '~/language_switcher/constants'; @@ -29,6 +30,7 @@ describe('<LanguageSwitcher />', () => { const getPreferredLanguage = () => wrapper.find('.gl-dropdown-button-text').text(); const findLanguageDropdownItem = (code) => wrapper.findByTestId(`language_switcher_lang_${code}`); + const findFooter = () => wrapper.findByTestId('footer'); it('preferred language', () => { expect(getPreferredLanguage()).toBe(EN.text); @@ -59,4 +61,12 @@ describe('<LanguageSwitcher />', () => { expect(utils.setCookie).toHaveBeenCalledWith(PREFERRED_LANGUAGE_COOKIE_KEY, ES.value); window.location = originalLocation; }); + + it('renders footer link', () => { + const link = findFooter().findComponent(GlLink); + + // Assert against actual value so we can implicitly test `helpPagePath` call + expect(link.attributes('href')).toBe('/help/development/i18n/translation.md'); + expect(link.text()).toBe(LanguageSwitcherApp.HELP_TRANSLATE_MSG); + }); }); diff --git a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js index 1790a9c9bf5..fbcae87f331 100644 --- a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js +++ b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js @@ -1,4 +1,4 @@ -import { GlEmptyState, GlLoadingIcon, GlTable } from '@gitlab/ui'; +import { GlEmptyState, GlLoadingIcon, GlTableLite } from '@gitlab/ui'; import { mount, shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; @@ -23,7 +23,9 @@ describe('BulkImportsHistoryApp', () => { id: 1, bulk_import_id: 1, status: 'finished', + entity_type: 'group', source_full_path: 'top-level-group-12', + destination_full_path: 'h5bp/top-level-group-12', destination_name: 'top-level-group-12', destination_namespace: 'h5bp', created_at: '2021-07-08T10:03:44.743Z', @@ -33,8 +35,10 @@ describe('BulkImportsHistoryApp', () => { id: 2, bulk_import_id: 2, status: 'failed', + entity_type: 'project', source_full_path: 'autodevops-demo', destination_name: 'autodevops-demo', + destination_full_path: 'some-group/autodevops-demo', destination_namespace: 'flightjs', parent_id: null, namespace_id: null, @@ -74,6 +78,7 @@ describe('BulkImportsHistoryApp', () => { beforeEach(() => { mock = new MockAdapter(axios); + mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS); }); afterEach(() => { @@ -97,11 +102,10 @@ describe('BulkImportsHistoryApp', () => { }); it('renders table with data when history is available', async () => { - mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS); createComponent(); await axios.waitForAll(); - const table = wrapper.findComponent(GlTable); + const table = wrapper.findComponent(GlTableLite); expect(table.exists()).toBe(true); // can't use .props() or .attributes() here expect(table.vm.$attrs.items).toHaveLength(DUMMY_RESPONSE.length); @@ -110,7 +114,6 @@ describe('BulkImportsHistoryApp', () => { it('changes page when requested by pagination bar', async () => { const NEW_PAGE = 4; - mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS); createComponent(); await axios.waitForAll(); mock.resetHistory(); @@ -126,7 +129,6 @@ describe('BulkImportsHistoryApp', () => { it('changes page size when requested by pagination bar', async () => { const NEW_PAGE_SIZE = 4; - mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS); createComponent(); await axios.waitForAll(); mock.resetHistory(); @@ -143,7 +145,6 @@ describe('BulkImportsHistoryApp', () => { it('sets up the local storage sync correctly', async () => { const NEW_PAGE_SIZE = 4; - mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS); createComponent(); await axios.waitForAll(); mock.resetHistory(); @@ -155,12 +156,27 @@ describe('BulkImportsHistoryApp', () => { }); it('renders correct url for destination group when relative_url is empty', async () => { - mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS); createComponent({ shallow: false }); await axios.waitForAll(); expect(wrapper.find('tbody tr a').attributes().href).toBe( - `/${DUMMY_RESPONSE[0].destination_namespace}/${DUMMY_RESPONSE[0].destination_name}`, + `/${DUMMY_RESPONSE[0].destination_full_path}`, + ); + }); + + it('adds slash to group urls', async () => { + createComponent({ shallow: false }); + await axios.waitForAll(); + + expect(wrapper.find('tbody tr a').text()).toBe(`${DUMMY_RESPONSE[0].destination_full_path}/`); + }); + + it('does not prefixes project urls with slash', async () => { + createComponent({ shallow: false }); + await axios.waitForAll(); + + expect(wrapper.findAll('tbody tr a').at(1).text()).toBe( + DUMMY_RESPONSE[1].destination_full_path, ); }); diff --git a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js index 8c18d2992ea..cf28eda5349 100644 --- a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js @@ -5,25 +5,32 @@ import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_a import SegmentedControlButtonGroup from '~/vue_shared/components/segmented_control_button_group.vue'; import { transformedAreaChartData, chartOptions } from '../mock_data'; +const charts = [ + { + range: 'test range 1', + title: 'title 1', + data: transformedAreaChartData, + }, + { + range: 'test range 2', + title: 'title 2', + data: transformedAreaChartData, + }, + { + range: 'test range 3', + title: 'title 3', + data: transformedAreaChartData, + }, + { + range: 'test range 4', + title: 'title 4', + data: transformedAreaChartData, + }, +]; + const DEFAULT_PROPS = { chartOptions, - charts: [ - { - range: 'test range 1', - title: 'title 1', - data: transformedAreaChartData, - }, - { - range: 'test range 2', - title: 'title 2', - data: transformedAreaChartData, - }, - { - range: 'test range 3', - title: 'title 3', - data: transformedAreaChartData, - }, - ], + charts, }; describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', () => { @@ -55,13 +62,13 @@ describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', ( wrapper = createWrapper(); }); - it('should default to the first chart', () => { - expect(findSegmentedControl().props('value')).toBe(0); + it('should default to the 3rd chart (last 90 days)', () => { + expect(findSegmentedControl().props('value')).toBe(2); }); it('should use the title and index as values', () => { const options = findSegmentedControl().props('options'); - expect(options).toHaveLength(3); + expect(options).toHaveLength(charts.length); expect(options).toEqual([ { text: 'title 1', @@ -75,6 +82,10 @@ describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', ( text: 'title 3', value: 2, }, + { + text: 'title 4', + value: 3, + }, ]); }); diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb index f5b36ebfa45..a77acb45978 100644 --- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb @@ -2,11 +2,13 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Config::External::File::Local do +RSpec.describe Gitlab::Ci::Config::External::File::Local, feature_category: :pipeline_authoring do + include RepoHelpers + let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { create(:user) } - let(:sha) { '12345' } + let(:sha) { project.commit.sha } let(:variables) { project.predefined_variables.to_runner_variables } let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } let(:params) { { local: location } } @@ -172,14 +174,17 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do let(:another_location) { 'another-config.yml' } let(:another_content) { 'rspec: JOB' } - before do - allow(project.repository).to receive(:blob_data_at).with(sha, location) - .and_return(content) - - allow(project.repository).to receive(:blob_data_at).with(sha, another_location) - .and_return(another_content) + let(:project_files) do + { + location => content, + another_location => another_content + } + end - local_file.validate! + around(:all) do |example| + create_and_delete_files(project, project_files) do + example.run + end end it 'does expand hash to include the template' do @@ -196,11 +201,11 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do it { is_expected.to eq( context_project: project.full_path, - context_sha: '12345', + context_sha: sha, type: :local, - location: location, - blob: "http://localhost/#{project.full_path}/-/blob/12345/lib/gitlab/ci/templates/existent-file.yml", - raw: "http://localhost/#{project.full_path}/-/raw/12345/lib/gitlab/ci/templates/existent-file.yml", + location: '/lib/gitlab/ci/templates/existent-file.yml', + blob: "http://localhost/#{project.full_path}/-/blob/#{sha}/lib/gitlab/ci/templates/existent-file.yml", + raw: "http://localhost/#{project.full_path}/-/raw/#{sha}/lib/gitlab/ci/templates/existent-file.yml", extra: {} ) } diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index b48a89059bf..5cdc9c21561 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do include StubRequests + include RepoHelpers let_it_be(:user) { create(:user) } @@ -313,9 +314,12 @@ RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do context "when using 'include' directive" do let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, group: group) } + let_it_be(:main_project) { create(:project, :repository, :public, group: group) } + + let(:project_sha) { project.commit.id } + let(:main_project_sha) { main_project.commit.id } - let(:project) { create(:project, :repository, group: group) } - let(:main_project) { create(:project, :repository, :public, group: group) } let(:pipeline) { build(:ci_pipeline, project: project) } let(:remote_location) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } @@ -356,36 +360,38 @@ RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do end let(:config) do - described_class.new(gitlab_ci_yml, project: project, pipeline: pipeline, sha: '12345', user: user) + described_class.new(gitlab_ci_yml, project: project, pipeline: pipeline, sha: project_sha, user: user) end - before do - stub_full_request(remote_location).to_return(body: remote_file_content) - - allow(project.repository) - .to receive(:blob_data_at).and_return(local_file_content) + let(:project_files) do + { + local_location => local_file_content + } + end - main_project.repository.create_file( - main_project.creator, - '.gitlab-ci.yml', - local_file_content, - message: 'Add README.md', - branch_name: 'master' - ) + let(:main_project_files) do + { + '.gitlab-ci.yml' => local_file_content, + '.another-ci-file.yml' => local_file_content + } + end - main_project.repository.create_file( - main_project.creator, - '.another-ci-file.yml', - local_file_content, - message: 'Add README.md', - branch_name: 'master' - ) + before do + stub_full_request(remote_location).to_return(body: remote_file_content) create(:ci_variable, project: project, key: "REF", value: "HEAD") create(:ci_group_variable, group: group, key: "FILENAME", value: ".gitlab-ci.yml") create(:ci_instance_variable, key: 'MAIN_PROJECT', value: main_project.full_path) end + around do |example| + create_and_delete_files(project, project_files) do + create_and_delete_files(main_project, main_project_files) do + example.run + end + end + end + context "when gitlab_ci_yml has valid 'include' defined" do it 'returns a composed hash' do composed_hash = { @@ -434,25 +440,25 @@ RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do expect(config.metadata[:includes]).to contain_exactly( { type: :local, location: local_location, - blob: "http://localhost/#{project.full_path}/-/blob/12345/#{local_location}", - raw: "http://localhost/#{project.full_path}/-/raw/12345/#{local_location}", + blob: "http://localhost/#{project.full_path}/-/blob/#{project_sha}/#{local_location}", + raw: "http://localhost/#{project.full_path}/-/raw/#{project_sha}/#{local_location}", extra: {}, context_project: project.full_path, - context_sha: '12345' }, + context_sha: project_sha }, { type: :remote, location: remote_location, blob: nil, raw: remote_location, extra: {}, context_project: project.full_path, - context_sha: '12345' }, + context_sha: project_sha }, { type: :file, location: '.gitlab-ci.yml', - blob: "http://localhost/#{main_project.full_path}/-/blob/#{main_project.commit.sha}/.gitlab-ci.yml", - raw: "http://localhost/#{main_project.full_path}/-/raw/#{main_project.commit.sha}/.gitlab-ci.yml", + blob: "http://localhost/#{main_project.full_path}/-/blob/#{main_project_sha}/.gitlab-ci.yml", + raw: "http://localhost/#{main_project.full_path}/-/raw/#{main_project_sha}/.gitlab-ci.yml", extra: { project: main_project.full_path, ref: 'HEAD' }, context_project: project.full_path, - context_sha: '12345' } + context_sha: project_sha } ) end end @@ -511,16 +517,13 @@ RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do describe 'external file version' do context 'when external local file SHA is defined' do it 'is using a defined value' do - expect(project.repository).to receive(:blob_data_at) - .with('eeff1122', local_location) - - described_class.new(gitlab_ci_yml, project: project, sha: 'eeff1122', user: user, pipeline: pipeline) + described_class.new(gitlab_ci_yml, project: project, sha: project_sha, user: user, pipeline: pipeline) end end context 'when external local file SHA is not defined' do it 'is using latest SHA on the default branch' do - expect(project.repository).to receive(:root_ref_sha) + expect(project.repository).to receive(:root_ref_sha).and_call_original described_class.new(gitlab_ci_yml, project: project, sha: nil, user: user, pipeline: pipeline) end @@ -757,13 +760,11 @@ RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do before do project.add_developer(user) + end - allow_next_instance_of(Repository) do |repository| - allow(repository).to receive(:blob_data_at).with(an_instance_of(String), local_location) - .and_return(local_file_content) - - allow(repository).to receive(:blob_data_at).with(an_instance_of(String), other_file_location) - .and_return(other_file_content) + around do |example| + create_and_delete_files(project, { other_file_location => other_file_content }) do + example.run end end @@ -819,14 +820,10 @@ RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do HEREDOC end - before do - project.repository.create_file( - project.creator, - 'my_builds.yml', - local_file_content, - message: 'Add my_builds.yml', - branch_name: '12345' - ) + around do |example| + create_and_delete_files(project, { 'my_builds.yml' => local_file_content }) do + example.run + end end context 'when the exists file does not exist' do @@ -853,7 +850,7 @@ RSpec.describe Gitlab::Ci::Config, feature_category: :pipeline_authoring do include: - local: #{local_location} rules: - - if: $CI_COMMIT_SHA == "#{project.commit.sha}" + - if: $CI_COMMIT_REF_NAME == "master" HEREDOC end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 80acf54bb78..b9f65ff749d 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -4,8 +4,9 @@ require 'spec_helper' module Gitlab module Ci - RSpec.describe YamlProcessor do + RSpec.describe YamlProcessor, feature_category: :pipeline_authoring do include StubRequests + include RepoHelpers subject(:processor) { described_class.new(config, user: nil).execute } @@ -1479,9 +1480,19 @@ module Gitlab let(:opts) { { project: project, sha: project.commit.sha } } context "when the included internal file is present" do - before do - expect(project.repository).to receive(:blob_data_at) - .and_return(YAML.dump({ job1: { script: 'hello' } })) + let(:project_files) do + { + 'local.gitlab-ci.yml' => <<~YAML + job1: + script: hello + YAML + } + end + + around do |example| + create_and_delete_files(project, project_files) do + example.run + end end it { is_expected.to be_valid } diff --git a/spec/lib/gitlab/pages/cache_control_spec.rb b/spec/lib/gitlab/pages/cache_control_spec.rb index d46124e0e16..dd15aa87441 100644 --- a/spec/lib/gitlab/pages/cache_control_spec.rb +++ b/spec/lib/gitlab/pages/cache_control_spec.rb @@ -3,20 +3,23 @@ require 'spec_helper' RSpec.describe Gitlab::Pages::CacheControl, feature_category: :pages do - describe '.for_namespace' do - subject(:cache_control) { described_class.for_namespace(1) } + RSpec.shared_examples 'cache_control' do |type| + it { expect(subject.cache_key).to match(/pages_domain_for_#{type}_1_*/) } - it { expect(subject.cache_key).to match(/pages_domain_for_namespace_1_*/) } + describe '#clear_cache', :use_clean_rails_redis_caching do + before do + Rails.cache.write("pages_domain_for_#{type}_1", ['settings-hash']) + Rails.cache.write("pages_domain_for_#{type}_1_settings-hash", 'payload') + end - describe '#clear_cache' do it 'clears the cache' do expect(Rails.cache) .to receive(:delete_multi) .with( array_including( [ - "pages_domain_for_namespace_1", - /pages_domain_for_namespace_1_*/ + "pages_domain_for_#{type}_1", + "pages_domain_for_#{type}_1_settings-hash" ] )) @@ -25,63 +28,53 @@ RSpec.describe Gitlab::Pages::CacheControl, feature_category: :pages do end end - describe '.for_project' do - subject(:cache_control) { described_class.for_project(1) } + describe '.for_namespace' do + subject(:cache_control) { described_class.for_namespace(1) } - it { expect(subject.cache_key).to match(/pages_domain_for_project_1_*/) } + it_behaves_like 'cache_control', 'namespace' + end - describe '#clear_cache' do - it 'clears the cache' do - expect(Rails.cache) - .to receive(:delete_multi) - .with( - array_including( - [ - "pages_domain_for_project_1", - /pages_domain_for_project_1_*/ - ] - )) + describe '.for_domain' do + subject(:cache_control) { described_class.for_domain(1) } - subject.clear_cache - end - end + it_behaves_like 'cache_control', 'domain' end describe '#cache_key' do it 'does not change the pages config' do - expect { described_class.new(type: :project, id: 1).cache_key } + expect { described_class.new(type: :domain, id: 1).cache_key } .not_to change(Gitlab.config, :pages) end it 'is based on pages settings' do access_control = Gitlab.config.pages.access_control - cache_key = described_class.new(type: :project, id: 1).cache_key + cache_key = described_class.new(type: :domain, id: 1).cache_key stub_config(pages: { access_control: !access_control }) - expect(described_class.new(type: :project, id: 1).cache_key).not_to eq(cache_key) + expect(described_class.new(type: :domain, id: 1).cache_key).not_to eq(cache_key) end it 'is based on the force_pages_access_control settings' do force_pages_access_control = ::Gitlab::CurrentSettings.force_pages_access_control - cache_key = described_class.new(type: :project, id: 1).cache_key + cache_key = described_class.new(type: :domain, id: 1).cache_key ::Gitlab::CurrentSettings.force_pages_access_control = !force_pages_access_control - expect(described_class.new(type: :project, id: 1).cache_key).not_to eq(cache_key) + expect(described_class.new(type: :domain, id: 1).cache_key).not_to eq(cache_key) end it 'caches the application settings hash' do expect(Rails.cache) .to receive(:write) - .with("pages_domain_for_project_1", kind_of(Set)) + .with('pages_domain_for_domain_1', kind_of(Set)) - described_class.new(type: :project, id: 1).cache_key + described_class.new(type: :domain, id: 1).cache_key end end it 'fails with invalid type' do expect { described_class.new(type: :unknown, id: nil) } - .to raise_error(ArgumentError, "type must be :namespace or :project") + .to raise_error(ArgumentError, 'type must be :namespace or :domain') end end diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb index f4f2b174a7b..b1c65c6b9ee 100644 --- a/spec/models/bulk_imports/entity_spec.rb +++ b/spec/models/bulk_imports/entity_spec.rb @@ -325,4 +325,24 @@ RSpec.describe BulkImports::Entity, type: :model do expect(project_entity.update_service).to eq(::Projects::UpdateService) end end + + describe '#full_path' do + it 'returns group full path for project entity' do + group_entity = build(:bulk_import_entity, :group_entity, group: build(:group)) + + expect(group_entity.full_path).to eq(group_entity.group.full_path) + end + + it 'returns project full path for project entity' do + project_entity = build(:bulk_import_entity, :project_entity, project: build(:project)) + + expect(project_entity.full_path).to eq(project_entity.project.full_path) + end + + it 'returns nil when not associated with group or project' do + entity = build(:bulk_import_entity, group: nil, project: nil) + + expect(entity.full_path).to eq(nil) + end + end end diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index e5f2e849a0a..f054fde78e7 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -567,7 +567,7 @@ RSpec.describe PagesDomain do it 'returns the virual domain when there are pages deployed for the project' do expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) expect(virtual_domain.lookup_paths).not_to be_empty - expect(virtual_domain.cache_key).to match(/pages_domain_for_project_#{project.id}_/) + expect(virtual_domain.cache_key).to match(/pages_domain_for_domain_#{pages_domain.id}_/) end context 'when :cache_pages_domain_api is disabled' do diff --git a/spec/models/work_item_spec.rb b/spec/models/work_item_spec.rb index 1c34936c5c2..0bedcc9791f 100644 --- a/spec/models/work_item_spec.rb +++ b/spec/models/work_item_spec.rb @@ -21,6 +21,13 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do .with_foreign_key('work_item_id') end + it 'has many `work_item_children_by_created_at`' do + is_expected.to have_many(:work_item_children_by_created_at) + .order(created_at: :asc) + .class_name('WorkItem') + .with_foreign_key('work_item_id') + end + it 'has many `child_links`' do is_expected.to have_many(:child_links) .class_name('::WorkItems::ParentLink') diff --git a/spec/models/work_items/widgets/hierarchy_spec.rb b/spec/models/work_items/widgets/hierarchy_spec.rb index c847f2694fe..43670b30645 100644 --- a/spec/models/work_items/widgets/hierarchy_spec.rb +++ b/spec/models/work_items/widgets/hierarchy_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' -RSpec.describe WorkItems::Widgets::Hierarchy do +RSpec.describe WorkItems::Widgets::Hierarchy, feature_category: :team_planning do let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, group: group) } let_it_be(:task) { create(:work_item, :task, project: project) } - let_it_be(:work_item_parent) { create(:work_item, project: project) } + let_it_be_with_reload(:work_item_parent) { create(:work_item, project: project) } describe '.type' do subject { described_class.type } @@ -21,7 +21,7 @@ RSpec.describe WorkItems::Widgets::Hierarchy do end describe '#parent' do - let_it_be(:parent_link) { create(:parent_link, work_item: task, work_item_parent: work_item_parent).reload } + let_it_be_with_reload(:parent_link) { create(:parent_link, work_item: task, work_item_parent: work_item_parent) } subject { described_class.new(parent_link.work_item).parent } @@ -29,11 +29,21 @@ RSpec.describe WorkItems::Widgets::Hierarchy do end describe '#children' do - let_it_be(:parent_link1) { create(:parent_link, work_item_parent: work_item_parent, work_item: task).reload } - let_it_be(:parent_link2) { create(:parent_link, work_item_parent: work_item_parent).reload } + let_it_be_with_reload(:parent_link1) { create(:parent_link, work_item_parent: work_item_parent, work_item: task) } + let_it_be_with_reload(:parent_link2) { create(:parent_link, work_item_parent: work_item_parent) } subject { described_class.new(work_item_parent).children } it { is_expected.to contain_exactly(parent_link1.work_item, parent_link2.work_item) } + + context 'with default order by created_at' do + let_it_be(:oldest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.ago) } + + let_it_be_with_reload(:link_to_oldest_child) do + create(:parent_link, work_item_parent: work_item_parent, work_item: oldest_child) + end + + it { is_expected.to eq([link_to_oldest_child, parent_link1, parent_link2].map(&:work_item)) } + end end end diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb index 8154f132430..5f43a0806f3 100644 --- a/spec/requests/api/graphql/ci/config_spec.rb +++ b/spec/requests/api/graphql/ci/config_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do include GraphqlHelpers include StubRequests + include RepoHelpers subject(:post_graphql_query) { post_graphql(query, current_user: user) } @@ -245,17 +246,22 @@ RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do ) end - before do - allow_next_instance_of(Repository) do |repository| - allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_file.yml') do - YAML.dump( - build: { - script: 'build' - } - ) - end + let(:project_files) do + { + 'other_file.yml' => <<~YAML + build: + script: build + YAML + } + end + + around do |example| + create_and_delete_files(project, project_files) do + example.run end + end + before do post_graphql_query end @@ -370,25 +376,33 @@ RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do ) end - before do - allow_next_instance_of(Repository) do |repository| - allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_file.yml') do - YAML.dump( - build: { - script: 'build' - } - ) - end + let(:project_files) do + { + 'other_file.yml' => <<~YAML + build: + script: build + YAML + } + end - allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_project_file.yml') do - YAML.dump( - other_project_test: { - script: 'other_project_test' - } - ) + let(:other_project_files) do + { + 'other_project_file.yml' => <<~YAML + other_project_test: + script: other_project_test + YAML + } + end + + around do |example| + create_and_delete_files(project, project_files) do + create_and_delete_files(other_project, other_project_files) do + example.run end end + end + before do stub_full_request('https://gitlab.com/gitlab-org/gitlab/raw/1234/.hello.yml').to_return(body: remote_file_content) post_graphql_query diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb index 14cb18d04b8..b33a394d023 100644 --- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb @@ -489,10 +489,10 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do expect(response).to have_gitlab_http_status(:success) expect(widgets_response).to include( { - 'children' => { 'edges' => [ + 'children' => { 'edges' => match_array([ { 'node' => { 'id' => valid_child2.to_global_id.to_s } }, { 'node' => { 'id' => valid_child1.to_global_id.to_s } } - ] }, + ]) }, 'parent' => nil, 'type' => 'HIERARCHY' } diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb index df7dbaea420..6b5d437df83 100644 --- a/spec/requests/api/graphql/work_item_spec.rb +++ b/spec/requests/api/graphql/work_item_spec.rb @@ -193,6 +193,24 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do ) end end + + context 'when ordered by default by created_at' do + let_it_be(:newest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.from_now) } + let_it_be(:oldest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.ago) } + let_it_be(:newest_link) { create(:parent_link, work_item_parent: work_item, work_item: newest_child) } + let_it_be(:oldest_link) { create(:parent_link, work_item_parent: work_item, work_item: oldest_child) } + + let(:hierarchy_widget) { work_item_data['widgets'].find { |widget| widget['type'] == 'HIERARCHY' } } + let(:hierarchy_children) { hierarchy_widget['children']['nodes'] } + + it 'places the oldest child item to the beginning of the children list' do + expect(hierarchy_children.first['id']).to eq(oldest_child.to_gid.to_s) + end + + it 'places the newest child item to the end of the children list' do + expect(hierarchy_children.last['id']).to eq(newest_child.to_gid.to_s) + end + end end describe 'assignees widget' do diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb index 65fcf9e006a..ba1fb5105b8 100644 --- a/spec/requests/api/pages_domains_spec.rb +++ b/spec/requests/api/pages_domains_spec.rb @@ -265,6 +265,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: kind_of(Numeric), domain: params[:domain] ) @@ -393,6 +394,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: pages_domain_secure.id, domain: pages_domain_secure.domain ) end @@ -556,6 +558,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: pages_domain.id, domain: pages_domain.domain ) diff --git a/spec/services/ci/create_pipeline_service/include_spec.rb b/spec/services/ci/create_pipeline_service/include_spec.rb index 3764663fd74..f18b4883aaf 100644 --- a/spec/services/ci/create_pipeline_service/include_spec.rb +++ b/spec/services/ci/create_pipeline_service/include_spec.rb @@ -2,7 +2,10 @@ require 'spec_helper' -RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do +RSpec.describe Ci::CreatePipelineService, +:yaml_processor_feature_flag_corectness, feature_category: :pipeline_authoring do + include RepoHelpers + context 'include:' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } @@ -16,14 +19,17 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes let(:file_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' } - before do - allow(project.repository) - .to receive(:blob_data_at).with(project.commit.id, '.gitlab-ci.yml') - .and_return(config) + let(:project_files) do + { + '.gitlab-ci.yml' => config, + file_location => File.read(Rails.root.join(file_location)) + } + end - allow(project.repository) - .to receive(:blob_data_at).with(project.commit.id, file_location) - .and_return(File.read(Rails.root.join(file_location))) + around do |example| + create_and_delete_files(project, project_files) do + example.run + end end shared_examples 'not including the file' do diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb index 2f2af9f6c85..c1669e0424a 100644 --- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb +++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category: :continuous_integration do + include RepoHelpers + describe 'Pipeline Processing Service Tests With Yaml' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } @@ -956,17 +958,16 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload end - before do - allow_next_instance_of(Repository) do |repository| - allow(repository) - .to receive(:blob_data_at) - .with(an_instance_of(String), '.gitlab-ci.yml') - .and_return(parent_config) - - allow(repository) - .to receive(:blob_data_at) - .with(an_instance_of(String), '.child.yml') - .and_return(child_config) + let(:project_files) do + { + '.gitlab-ci.yml' => parent_config, + '.child.yml' => child_config + } + end + + around do |example| + create_and_delete_files(project, project_files) do + example.run end end diff --git a/spec/services/pages_domains/create_service_spec.rb b/spec/services/pages_domains/create_service_spec.rb index cac941fb134..4dd9bd8f3bb 100644 --- a/spec/services/pages_domains/create_service_spec.rb +++ b/spec/services/pages_domains/create_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::PagesDomains::CreateService do +RSpec.describe ::PagesDomains::CreateService, feature_category: :pages do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :in_subgroup) } @@ -37,6 +37,7 @@ RSpec.describe ::PagesDomains::CreateService do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: kind_of(Numeric), domain: domain ) diff --git a/spec/services/pages_domains/delete_service_spec.rb b/spec/services/pages_domains/delete_service_spec.rb index 5f98fe3c7f7..43d59961637 100644 --- a/spec/services/pages_domains/delete_service_spec.rb +++ b/spec/services/pages_domains/delete_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::PagesDomains::DeleteService do +RSpec.describe ::PagesDomains::DeleteService, feature_category: :pages do let_it_be(:user) { create(:user) } let_it_be(:pages_domain) { create(:pages_domain, :with_project) } @@ -39,6 +39,7 @@ RSpec.describe ::PagesDomains::DeleteService do project_id: pages_domain.project.id, namespace_id: pages_domain.project.namespace.id, root_namespace_id: pages_domain.project.root_namespace.id, + domain_id: pages_domain.id, domain: pages_domain.domain ) end diff --git a/spec/services/pages_domains/retry_acme_order_service_spec.rb b/spec/services/pages_domains/retry_acme_order_service_spec.rb index 3152e05f2f1..4860d57475b 100644 --- a/spec/services/pages_domains/retry_acme_order_service_spec.rb +++ b/spec/services/pages_domains/retry_acme_order_service_spec.rb @@ -18,6 +18,7 @@ RSpec.describe PagesDomains::RetryAcmeOrderService, feature_category: :pages do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: domain.id, domain: domain.domain ) end @@ -31,6 +32,7 @@ RSpec.describe PagesDomains::RetryAcmeOrderService, feature_category: :pages do project_id: project.id, namespace_id: project.namespace.id, root_namespace_id: project.root_namespace.id, + domain_id: domain.id, domain: domain.domain ) end diff --git a/spec/services/pages_domains/update_service_spec.rb b/spec/services/pages_domains/update_service_spec.rb index f6558f56422..c317a2c68f6 100644 --- a/spec/services/pages_domains/update_service_spec.rb +++ b/spec/services/pages_domains/update_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe PagesDomains::UpdateService do +RSpec.describe PagesDomains::UpdateService, feature_category: :pages do let_it_be(:user) { create(:user) } let_it_be(:pages_domain) { create(:pages_domain, :with_project) } @@ -40,6 +40,7 @@ RSpec.describe PagesDomains::UpdateService do project_id: pages_domain.project.id, namespace_id: pages_domain.project.namespace.id, root_namespace_id: pages_domain.project.root_namespace.id, + domain_id: pages_domain.id, domain: pages_domain.domain ) end diff --git a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb index c786d4658d4..b3c81b25a93 100644 --- a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb +++ b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Pages::InvalidateDomainCacheWorker do +RSpec.describe Pages::InvalidateDomainCacheWorker, feature_category: :pages do shared_examples 'clears caches with' do |event_class:, event_data:, caches:| include AfterNextHelpers @@ -22,44 +22,70 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do end end + context 'when a project have multiple domains' do + include AfterNextHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:pages_domain) { create(:pages_domain, project: project) } + let_it_be(:pages_domain2) { create(:pages_domain, project: project) } + + let(:event) do + Pages::PageDeployedEvent.new( + data: { + project_id: project.id, + namespace_id: project.namespace_id, + root_namespace_id: project.root_ancestor.id + } + ) + end + + subject { consume_event(subscriber: described_class, event: event) } + + it 'clears the cache with Gitlab::Pages::CacheControl' do + expect_next(Gitlab::Pages::CacheControl, type: :namespace, id: project.namespace_id) + .to receive(:clear_cache) + expect_next(Gitlab::Pages::CacheControl, type: :domain, id: pages_domain.id) + .to receive(:clear_cache) + expect_next(Gitlab::Pages::CacheControl, type: :domain, id: pages_domain2.id) + .to receive(:clear_cache) + + subject + end + end + it_behaves_like 'clears caches with', event_class: Pages::PageDeployedEvent, event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, caches: [ - { type: :namespace, id: 3 }, - { type: :project, id: 1 } + { type: :namespace, id: 3 } ] it_behaves_like 'clears caches with', event_class: Pages::PageDeletedEvent, event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, caches: [ - { type: :namespace, id: 3 }, - { type: :project, id: 1 } + { type: :namespace, id: 3 } ] it_behaves_like 'clears caches with', event_class: Projects::ProjectDeletedEvent, event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, caches: [ - { type: :namespace, id: 3 }, - { type: :project, id: 1 } + { type: :namespace, id: 3 } ] it_behaves_like 'clears caches with', event_class: Projects::ProjectCreatedEvent, event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, caches: [ - { type: :namespace, id: 3 }, - { type: :project, id: 1 } + { type: :namespace, id: 3 } ] it_behaves_like 'clears caches with', event_class: Projects::ProjectArchivedEvent, event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, caches: [ - { type: :namespace, id: 3 }, - { type: :project, id: 1 } + { type: :namespace, id: 3 } ] it_behaves_like 'clears caches with', @@ -72,8 +98,7 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do new_path: 'new_path' }, caches: [ - { type: :namespace, id: 3 }, - { type: :project, id: 1 } + { type: :namespace, id: 3 } ] it_behaves_like 'clears caches with', @@ -86,7 +111,6 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do new_root_namespace_id: 5 }, caches: [ - { type: :project, id: 1 }, { type: :namespace, id: 3 }, { type: :namespace, id: 5 } ] @@ -131,10 +155,11 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do project_id: 1, namespace_id: 2, root_namespace_id: 3, + domain_id: 4, domain: 'somedomain.com' }, caches: [ - { type: :project, id: 1 }, + { type: :domain, id: 4 }, { type: :namespace, id: 3 } ] @@ -144,10 +169,11 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do project_id: 1, namespace_id: 2, root_namespace_id: 3, + domain_id: 4, domain: 'somedomain.com' }, caches: [ - { type: :project, id: 1 }, + { type: :domain, id: 4 }, { type: :namespace, id: 3 } ] @@ -157,10 +183,11 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do project_id: 1, namespace_id: 2, root_namespace_id: 3, + domain_id: 4, domain: 'somedomain.com' }, caches: [ - { type: :project, id: 1 }, + { type: :domain, id: 4 }, { type: :namespace, id: 3 } ] @@ -172,10 +199,11 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do project_id: 1, namespace_id: 2, root_namespace_id: 3, + domain_id: 4, attributes: [attribute] }, caches: [ - { type: :project, id: 1 }, + { type: :domain, id: 4 }, { type: :namespace, id: 3 } ] end @@ -204,7 +232,6 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do features: ['pages_access_level'] }, caches: [ - { type: :project, id: 1 }, { type: :namespace, id: 3 } ] @@ -234,7 +261,6 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do new_root_namespace_id: 5 }, caches: [ - { type: :project, id: 1 }, { type: :namespace, id: 5 } ] end |
