diff options
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 Binary files differindex 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 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 } |