summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-13 18:10:04 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-13 18:10:04 +0000
commit04befb368f4b170ce19bb2c7c8739baa08b04a0a (patch)
treec3e7466f95c9757fe2a21ebc1ed6e91ea80d5369
parentaadb3204eaf8b5912e262cd19fed34fc70789e95 (diff)
downloadgitlab-ce-04befb368f4b170ce19bb2c7c8739baa08b04a0a.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_assigned_item.vue2
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_delete_button.vue2
-rw-r--r--app/assets/javascripts/design_management/components/image.vue22
-rw-r--r--app/assets/javascripts/design_management/components/list/item.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/report_widget_container.vue32
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue6
-rw-r--r--app/models/ci/runner_machine.rb2
-rw-r--r--app/models/concerns/enums/package_metadata.rb20
-rw-r--r--app/services/ci/archive_trace_service.rb30
-rw-r--r--app/views/admin/runners/new.html.haml2
-rw-r--r--app/workers/ci/archive_traces_cron_worker.rb10
-rw-r--r--config/feature_flags/development/deduplicate_archive_traces_cron_worker.yml8
-rw-r--r--config/initializers/countries.rb9
-rw-r--r--db/docs/pm_checkpoints.yml10
-rw-r--r--db/migrate/20230119215436_add_package_metadata_checkpoints.rb19
-rw-r--r--db/schema_migrations/202301192154361
-rw-r--r--db/structure.sql11
-rw-r--r--doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md1
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md15
-rw-r--r--doc/ci/environments/deployment_approvals.md14
-rw-r--r--doc/development/documentation/styleguide/word_list.md9
-rw-r--r--doc/user/clusters/agent/gitops.md2
-rw-r--r--doc/user/group/saml_sso/index.md51
-rw-r--r--doc/user/project/merge_requests/reviews/img/suggested_reviewers_v15_9.pngbin7957 -> 9184 bytes
-rw-r--r--doc/user/project/repository/index.md17
-rw-r--r--doc/user/project/working_with_projects.md2
-rw-r--r--lib/gitlab/ci/status/waiting_for_resource.rb2
-rw-r--r--lib/tasks/gitlab/tw/codeowners.rake16
-rw-r--r--locale/gitlab.pot25
-rw-r--r--spec/features/projects/issues/design_management/user_views_design_spec.rb22
-rw-r--r--spec/fixtures/svg_without_attr.svg17
-rw-r--r--spec/frontend/design_management/components/__snapshots__/image_spec.js.snap14
-rw-r--r--spec/frontend/design_management/components/image_spec.js10
-rw-r--r--spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap12
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js9
-rw-r--r--spec/initializers/countries_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/status/bridge/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb2
-rw-r--r--spec/models/release_highlight_spec.rb3
-rw-r--r--spec/services/ci/archive_trace_service_spec.rb67
-rw-r--r--spec/workers/ci/archive_traces_cron_worker_spec.rb20
41 files changed, 439 insertions, 98 deletions
diff --git a/app/assets/javascripts/ci/runner/components/runner_assigned_item.vue b/app/assets/javascripts/ci/runner/components/runner_assigned_item.vue
index 2fa87bdd776..5e61e4d7377 100644
--- a/app/assets/javascripts/ci/runner/components/runner_assigned_item.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_assigned_item.vue
@@ -55,7 +55,7 @@ export default {
<div>
<div class="gl-mb-1">
<gl-link :href="href" class="gl-font-weight-bold gl-text-gray-900!">{{ fullName }}</gl-link>
- <gl-badge v-if="isOwner" variant="info">{{ s__('Runner|Owner') }}</gl-badge>
+ <gl-badge v-if="isOwner" variant="info">{{ s__('Runners|Owner') }}</gl-badge>
</div>
<div v-if="description">{{ description }}</div>
</div>
diff --git a/app/assets/javascripts/ci/runner/components/runner_delete_button.vue b/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
index 32d4076b00f..f02e6bce5c3 100644
--- a/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_delete_button.vue
@@ -122,7 +122,7 @@ export default {
onError(error) {
this.deleting = false;
const { message } = error;
- const title = sprintf(s__('Runner|Runner %{runnerName} failed to delete'), {
+ const title = sprintf(s__('Runners|Runner %{runnerName} failed to delete'), {
runnerName: this.runnerName,
});
diff --git a/app/assets/javascripts/design_management/components/image.vue b/app/assets/javascripts/design_management/components/image.vue
index 5354c7756f5..fd691d1f04e 100644
--- a/app/assets/javascripts/design_management/components/image.vue
+++ b/app/assets/javascripts/design_management/components/image.vue
@@ -72,12 +72,19 @@ export default {
},
setBaseImageSize() {
const { contentImg } = this.$refs;
- if (!contentImg || contentImg.offsetHeight === 0 || contentImg.offsetWidth === 0) return;
+ if (!contentImg) return;
+ if (contentImg.offsetHeight === 0 || contentImg.offsetWidth === 0) {
+ this.baseImageSize = {
+ height: contentImg.naturalHeight,
+ width: contentImg.naturalWidth,
+ };
+ } else {
+ this.baseImageSize = {
+ height: contentImg.offsetHeight,
+ width: contentImg.offsetWidth,
+ };
+ }
- this.baseImageSize = {
- height: contentImg.offsetHeight,
- width: contentImg.offsetWidth,
- };
this.onResize({ width: this.baseImageSize.width, height: this.baseImageSize.height });
},
setImageNaturalScale() {
@@ -96,6 +103,11 @@ export default {
const { height, width } = this.baseImageSize;
+ this.imageStyle = {
+ width: `${width}px`,
+ height: `${height}px`,
+ };
+
this.$parent.$emit(
'setMaxScale',
Math.round(((height + width) / (naturalHeight + naturalWidth)) * 100) / 100,
diff --git a/app/assets/javascripts/design_management/components/list/item.vue b/app/assets/javascripts/design_management/components/list/item.vue
index 1e36aa686a4..d6405549452 100644
--- a/app/assets/javascripts/design_management/components/list/item.vue
+++ b/app/assets/javascripts/design_management/components/list/item.vue
@@ -144,7 +144,7 @@ export default {
/>
</span>
</div>
- <gl-intersection-observer @appear="onAppear">
+ <gl-intersection-observer class="gl-flex-grow-1" @appear="onAppear">
<gl-loading-icon v-if="showLoadingSpinner" size="lg" />
<gl-icon
v-else-if="showImageErrorIcon"
@@ -156,7 +156,7 @@ export default {
v-show="showImage"
:src="imageLink"
:alt="filename"
- class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full design-img"
+ class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full gl-w-auto design-img"
data-qa-selector="design_image"
:data-qa-filename="filename"
:data-testid="`design-img-${id}`"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/report_widget_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/report_widget_container.vue
index 0f20fb6d176..34a1d1facda 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/report_widget_container.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/report_widget_container.vue
@@ -5,16 +5,32 @@ export default {
hasChildren: false,
};
},
- updated() {
- this.hasChildren = this.checkSlots();
- },
mounted() {
- this.hasChildren = this.checkSlots();
+ const setHasChildren = () => {
+ this.hasChildren = Boolean(this.$el.innerText.trim());
+ };
+
+ // Set initial.
+ setHasChildren();
+
+ if (!this.hasChildren) {
+ // Observe children changed.
+ this.observer = new MutationObserver(() => {
+ setHasChildren();
+
+ if (this.hasChildren) {
+ this.observer.disconnect();
+ this.observer = undefined;
+ }
+ });
+
+ this.observer.observe(this.$el, { childList: true, subtree: true });
+ }
},
- methods: {
- checkSlots() {
- return this.$scopedSlots.default?.()?.some((c) => c.elm?.innerText);
- },
+ beforeUnmount() {
+ if (this.observer) {
+ this.observer.disconnect();
+ }
},
};
</script>
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index 9a347ebd874..262c093a1d0 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -293,7 +293,10 @@ export default {
return this.isWidgetPresent(WIDGET_TYPE_NOTES);
},
fetchByIid() {
- return this.glFeatures.useIidInWorkItemsPath && parseBoolean(getParameterByName('iid_path'));
+ return (
+ (this.glFeatures.useIidInWorkItemsPath && parseBoolean(getParameterByName('iid_path'))) ||
+ false
+ );
},
queryVariables() {
return this.fetchByIid
@@ -572,7 +575,6 @@ export default {
@error="updateError = $event"
/>
<work-item-created-updated
- v-if="workItemsMvcEnabled"
:work-item-id="workItem.id"
:work-item-iid="workItemIid"
:full-path="fullPath"
diff --git a/app/models/ci/runner_machine.rb b/app/models/ci/runner_machine.rb
index 7048a9c027a..2564816dc14 100644
--- a/app/models/ci/runner_machine.rb
+++ b/app/models/ci/runner_machine.rb
@@ -6,7 +6,7 @@ module Ci
include Ci::HasRunnerExecutor
include IgnorableColumns
- ignore_column :machine_xid, remove_with: '15.10', remove_after: '2022-03-22'
+ ignore_column :machine_xid, remove_with: '15.11', remove_after: '2022-03-22'
belongs_to :runner
diff --git a/app/models/concerns/enums/package_metadata.rb b/app/models/concerns/enums/package_metadata.rb
new file mode 100644
index 00000000000..e15fe758e69
--- /dev/null
+++ b/app/models/concerns/enums/package_metadata.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Enums
+ class PackageMetadata
+ PURL_TYPES = {
+ composer: 1,
+ conan: 2,
+ gem: 3,
+ golang: 4,
+ maven: 5,
+ npm: 6,
+ nuget: 7,
+ pypi: 8
+ }.with_indifferent_access.freeze
+
+ def self.purl_types
+ PURL_TYPES
+ end
+ end
+end
diff --git a/app/services/ci/archive_trace_service.rb b/app/services/ci/archive_trace_service.rb
index 3d548c824c8..4b62580e670 100644
--- a/app/services/ci/archive_trace_service.rb
+++ b/app/services/ci/archive_trace_service.rb
@@ -2,6 +2,36 @@
module Ci
class ArchiveTraceService
+ include ::Gitlab::ExclusiveLeaseHelpers
+
+ EXCLUSIVE_LOCK_KEY = 'archive_trace_service:batch_execute:lock'
+ LOCK_TIMEOUT = 56.minutes
+ LOOP_TIMEOUT = 55.minutes
+ LOOP_LIMIT = 2000
+ BATCH_SIZE = 100
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def batch_execute(worker_name:)
+ start_time = Time.current
+ in_lock(EXCLUSIVE_LOCK_KEY, ttl: LOCK_TIMEOUT, retries: 1) do
+ Ci::Build.with_stale_live_trace.find_each(batch_size: BATCH_SIZE).with_index do |build, index|
+ break if Time.current - start_time > LOOP_TIMEOUT
+
+ if index > LOOP_LIMIT
+ Sidekiq.logger.warn(class: worker_name, message: 'Loop limit reached.', job_id: build.id)
+ break
+ end
+
+ begin
+ execute(build, worker_name: worker_name)
+ rescue StandardError
+ next
+ end
+ end
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def execute(job, worker_name:)
unless job.trace.archival_attempts_available?
Sidekiq.logger.warn(class: worker_name, message: 'The job is out of archival attempts.', job_id: job.id)
diff --git a/app/views/admin/runners/new.html.haml b/app/views/admin/runners/new.html.haml
index cf5638d0294..afe0d57e0a3 100644
--- a/app/views/admin/runners/new.html.haml
+++ b/app/views/admin/runners/new.html.haml
@@ -1,5 +1,5 @@
- add_to_breadcrumbs _('Runners'), admin_runners_path
-- breadcrumb_title s_('Runner|New')
+- breadcrumb_title s_('Runners|New')
- page_title s_('Runners|Create an instance runner')
#js-admin-new-runner{ data: {
diff --git a/app/workers/ci/archive_traces_cron_worker.rb b/app/workers/ci/archive_traces_cron_worker.rb
index 12856805243..fe23d10c2ac 100644
--- a/app/workers/ci/archive_traces_cron_worker.rb
+++ b/app/workers/ci/archive_traces_cron_worker.rb
@@ -9,14 +9,20 @@ module Ci
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
feature_category :continuous_integration
+ deduplicate :until_executed, including_scheduled: true
# rubocop: disable CodeReuse/ActiveRecord
def perform
# Archive stale live traces which still resides in redis or database
# This could happen when Ci::ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL
# More details in https://gitlab.com/gitlab-org/gitlab-foss/issues/36791
- Ci::Build.with_stale_live_trace.find_each(batch_size: 100) do |build|
- Ci::ArchiveTraceService.new.execute(build, worker_name: self.class.name)
+
+ if Feature.enabled?(:deduplicate_archive_traces_cron_worker)
+ Ci::ArchiveTraceService.new.batch_execute(worker_name: self.class.name)
+ else
+ Ci::Build.with_stale_live_trace.find_each(batch_size: 100) do |build|
+ Ci::ArchiveTraceService.new.execute(build, worker_name: self.class.name)
+ end
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/config/feature_flags/development/deduplicate_archive_traces_cron_worker.yml b/config/feature_flags/development/deduplicate_archive_traces_cron_worker.yml
new file mode 100644
index 00000000000..c26968381ae
--- /dev/null
+++ b/config/feature_flags/development/deduplicate_archive_traces_cron_worker.yml
@@ -0,0 +1,8 @@
+---
+name: deduplicate_archive_traces_cron_worker
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110305
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/389632
+milestone: '15.9'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/initializers/countries.rb b/config/initializers/countries.rb
index 171c126143c..e22637f459c 100644
--- a/config/initializers/countries.rb
+++ b/config/initializers/countries.rb
@@ -60,3 +60,12 @@ ISO3166::Data.register(
currency_code: "UAH",
start_of_week: "monday"
)
+
+# Updating the display name of Taiwan, from `Taiwan, Province of China` to `Taiwan`
+# See issue: https://gitlab.com/gitlab-org/gitlab/-/issues/349333
+ISO3166::Data.register(
+ ISO3166::Data.new('TW')
+ .call
+ .deep_symbolize_keys
+ .merge({ name: 'Taiwan' })
+)
diff --git a/db/docs/pm_checkpoints.yml b/db/docs/pm_checkpoints.yml
new file mode 100644
index 00000000000..e360e8ad356
--- /dev/null
+++ b/db/docs/pm_checkpoints.yml
@@ -0,0 +1,10 @@
+---
+table_name: pm_checkpoints
+classes:
+- PackageMetadata::Checkpoint
+feature_categories:
+- license_compliance
+description: Tracks position of last synced file.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109713
+milestone: '15.9'
+gitlab_schema: gitlab_pm
diff --git a/db/migrate/20230119215436_add_package_metadata_checkpoints.rb b/db/migrate/20230119215436_add_package_metadata_checkpoints.rb
new file mode 100644
index 00000000000..a8349a107b1
--- /dev/null
+++ b/db/migrate/20230119215436_add_package_metadata_checkpoints.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddPackageMetadataCheckpoints < Gitlab::Database::Migration[2.1]
+ def up
+ create_table :pm_checkpoints, id: false do |t|
+ t.integer :sequence, null: false
+ t.timestamps_with_timezone
+ t.integer :purl_type, null: false, primary_key: true
+ t.integer :chunk, null: false, limit: 2
+ end
+
+ change_column(:pm_checkpoints, :purl_type, :integer, limit: 2)
+ drop_sequence(:pm_checkpoints, :purl_type, 'pm_checkpoints_purl_type_seq')
+ end
+
+ def down
+ drop_table :pm_checkpoints
+ end
+end
diff --git a/db/schema_migrations/20230119215436 b/db/schema_migrations/20230119215436
new file mode 100644
index 00000000000..97303b6e759
--- /dev/null
+++ b/db/schema_migrations/20230119215436
@@ -0,0 +1 @@
+e5498ebd6ea0c18271078236a4f64b447fa5c55318b92c04f12a66834a38f67d \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 0f351f2db5e..467cca32e58 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -19816,6 +19816,14 @@ CREATE SEQUENCE plans_id_seq
ALTER SEQUENCE plans_id_seq OWNED BY plans.id;
+CREATE TABLE pm_checkpoints (
+ sequence integer NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ purl_type smallint NOT NULL,
+ chunk smallint NOT NULL
+);
+
CREATE TABLE pm_licenses (
id bigint NOT NULL,
spdx_identifier text NOT NULL,
@@ -27045,6 +27053,9 @@ ALTER TABLE ONLY plan_limits
ALTER TABLE ONLY plans
ADD CONSTRAINT plans_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY pm_checkpoints
+ ADD CONSTRAINT pm_checkpoints_pkey PRIMARY KEY (purl_type);
+
ALTER TABLE ONLY pm_licenses
ADD CONSTRAINT pm_licenses_pkey PRIMARY KEY (id);
diff --git a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
index ecbafc1f32e..ebe3c72adfc 100644
--- a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
+++ b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
@@ -777,6 +777,7 @@ gantt
Introduce auto-partitioning mechanisms :5_1, 2023-09-01, 120d
New partitions are being created automatically :milestone, part3, 2023-12-01, 1min
Partitioning is made available on self-managed :milestone, part4, 2024-01-01, 1min
+```
## Conclusions
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index 9709d9ff058..4fda47a99a0 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -352,6 +352,13 @@ scope.
| 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 2a - Prepare GitLab Runner Helm Chart and GitLab Runner Operator
+
+| Component | Milestone | Issue | Changes |
+|------------------|----------:|-------|---------|
+|GitLab Runner Helm Chart| `%15.10` | Update the Runner Helm Chart to support registration with the authentication token. |
+|GitLab Runner Operator| `%15.10` | Update the Runner Operator to support registration with the authentication token. |
+
### Stage 3 - Database changes
| Component | Milestone | Changes |
@@ -368,14 +375,18 @@ scope.
| GitLab Rails app | `%15.9` | 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.9` | [Feature flag] Enable runner creation workflow (`create_runner_workflow`). |
| GitLab Rails app | `%15.9` | Implement `create_{instance|group|project}_runner` permissions. |
-| GitLab Rails app | `%15.10` | Rename `ci_runner_machines.machine_xid` column to `system_xid` to be consistent with `system_id` passed in APIs. |
+| GitLab Rails app | `%15.9` | Rename `ci_runner_machines.machine_xid` column to `system_xid` to be consistent with `system_id` passed in APIs. |
+| GitLab Rails app | `%15.10` | Drop `ci_runner_machines.machine_xid` column. |
+| GitLab Rails app | `%15.11` | Remove the ignore rule for `ci_runner_machines.machine_xid` column. |
-### Stage 4 - New UI
+### Stage 4 - Create runners from the UI
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Rails app | `%15.9` | Implement new GraphQL user-authenticated API to create a new runner. |
| GitLab Rails app | `%15.9` | [Add prefix to newly generated runner authentication tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/383198). |
+| GitLab Rails app | `%15.10` | Return token and runner ID information from `/runners/verify` REST endpoint. |
+| GitLab Runner | `%15.10` | [Modify register command to allow new flow with glrt- prefixed authentication tokens](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/29613). |
| 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.) (?) |
diff --git a/doc/ci/environments/deployment_approvals.md b/doc/ci/environments/deployment_approvals.md
index 50c49079ce7..089ecefb373 100644
--- a/doc/ci/environments/deployment_approvals.md
+++ b/doc/ci/environments/deployment_approvals.md
@@ -113,22 +113,20 @@ NOTE:
To protect, update, or unprotect an environment, you must have at least the
Maintainer role.
-### Optional settings
-
-#### Allow self-approval
+### Allow self-approval **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/381418) in GitLab 15.8.
-By default, a user who triggered a deployment pipeline can't self-approve the deployment jobs.
-You can allow the self-approval by the following settings:
+By default, the user who triggers a deployment pipeline can't also approve the deployment job.
+To allow self-approval of a deployment job:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Protected environments**.
-1. From the **Approval options**, check **Allow pipeline triggerer to approve deployment**.
+1. From the **Approval options**, select the **Allow pipeline triggerer to approve deployment** checkbox.
-By enabling this, when a pipeline runs, deployment jobs will automatically be approved in the pipeline
-if the triggerer is allowed to approve, otherwise nothing happens.
+When a pipeline runs, deployment jobs are automatically approved in the pipeline if the user who
+triggered the deployment is allowed to approve.
## Approve or reject a deployment
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index 7e77e19c4a2..4f8a9334787 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -152,6 +152,15 @@ Use uppercase for **Alpha**. For example: **The XYZ feature is in Alpha.** or **
You might also want to link to [this section](../../../policy/alpha-beta-support.md#alpha-features)
in the handbook when writing about Alpha features.
+## analytics
+
+Use lowercase for **analytics** and its variations, like **contribution analytics** and **issue analytics**.
+However, if the UI has different capitalization, make the documentation match the UI.
+
+For example:
+
+- You can view merge request analytics for a project. They are displayed on the Merge Request Analytics dashboard.
+
## and/or
Instead of **and/or**, use **or** or rewrite the sentence to spell out both options.
diff --git a/doc/user/clusters/agent/gitops.md b/doc/user/clusters/agent/gitops.md
index bd7dfb3abee..ba5029229c0 100644
--- a/doc/user/clusters/agent/gitops.md
+++ b/doc/user/clusters/agent/gitops.md
@@ -183,4 +183,4 @@ update your agent configuration file with the location of these projects.
WARNING:
The project with the agent's
configuration file can be private or public. Other projects with Kubernetes manifests must be public. Support for private manifest projects is tracked
-in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/283885).
+in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7704).
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 810d680ec59..25723687ebd 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -97,7 +97,7 @@ After you set up your identity provider to work with GitLab, you must configure
![Group SAML Settings for GitLab.com](img/group_saml_settings_v13_12.png)
NOTE:
-The certificate [fingerprint algorithm](../../../integration/saml.md#configure-saml-on-your-idp) must be in SHA1. When configuring the identity provider (such as [Google Workspace](#google-workspace-setup-notes)), use a secure signature algorithm.
+The certificate [fingerprint algorithm](../../../integration/saml.md#configure-saml-on-your-idp) must be in SHA1. When configuring the identity provider (such as [Google Workspace](#set-up-google-workspace)), use a secure signature algorithm.
### Additional configuration information
@@ -240,37 +240,38 @@ If using [Group Sync](#group-sync), customize the name of the group claim to mat
See our [example configuration page](example_saml_config.md#azure-active-directory).
-### Google Workspace setup notes
+### Set up Google Workspace
-Follow the Google Workspace documentation on
-[setting up SSO with Google as your identity provider](https://support.google.com/a/answer/6087519?hl=en)
-with the notes below for consideration.
+1. [Set up SSO with Google as your identity provider](https://support.google.com/a/answer/6087519?hl=en).
+ The following GitLab settings correspond to the Google Workspace fields.
-| GitLab setting | Google Workspace field |
-|:-------------------------------------|:-----------------------|
-| Identifier | Entity ID |
-| Assertion consumer service URL | ACS URL |
-| GitLab single sign-on URL | Start URL |
-| Identity provider single sign-on URL | SSO URL |
+ | GitLab setting | Google Workspace field |
+ |:-------------------------------------|:-----------------------|
+ | Identifier | **Entity ID** |
+ | Assertion consumer service URL | **ACS URL** |
+ | GitLab single sign-on URL | **Start URL** |
+ | Identity provider single sign-on URL | **SSO URL** |
-NOTE:
-Google Workspace displays a SHA256 fingerprint. To retrieve the SHA1 fingerprint required by GitLab for [configuring SAML](#configure-gitlab), download the certificate and calculate
-the SHA1 certificate fingerprint using this sample command: `openssl x509 -noout -fingerprint -sha1 -inform pem -in "GoogleIDPCertificate-domain.com.pem"`.
-
-The recommended attributes and claims settings are:
-
-- **Primary email** set to `email`.
-- **First name** set to `first_name`.
-- **Last name** set to `last_name`.
+1. Google Workspace displays a SHA256 fingerprint. To retrieve the SHA1 fingerprint
+ required by GitLab to [configure SAML](#configure-gitlab):
+ 1. Download the certificate.
+ 1. Run this command:
-For NameID, the following settings are recommended:
+ ```shell
+ openssl x509 -noout -fingerprint -sha1 -inform pem -in "GoogleIDPCertificate-domain.com.pem"
+ ```
-- **Name ID format** is set to `EMAIL`.
-- **NameID** set to `Basic Information > Primary email`.
+1. Set these values:
+ - For **Primary email**: `email`.
+ - For **First name**: `first_name`.
+ - For **Last name**: `last_name`.
+ - For **Name ID format**: `EMAIL`.
+ - For **NameID**: `Basic Information > Primary email`.
-When selecting **Verify SAML Configuration** on the GitLab SAML SSO page, disregard the warning recommending setting the NameID format to "persistent".
+On the GitLab SAML SSO page, when you select **Verify SAML Configuration**, disregard
+the warning that recommends setting the **NameID** format to `persistent`.
-See our [example configuration page](example_saml_config.md#google-workspace).
+For details, see the [example configuration page](example_saml_config.md#google-workspace).
### Okta setup notes
diff --git a/doc/user/project/merge_requests/reviews/img/suggested_reviewers_v15_9.png b/doc/user/project/merge_requests/reviews/img/suggested_reviewers_v15_9.png
index 80083e1819e..70db0d79eaf 100644
--- a/doc/user/project/merge_requests/reviews/img/suggested_reviewers_v15_9.png
+++ b/doc/user/project/merge_requests/reviews/img/suggested_reviewers_v15_9.png
Binary files differ
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index e35d644e569..5b4e1b14489 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -86,13 +86,28 @@ Visual Studio Code:
- From the GitLab interface:
1. Go to the project's overview page.
1. Select **Clone**.
- 1. Under either the **HTTPS** or **SSH** method, select **Clone with Visual Studio Code**.
+ 1. Under **Open in your IDE**, select **Visual Studio Code (SSH)** or **Visual Studio Code (HTTPS)**.
1. Select a folder to clone the project into.
After Visual Studio Code clones your project, it opens the folder.
- From Visual Studio Code, with the [extension](vscode.md) installed, use the
extension's [`Git: Clone` command](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#clone-gitlab-projects).
+### Clone and open in IntelliJ IDEA
+
+All projects can be cloned into [IntelliJ IDEA](https://www.jetbrains.com/idea/)
+from the GitLab user interface.
+
+Prerequisites:
+
+- The [Jetbrains Toolbox App](https://www.jetbrains.com/toolbox-app/) must be also be installed.
+
+To do this:
+
+1. Go to the project's overview page.
+1. Select **Clone**.
+1. Under **Open in your IDE**, select **IntelliJ IDEA (SSH)** or **IntelliJ IDEA (HTTPS)**.
+
## Download the code in a repository
> Support for [including Git LFS blobs](../../../topics/git/lfs#lfs-objects-in-project-archives) was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15079) in GitLab 13.5.
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index 5f3ceb51e10..a0e6a78dfe2 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -87,7 +87,7 @@ called `my-project` under your username, the project is created at `https://gitl
To view your personal projects:
1. On the top bar, select **Main menu > Projects > View all projects**.
-1. In the **Your projects** tab, select **Personal**.
+1. In the **Yours** tab, select **Personal**.
## Delete a project
diff --git a/lib/gitlab/ci/status/waiting_for_resource.rb b/lib/gitlab/ci/status/waiting_for_resource.rb
index 2026148f752..9ced0aadb88 100644
--- a/lib/gitlab/ci/status/waiting_for_resource.rb
+++ b/lib/gitlab/ci/status/waiting_for_resource.rb
@@ -17,7 +17,7 @@ module Gitlab
end
def favicon
- 'favicon_pending'
+ 'favicon_status_pending'
end
def group
diff --git a/lib/tasks/gitlab/tw/codeowners.rake b/lib/tasks/gitlab/tw/codeowners.rake
index fc5aab861ca..ef47e73df5f 100644
--- a/lib/tasks/gitlab/tw/codeowners.rake
+++ b/lib/tasks/gitlab/tw/codeowners.rake
@@ -14,7 +14,7 @@ namespace :tw do
end
def directory
- @directory ||= File.dirname(path)
+ @directory ||= "#{File.dirname(path)}/"
end
end
@@ -123,16 +123,20 @@ namespace :tw do
mappings << DocumentOwnerMapping.new(relative_file, writer) if document.has_a_valid_group?
end
- deduplicated_mappings = Set.new
-
- mappings.each do |mapping|
+ transformed_mappings = mappings.map do |mapping|
if mapping.writer_owns_directory?(mappings)
- deduplicated_mappings.add("#{mapping.directory}/ #{mapping.writer}")
+ DocumentOwnerMapping.new(mapping.directory, mapping.writer)
else
- deduplicated_mappings.add("#{mapping.path} #{mapping.writer}")
+ DocumentOwnerMapping.new(mapping.path, mapping.writer)
end
end
+ deduplicated_mappings = Set.new
+
+ transformed_mappings
+ .reject { |mapping| transformed_mappings.any? { |m| m.path == mapping.directory && m.writer == mapping.writer } }
+ .each { |mapping| deduplicated_mappings.add("#{mapping.path} #{mapping.writer}") }
+
new_docs_owners = deduplicated_mappings.sort.join("\n")
codeowners_path = Rails.root.join('.gitlab/CODEOWNERS')
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3e2a6a6376b..47d3a9f12c3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8735,6 +8735,9 @@ msgstr ""
msgid "Choose your framework"
msgstr ""
+msgid "Ci config already present"
+msgstr ""
+
msgid "CiCdAnalytics|Date range: %{range}"
msgstr ""
@@ -11600,7 +11603,7 @@ msgstr ""
msgid "Couldn't find event type filters where audit event type(s): %{missing_filters}"
msgstr ""
-msgid "Country"
+msgid "Country / Region"
msgstr ""
msgid "Counts"
@@ -31826,7 +31829,7 @@ msgstr ""
msgid "Please select a Jira project"
msgstr ""
-msgid "Please select a country"
+msgid "Please select a country / region"
msgstr ""
msgid "Please select a group"
@@ -33083,6 +33086,9 @@ msgstr ""
msgid "Project milestone"
msgstr ""
+msgid "Project must have default branch"
+msgstr ""
+
msgid "Project name"
msgstr ""
@@ -36891,6 +36897,9 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
+msgid "Runners|New"
+msgstr ""
+
msgid "Runners|New group runners can be registered"
msgstr ""
@@ -37004,6 +37013,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner %{runnerName} failed to delete"
+msgstr ""
+
msgid "Runners|Runner Registration"
msgstr ""
@@ -37255,15 +37267,6 @@ msgstr ""
msgid "Runners|shared"
msgstr ""
-msgid "Runner|New"
-msgstr ""
-
-msgid "Runner|Owner"
-msgstr ""
-
-msgid "Runner|Runner %{runnerName} failed to delete"
-msgstr ""
-
msgid "Running"
msgstr ""
diff --git a/spec/features/projects/issues/design_management/user_views_design_spec.rb b/spec/features/projects/issues/design_management/user_views_design_spec.rb
index 11c8bdda3ac..268c209cba1 100644
--- a/spec/features/projects/issues/design_management/user_views_design_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_design_spec.rb
@@ -24,4 +24,26 @@ RSpec.describe 'User views issue designs', :js, feature_category: :design_manage
expect(page).to have_selector('.js-design-image')
end
+
+ context 'when svg file is loaded in design detail' do
+ let_it_be(:file) { Rails.root.join('spec/fixtures/svg_without_attr.svg') }
+ let_it_be(:design) { create(:design, :with_file, filename: 'svg_without_attr.svg', file: file, issue: issue) }
+
+ before do
+ visit designs_project_issue_path(
+ project,
+ issue,
+ { vueroute: design.filename }
+ )
+ wait_for_requests
+ end
+
+ it 'check if svg is loading' do
+ expect(page).to have_selector(
+ ".js-design-image > img[alt='svg_without_attr.svg']",
+ count: 1,
+ visible: :hidden
+ )
+ end
+ end
end
diff --git a/spec/fixtures/svg_without_attr.svg b/spec/fixtures/svg_without_attr.svg
new file mode 100644
index 00000000000..0864c6009ef
--- /dev/null
+++ b/spec/fixtures/svg_without_attr.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg viewBox="0 0 50 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
+ <title>Slice 1</title>
+ <desc>Created with Sketch.</desc>
+ <script>alert('FAIL')</script>
+ <defs></defs>
+ <path d="m49.014 19-.067-.18-6.784-17.696a1.792 1.792 0 0 0-3.389.182l-4.579 14.02H15.651l-4.58-14.02a1.795 1.795 0 0 0-3.388-.182l-6.78 17.7-.071.175A12.595 12.595 0 0 0 5.01 33.556l.026.02.057.044 10.32 7.734 5.12 3.87 3.11 2.351a2.102 2.102 0 0 0 2.535 0l3.11-2.352 5.12-3.869 10.394-7.779.029-.022a12.595 12.595 0 0 0 4.182-14.554Z"
+ fill="#E24329"/>
+ <path d="m49.014 19-.067-.18a22.88 22.88 0 0 0-9.12 4.103L24.931 34.187l9.485 7.167 10.393-7.779.03-.022a12.595 12.595 0 0 0 4.175-14.554Z"
+ fill="#FC6D26"/>
+ <path d="m15.414 41.354 5.12 3.87 3.11 2.351a2.102 2.102 0 0 0 2.535 0l3.11-2.352 5.12-3.869-9.484-7.167-9.51 7.167Z"
+ fill="#FCA326"/>
+ <path d="M10.019 22.923a22.86 22.86 0 0 0-9.117-4.1L.832 19A12.595 12.595 0 0 0 5.01 33.556l.026.02.057.044 10.32 7.734 9.491-7.167L10.02 22.923Z"
+ fill="#FC6D26"/>
+</svg>
+
diff --git a/spec/frontend/design_management/components/__snapshots__/image_spec.js.snap b/spec/frontend/design_management/components/__snapshots__/image_spec.js.snap
index 7cffd3cf3e8..1f4e579f075 100644
--- a/spec/frontend/design_management/components/__snapshots__/image_spec.js.snap
+++ b/spec/frontend/design_management/components/__snapshots__/image_spec.js.snap
@@ -1,5 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`Design management large image component renders SVG with proper height and width 1`] = `
+<div
+ class="gl-mx-auto gl-my-auto js-design-image"
+>
+ <!---->
+
+ <img
+ alt="test"
+ class="mh-100 img-fluid"
+ src="mockImage.svg"
+ />
+</div>
+`;
+
exports[`Design management large image component renders image 1`] = `
<div
class="gl-mx-auto gl-my-auto js-design-image"
diff --git a/spec/frontend/design_management/components/image_spec.js b/spec/frontend/design_management/components/image_spec.js
index 8163cb0d87a..95d2ad504de 100644
--- a/spec/frontend/design_management/components/image_spec.js
+++ b/spec/frontend/design_management/components/image_spec.js
@@ -42,6 +42,16 @@ describe('Design management large image component', () => {
expect(wrapper.element).toMatchSnapshot();
});
+ it('renders SVG with proper height and width', () => {
+ createComponent({
+ isLoading: false,
+ image: 'mockImage.svg',
+ name: 'test',
+ });
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
it('sets correct classes and styles if imageStyle is set', async () => {
createComponent(
{
diff --git a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
index 096d776a7d2..3517c0f7a44 100644
--- a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
+++ b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
@@ -21,12 +21,14 @@ exports[`Design management list item component with notes renders item with mult
>
<!---->
- <gl-intersection-observer-stub>
+ <gl-intersection-observer-stub
+ class="gl-flex-grow-1"
+ >
<!---->
<img
alt="test"
- class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full design-img"
+ class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full gl-w-auto design-img"
data-qa-filename="test"
data-qa-selector="design_image"
data-testid="design-img-1"
@@ -98,12 +100,14 @@ exports[`Design management list item component with notes renders item with sing
>
<!---->
- <gl-intersection-observer-stub>
+ <gl-intersection-observer-stub
+ class="gl-flex-grow-1"
+ >
<!---->
<img
alt="test"
- class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full design-img"
+ class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full gl-w-auto design-img"
data-qa-filename="test"
data-qa-selector="design_image"
data-testid="design-img-1"
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index cc4a4416253..64a7502671e 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -767,17 +767,10 @@ describe('WorkItemDetail component', () => {
});
});
- it('does not render created/updated by default', async () => {
+ it('renders created/updated', async () => {
createComponent();
await waitForPromises();
- expect(findCreatedUpdated().exists()).toBe(false);
- });
-
- it('renders created/updated when the work_items_mvc flag is on', async () => {
- createComponent({ workItemsMvcEnabled: true });
- await waitForPromises();
-
expect(findCreatedUpdated().exists()).toBe(true);
});
});
diff --git a/spec/initializers/countries_spec.rb b/spec/initializers/countries_spec.rb
new file mode 100644
index 00000000000..2b968e41322
--- /dev/null
+++ b/spec/initializers/countries_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+RSpec.describe 'countries', feature_category: :onboarding do
+ it 'configures locals to EN' do
+ expect(ISO3166.configuration.locales).to eq([:en])
+ end
+
+ it 'initialises Ukraine with custom country name' do
+ expect(ISO3166::Country['UA'].data["name"]).to be('Ukraine (except the Crimea, Donetsk, and Luhansk regions)')
+ end
+
+ it 'initialises Taiwan with custom country name' do
+ expect(ISO3166::Country['TW'].data["name"]).to be('Taiwan')
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/bridge/factory_spec.rb b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb
index 552c64d81ce..040c3ec7f6e 100644
--- a/spec/lib/gitlab/ci/status/bridge/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb
@@ -131,7 +131,7 @@ RSpec.describe Gitlab::Ci::Status::Bridge::Factory, feature_category: :continuou
expect(status.text).to eq 'waiting'
expect(status.group).to eq 'waiting-for-resource'
expect(status.icon).to eq 'status_pending'
- expect(status.favicon).to eq 'favicon_pending'
+ expect(status.favicon).to eq 'favicon_status_pending'
expect(status.illustration).to include(:image, :size, :title)
expect(status).not_to have_details
end
diff --git a/spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb b/spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb
index bb6139accaf..6f5ab77a358 100644
--- a/spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb
+++ b/spec/lib/gitlab/ci/status/waiting_for_resource_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Ci::Status::WaitingForResource do
end
describe '#favicon' do
- it { expect(subject.favicon).to eq 'favicon_pending' }
+ it { expect(subject.favicon).to eq 'favicon_status_pending' }
end
describe '#group' do
diff --git a/spec/models/release_highlight_spec.rb b/spec/models/release_highlight_spec.rb
index 4148452f849..0391acc3781 100644
--- a/spec/models/release_highlight_spec.rb
+++ b/spec/models/release_highlight_spec.rb
@@ -6,7 +6,8 @@ RSpec.describe ReleaseHighlight, :clean_gitlab_redis_cache, feature_category: :r
let(:fixture_dir_glob) { Dir.glob(File.join(Rails.root, 'spec', 'fixtures', 'whats_new', '*.yml')).grep(/\d*_(\d*_\d*)\.yml$/) }
before do
- allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
+ allow(Dir).to receive(:glob).and_call_original
+ allow(Dir).to receive(:glob).with(described_class.whats_new_path).and_return(fixture_dir_glob)
Gitlab::CurrentSettings.update!(whats_new_variant: ApplicationSetting.whats_new_variants[:all_tiers])
end
diff --git a/spec/services/ci/archive_trace_service_spec.rb b/spec/services/ci/archive_trace_service_spec.rb
index 359ea0699e4..3fb9d092ae7 100644
--- a/spec/services/ci/archive_trace_service_spec.rb
+++ b/spec/services/ci/archive_trace_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ArchiveTraceService, '#execute' do
+RSpec.describe Ci::ArchiveTraceService, '#execute', feature_category: :continuous_integration do
subject { described_class.new.execute(job, worker_name: Ci::ArchiveTraceWorker.name) }
context 'when job is finished' do
@@ -192,4 +192,69 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do
expect(job.trace_metadata.archival_attempts).to eq(1)
end
end
+
+ describe '#batch_execute' do
+ subject { described_class.new.batch_execute(worker_name: Ci::ArchiveTraceWorker.name) }
+
+ let_it_be_with_reload(:job) { create(:ci_build, :success, :trace_live, finished_at: 1.day.ago) }
+ let_it_be_with_reload(:job2) { create(:ci_build, :success, :trace_live, finished_at: 1.day.ago) }
+
+ it 'archives multiple traces' do
+ expect { subject }.not_to raise_error
+
+ expect(job.reload.job_artifacts_trace).to be_exist
+ expect(job2.reload.job_artifacts_trace).to be_exist
+ end
+
+ it 'processes traces independently' do
+ allow_next_instance_of(Gitlab::Ci::Trace) do |instance|
+ orig_method = instance.method(:archive!)
+ allow(instance).to receive(:archive!) do
+ raise('Unexpected error') if instance.job.id == job.id
+
+ orig_method.call
+ end
+ end
+
+ expect { subject }.not_to raise_error
+
+ expect(job.reload.job_artifacts_trace).to be_nil
+ expect(job2.reload.job_artifacts_trace).to be_exist
+ end
+
+ context 'when timeout is reached' do
+ before do
+ stub_const("#{described_class}::LOOP_TIMEOUT", 0.seconds)
+ end
+
+ it 'stops executing traces' do
+ expect { subject }.not_to raise_error
+
+ expect(job.reload.job_artifacts_trace).to be_nil
+ end
+ end
+
+ context 'when loop limit is reached' do
+ before do
+ stub_const("#{described_class}::LOOP_LIMIT", -1)
+ end
+
+ it 'skips archiving' do
+ expect(job.trace).not_to receive(:archive!)
+
+ subject
+ end
+
+ it 'stops executing traces' do
+ expect(Sidekiq.logger).to receive(:warn).with(
+ class: Ci::ArchiveTraceWorker.name,
+ message: "Loop limit reached.",
+ job_id: job.id)
+
+ expect { subject }.not_to raise_error
+
+ expect(job.reload.job_artifacts_trace).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/workers/ci/archive_traces_cron_worker_spec.rb b/spec/workers/ci/archive_traces_cron_worker_spec.rb
index 14abe819587..0c1010960a1 100644
--- a/spec/workers/ci/archive_traces_cron_worker_spec.rb
+++ b/spec/workers/ci/archive_traces_cron_worker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ArchiveTracesCronWorker do
+RSpec.describe Ci::ArchiveTracesCronWorker, feature_category: :continuous_integration do
subject { described_class.new.perform }
let(:finished_at) { 1.day.ago }
@@ -34,14 +34,28 @@ RSpec.describe Ci::ArchiveTracesCronWorker do
it_behaves_like 'archives trace'
- it 'executes service' do
+ it 'batch_execute service' do
expect_next_instance_of(Ci::ArchiveTraceService) do |instance|
- expect(instance).to receive(:execute).with(build, anything)
+ expect(instance).to receive(:batch_execute).with(worker_name: "Ci::ArchiveTracesCronWorker")
end
subject
end
+ context "with FF deduplicate_archive_traces_cron_worker false" do
+ before do
+ stub_feature_flags(deduplicate_archive_traces_cron_worker: false)
+ end
+
+ it 'calls execute service' do
+ expect_next_instance_of(Ci::ArchiveTraceService) do |instance|
+ expect(instance).to receive(:execute).with(build, worker_name: "Ci::ArchiveTracesCronWorker")
+ end
+
+ subject
+ end
+ end
+
context 'when the job finished recently' do
let(:finished_at) { 1.hour.ago }