summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-04-09 00:09:46 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-04-09 00:09:46 +0000
commitb3c9b2468d3d45445ecd6873ad90ca0d34a8ab97 (patch)
tree829735adf37bfcf6839ed06e6842d7598a303e68
parent81c0f29ad962733b0750bdab2d3250e2c796a578 (diff)
downloadgitlab-ce-b3c9b2468d3d45445ecd6873ad90ca0d34a8ab97.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/repository/components/blob_content_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue17
-rw-r--r--app/controllers/users_controller.rb6
-rw-r--r--app/models/container_repository.rb18
-rw-r--r--app/models/project.rb11
-rw-r--r--app/views/admin/application_settings/_snowplow.html.haml6
-rw-r--r--app/views/admin/application_settings/_sourcegraph.html.haml12
-rw-r--r--app/views/notify/_note_email.html.haml7
-rw-r--r--app/views/projects/services/prometheus/_top.html.haml3
-rw-r--r--app/workers/concerns/packages/cleanup_artifact_worker.rb4
-rw-r--r--app/workers/container_registry/migration/enqueuer_worker.rb2
-rw-r--r--db/migrate/20220405125459_add_non_migrated_index_to_container_repositories.rb20
-rw-r--r--db/migrate/20220408135815_update_index_on_greated_done_at_on_container_repositories.rb24
-rw-r--r--db/schema_migrations/202204051254591
-rw-r--r--db/schema_migrations/202204081358151
-rw-r--r--db/structure.sql4
-rw-r--r--doc/ci/pipelines/settings.md9
-rw-r--r--lib/container_registry/base_client.rb16
-rw-r--r--lib/container_registry/gitlab_api_client.rb10
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js18
-rw-r--r--spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js17
-rw-r--r--spec/lib/container_registry/gitlab_api_client_spec.rb85
-rw-r--r--spec/models/container_repository_spec.rb46
-rw-r--r--spec/models/project_spec.rb33
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb7
-rw-r--r--spec/support/shared_contexts/lib/container_registry/client_stubs_shared_context.rb4
-rw-r--r--spec/workers/container_registry/migration/enqueuer_worker_spec.rb31
-rw-r--r--spec/workers/packages/cleanup_package_file_worker_spec.rb32
29 files changed, 351 insertions, 101 deletions
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue
index 7aadc00ccea..c9e4aab1db1 100644
--- a/app/assets/javascripts/repository/components/blob_content_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue
@@ -12,6 +12,7 @@ import { redirectTo } from '~/lib/utils/url_utility';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
import CodeIntelligence from '~/code_navigation/components/app.vue';
+import LineHighlighter from '~/blob/line_highlighter';
import getRefMixin from '../mixins/get_ref';
import blobInfoQuery from '../queries/blob_info.query.graphql';
import userInfoQuery from '../queries/user_info.query.graphql';
@@ -192,6 +193,7 @@ export default {
window.requestIdleCallback(() => {
this.isRenderingLegacyTextViewer = false;
+ new LineHighlighter(); // eslint-disable-line no-new
});
} else {
this.legacyRichViewer = html;
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
index 3aaa7d915ea..0117c06c3d5 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
@@ -1,7 +1,5 @@
<script>
import { GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import LineHighlighter from '~/blob/line_highlighter';
import { HIGHLIGHT_CLASS_NAME } from './constants';
import ViewerMixin from './mixins';
@@ -13,7 +11,7 @@ export default {
directives: {
SafeHtml: GlSafeHtmlDirective,
},
- mixins: [ViewerMixin, glFeatureFlagsMixin()],
+ mixins: [ViewerMixin],
inject: ['blobHash'],
data() {
return {
@@ -21,21 +19,14 @@ export default {
};
},
computed: {
- refactorBlobViewerEnabled() {
- return this.glFeatures.refactorBlobViewer;
- },
-
lineNumbers() {
return this.content.split('\n').length;
},
},
mounted() {
- if (this.refactorBlobViewerEnabled) {
- // This line will be removed once we start using highlight.js on the frontend (https://gitlab.com/groups/gitlab-org/-/epics/7146)
- new LineHighlighter(); // eslint-disable-line no-new
- } else {
- const { hash } = window.location;
- if (hash) this.scrollToLine(hash, true);
+ const { hash } = window.location;
+ if (hash) {
+ this.scrollToLine(hash, true);
}
},
methods: {
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index dc02e4a3e87..e6d9dae5989 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -27,7 +27,11 @@ class UsersController < ApplicationController
check_rate_limit!(:username_exists, scope: request.ip)
end
- feature_category :users
+ feature_category :users, [:show, :activity, :groups, :projects, :contributed, :starred,
+ :followers, :following, :calendar, :calendar_activities,
+ :exists, :activity, :follow, :unfollow, :ssh_keys, :gpg_keys]
+
+ feature_category :snippets, [:snippets]
def show
respond_to do |format|
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index bd2a12c7698..92e53beebae 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -17,6 +17,7 @@ class ContainerRepository < ApplicationRecord
SKIPPABLE_MIGRATION_STATES = (ABORTABLE_MIGRATION_STATES + %w[import_aborted]).freeze
MIGRATION_PHASE_1_STARTED_AT = Date.new(2021, 11, 4).freeze
+ MIGRATION_PHASE_1_ENDED_AT = Date.new(2022, 01, 23).freeze
TooManyImportsError = Class.new(StandardError)
@@ -58,8 +59,8 @@ class ContainerRepository < ApplicationRecord
scope :import_in_process, -> { where(migration_state: %w[pre_importing pre_import_done importing]) }
scope :recently_done_migration_step, -> do
- where(migration_state: %w[import_done pre_import_done import_aborted])
- .order(Arel.sql('GREATEST(migration_pre_import_done_at, migration_import_done_at, migration_aborted_at) DESC'))
+ where(migration_state: %w[import_done pre_import_done import_aborted import_skipped])
+ .order(Arel.sql('GREATEST(migration_pre_import_done_at, migration_import_done_at, migration_aborted_at, migration_skipped_at) DESC'))
end
scope :ready_for_import, -> do
@@ -160,7 +161,7 @@ class ContainerRepository < ApplicationRecord
end
end
- before_transition %i[pre_importing import_aborted] => :pre_import_done do |container_repository|
+ before_transition any => :pre_import_done do |container_repository|
container_repository.migration_pre_import_done_at = Time.zone.now
end
@@ -217,6 +218,13 @@ class ContainerRepository < ApplicationRecord
).exists?
end
+ def self.all_migrated?
+ # check that the set of non migrated repositories is empty
+ where(created_at: ...MIGRATION_PHASE_1_ENDED_AT)
+ .where.not(migration_state: 'import_done')
+ .empty?
+ end
+
def self.with_enabled_policy
joins('INNER JOIN container_expiration_policies ON container_repositories.project_id = container_expiration_policies.project_id')
.where(container_expiration_policies: { enabled: true })
@@ -359,7 +367,7 @@ class ContainerRepository < ApplicationRecord
end
def last_import_step_done_at
- [migration_pre_import_done_at, migration_import_done_at, migration_aborted_at].compact.max
+ [migration_pre_import_done_at, migration_import_done_at, migration_aborted_at, migration_skipped_at].compact.max
end
def external_import_status
@@ -456,7 +464,7 @@ class ContainerRepository < ApplicationRecord
next if self.created_at.before?(MIGRATION_PHASE_1_STARTED_AT)
next unless gitlab_api_client.supports_gitlab_api?
- gitlab_api_client.repository_details(self.path, with_size: true)['size_bytes']
+ gitlab_api_client.repository_details(self.path, sizing: :self)['size_bytes']
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 7796bdf67e2..c15c3a65e74 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1063,6 +1063,17 @@ class Project < ApplicationRecord
end
end
+ def container_repositories_size
+ strong_memoize(:container_repositories_size) do
+ next unless Gitlab.com?
+ next 0 if container_repositories.empty?
+ next unless container_repositories.all_migrated?
+ next unless ContainerRegistry::GitlabApiClient.supports_gitlab_api?
+
+ ContainerRegistry::GitlabApiClient.deduplicated_size(full_path)
+ end
+ end
+
def has_container_registry_tags?
return @images if defined?(@images)
diff --git a/app/views/admin/application_settings/_snowplow.html.haml b/app/views/admin/application_settings/_snowplow.html.haml
index f7a6a26c645..378c1712ae0 100644
--- a/app/views/admin/application_settings/_snowplow.html.haml
+++ b/app/views/admin/application_settings/_snowplow.html.haml
@@ -9,14 +9,12 @@
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('development/snowplow/index') }
= html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank', rel: 'noopener noreferrer').html_safe, link_start: link_start, link_end: '</a>'.html_safe }
.settings-content
- = form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-snowplow-settings'), html: { class: 'fieldset-form', id: 'snowplow-settings' } do |f|
+ = gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-snowplow-settings'), html: { class: 'fieldset-form', id: 'snowplow-settings' } do |f|
= form_errors(@application_setting) if expanded
%fieldset
.form-group
- .form-check
- = f.check_box :snowplow_enabled, class: 'form-check-input', data: { qa_selector: 'snowplow_enabled_checkbox' }
- = f.label :snowplow_enabled, _('Enable Snowplow tracking'), class: 'form-check-label'
+ = f.gitlab_ui_checkbox_component :snowplow_enabled, _('Enable Snowplow tracking'), checkbox_options: { data: { qa_selector: 'snowplow_enabled_checkbox' } }
.form-group
= f.label :snowplow_collector_hostname, _('Collector hostname'), class: 'label-light'
= f.text_field :snowplow_collector_hostname, class: 'form-control gl-form-input', placeholder: 'snowplow.example.com'
diff --git a/app/views/admin/application_settings/_sourcegraph.html.haml b/app/views/admin/application_settings/_sourcegraph.html.haml
index 65b2a95bcc1..391f79e431b 100644
--- a/app/views/admin/application_settings/_sourcegraph.html.haml
+++ b/app/views/admin/application_settings/_sourcegraph.html.haml
@@ -16,20 +16,14 @@
.settings-content
- = form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-sourcegraph-settings'), html: { class: 'fieldset-form', id: 'sourcegraph-settings' } do |f|
+ = gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-sourcegraph-settings'), html: { class: 'fieldset-form', id: 'sourcegraph-settings' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- .form-check
- = f.check_box :sourcegraph_enabled, class: 'form-check-input'
- = f.label :sourcegraph_enabled, s_('SourcegraphAdmin|Enable Sourcegraph'), class: 'form-check-label'
+ = f.gitlab_ui_checkbox_component :sourcegraph_enabled, s_('SourcegraphAdmin|Enable Sourcegraph')
.form-group
- .form-check
- = f.check_box :sourcegraph_public_only, class: 'form-check-input'
- = f.label :sourcegraph_public_only, s_('SourcegraphAdmin|Block on private and internal projects'), class: 'form-check-label'
- .form-text.text-muted
- = s_('SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph.')
+ = f.gitlab_ui_checkbox_component :sourcegraph_public_only, s_('SourcegraphAdmin|Block on private and internal projects'), help_text: s_('SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph.')
.form-group
= f.label :sourcegraph_url, s_('SourcegraphAdmin|Sourcegraph URL'), class: 'label-bold'
= f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|https://sourcegraph.example.com')
diff --git a/app/views/notify/_note_email.html.haml b/app/views/notify/_note_email.html.haml
index f2c67b84c80..2f0e62981ec 100644
--- a/app/views/notify/_note_email.html.haml
+++ b/app/views/notify/_note_email.html.haml
@@ -23,9 +23,10 @@
- else
= link_to 'discussion', target_url
-- if include_stylesheet_link && discussion&.diff_discussion? && discussion.on_text?
- = content_for :head do
- = stylesheet_link_tag 'mailers/highlighted_diff_email'
+- if discussion&.diff_discussion? && discussion.on_text?
+ - if include_stylesheet_link
+ = content_for :head do
+ = stylesheet_link_tag 'mailers/highlighted_diff_email'
%table.code.gl-mb-5
= render partial: "projects/diffs/email_line",
diff --git a/app/views/projects/services/prometheus/_top.html.haml b/app/views/projects/services/prometheus/_top.html.haml
index f7446273a80..52b29ea2e8f 100644
--- a/app/views/projects/services/prometheus/_top.html.haml
+++ b/app/views/projects/services/prometheus/_top.html.haml
@@ -2,8 +2,7 @@
.row
.col-lg-12
- .gl-alert.gl-alert-info{ role: 'alert' }
- = sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ = render Pajamas::AlertComponent.new(dismissible: false) do
.gl-alert-body
= s_('AlertSettings|You can now set up alert endpoints for manually configured Prometheus instances in the Alerts section on the Operations settings page. Alert endpoint fields on this page have been deprecated.')
.gl-alert-actions
diff --git a/app/workers/concerns/packages/cleanup_artifact_worker.rb b/app/workers/concerns/packages/cleanup_artifact_worker.rb
index bee9587cb35..a01d7e8abba 100644
--- a/app/workers/concerns/packages/cleanup_artifact_worker.rb
+++ b/app/workers/concerns/packages/cleanup_artifact_worker.rb
@@ -14,7 +14,9 @@ module Packages
artifact.destroy!
rescue StandardError
- artifact&.update_column(:status, :error)
+ unless artifact&.destroyed?
+ artifact&.update_column(:status, :error)
+ end
end
after_destroy
diff --git a/app/workers/container_registry/migration/enqueuer_worker.rb b/app/workers/container_registry/migration/enqueuer_worker.rb
index 51029626f40..8705deb0cb2 100644
--- a/app/workers/container_registry/migration/enqueuer_worker.rb
+++ b/app/workers/container_registry/migration/enqueuer_worker.rb
@@ -82,7 +82,7 @@ module ContainerRegistry
def waiting_time_passed?
delay = migration.enqueue_waiting_time
return true if delay == 0
- return true unless last_step_completed_repository
+ return true unless last_step_completed_repository&.last_import_step_done_at
last_step_completed_repository.last_import_step_done_at < Time.zone.now - delay
end
diff --git a/db/migrate/20220405125459_add_non_migrated_index_to_container_repositories.rb b/db/migrate/20220405125459_add_non_migrated_index_to_container_repositories.rb
new file mode 100644
index 00000000000..7aad10c288b
--- /dev/null
+++ b/db/migrate/20220405125459_add_non_migrated_index_to_container_repositories.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddNonMigratedIndexToContainerRepositories < Gitlab::Database::Migration[1.0]
+ disable_ddl_transaction!
+
+ # follow up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/358407
+ INDEX_NAME = 'tmp_idx_container_repos_on_non_migrated'
+ MIGRATION_PHASE_1_ENDED_AT = '2022-01-23'
+
+ def up
+ add_concurrent_index :container_repositories,
+ [:project_id, :id],
+ name: INDEX_NAME,
+ where: "migration_state != 'import_done' AND created_at < '#{MIGRATION_PHASE_1_ENDED_AT}'"
+ end
+
+ def down
+ remove_concurrent_index_by_name :container_repositories, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220408135815_update_index_on_greated_done_at_on_container_repositories.rb b/db/migrate/20220408135815_update_index_on_greated_done_at_on_container_repositories.rb
new file mode 100644
index 00000000000..b906a867895
--- /dev/null
+++ b/db/migrate/20220408135815_update_index_on_greated_done_at_on_container_repositories.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class UpdateIndexOnGreatedDoneAtOnContainerRepositories < Gitlab::Database::Migration[1.0]
+ OLD_INDEX_NAME = 'index_container_repositories_on_greatest_done_at'
+ NEW_INDEX_NAME = 'index_container_repositories_on_greatest_completed_at'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :container_repositories,
+ 'GREATEST(migration_pre_import_done_at, migration_import_done_at, migration_aborted_at, migration_skipped_at)',
+ where: "migration_state IN ('import_done', 'pre_import_done', 'import_aborted', 'import_skipped')",
+ name: NEW_INDEX_NAME
+ remove_concurrent_index_by_name :container_repositories, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :container_repositories,
+ 'GREATEST(migration_pre_import_done_at, migration_import_done_at, migration_aborted_at)',
+ where: "migration_state IN ('import_done', 'pre_import_done', 'import_aborted')",
+ name: OLD_INDEX_NAME
+ remove_concurrent_index_by_name :container_repositories, NEW_INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20220405125459 b/db/schema_migrations/20220405125459
new file mode 100644
index 00000000000..1ed46eff43e
--- /dev/null
+++ b/db/schema_migrations/20220405125459
@@ -0,0 +1 @@
+c4dcb2b2e1262d63c56e171796f1cb6fb76d4b7dc090cf585f17a451c2fa784f \ No newline at end of file
diff --git a/db/schema_migrations/20220408135815 b/db/schema_migrations/20220408135815
new file mode 100644
index 00000000000..3a41e4a5420
--- /dev/null
+++ b/db/schema_migrations/20220408135815
@@ -0,0 +1 @@
+01d8ab924e8c76b54d316ba94089eabea28999e4ce747e6c51803e1ea97b37df \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 38cf5d58524..fa1516d4bcf 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -27308,7 +27308,7 @@ CREATE INDEX index_composer_cache_files_where_namespace_id_is_null ON packages_c
CREATE INDEX index_container_expiration_policies_on_next_run_at_and_enabled ON container_expiration_policies USING btree (next_run_at, enabled);
-CREATE INDEX index_container_repositories_on_greatest_done_at ON container_repositories USING btree (GREATEST(migration_pre_import_done_at, migration_import_done_at, migration_aborted_at)) WHERE (migration_state = ANY (ARRAY['import_done'::text, 'pre_import_done'::text, 'import_aborted'::text]));
+CREATE INDEX index_container_repositories_on_greatest_completed_at ON container_repositories USING btree (GREATEST(migration_pre_import_done_at, migration_import_done_at, migration_aborted_at, migration_skipped_at)) WHERE (migration_state = ANY (ARRAY['import_done'::text, 'pre_import_done'::text, 'import_aborted'::text, 'import_skipped'::text]));
CREATE INDEX index_container_repositories_on_migration_state_import_done_at ON container_repositories USING btree (migration_state, migration_import_done_at);
@@ -29646,6 +29646,8 @@ CREATE INDEX tmp_gitlab_subscriptions_max_seats_used_migration ON gitlab_subscri
CREATE INDEX tmp_gitlab_subscriptions_max_seats_used_migration_2 ON gitlab_subscriptions USING btree (id) WHERE ((start_date < '2021-08-02'::date) AND (max_seats_used <> 0) AND (max_seats_used > seats_in_use) AND (max_seats_used > seats));
+CREATE INDEX tmp_idx_container_repos_on_non_migrated ON container_repositories USING btree (project_id, id) WHERE ((migration_state <> 'import_done'::text) AND (created_at < '2022-01-23 00:00:00'::timestamp without time zone));
+
CREATE INDEX tmp_index_ci_job_artifacts_on_id_where_trace_and_expire_at ON ci_job_artifacts USING btree (id) WHERE ((file_type = 3) AND (expire_at = ANY (ARRAY['2021-04-22 00:00:00+00'::timestamp with time zone, '2021-05-22 00:00:00+00'::timestamp with time zone, '2021-06-22 00:00:00+00'::timestamp with time zone, '2022-01-22 00:00:00+00'::timestamp with time zone, '2022-02-22 00:00:00+00'::timestamp with time zone, '2022-03-22 00:00:00+00'::timestamp with time zone, '2022-04-22 00:00:00+00'::timestamp with time zone])));
CREATE INDEX tmp_index_container_repositories_on_id_migration_state ON container_repositories USING btree (id, migration_state);
diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md
index 7960d0afa85..aa19c075c80 100644
--- a/doc/ci/pipelines/settings.md
+++ b/doc/ci/pipelines/settings.md
@@ -76,7 +76,14 @@ To avoid this scenario:
1. Select the **Skip outdated deployment jobs** checkbox.
1. Select **Save changes**.
-Older deployment jobs are skipped when a new deployment starts.
+When a new deployment starts, older deployment jobs are skipped. Skipped jobs are labeled:
+
+- `forward deployment failure` in the pipeline view.
+- `The deployment job is older than the previously succeeded deployment job, and therefore cannot be run`
+ when viewing the completed job.
+
+Job age is determined by the job start time, not the commit time, so a newer commit
+can be skipped in some circumstances.
For more information, see [Deployment safety](../environments/deployment_safety.md).
diff --git a/lib/container_registry/base_client.rb b/lib/container_registry/base_client.rb
index 22d4510fe71..bb9422ae048 100644
--- a/lib/container_registry/base_client.rb
+++ b/lib/container_registry/base_client.rb
@@ -37,14 +37,24 @@ module ContainerRegistry
class << self
private
- def with_dummy_client(return_value_if_disabled: nil)
+ def with_dummy_client(return_value_if_disabled: nil, token_config: { type: :full_access_token, path: nil })
registry_config = Gitlab.config.registry
unless registry_config.enabled && registry_config.api_url.present?
return return_value_if_disabled
end
- token = Auth::ContainerRegistryAuthenticationService.access_token([], [])
- yield new(registry_config.api_url, token: token)
+ yield new(registry_config.api_url, token: token_from(token_config))
+ end
+
+ def token_from(config)
+ case config[:type]
+ when :full_access_token
+ Auth::ContainerRegistryAuthenticationService.access_token([], [])
+ when :nested_repositories_token
+ return unless config[:path]
+
+ Auth::ContainerRegistryAuthenticationService.pull_nested_repositories_access_token(config[:path])
+ end
end
end
diff --git a/lib/container_registry/gitlab_api_client.rb b/lib/container_registry/gitlab_api_client.rb
index c627ad30311..565ce3a8c2e 100644
--- a/lib/container_registry/gitlab_api_client.rb
+++ b/lib/container_registry/gitlab_api_client.rb
@@ -27,6 +27,12 @@ module ContainerRegistry
end
end
+ def self.deduplicated_size(path)
+ with_dummy_client(token_config: { type: :nested_repositories_token, path: path }) do |client|
+ client.repository_details(path, sizing: :self_with_descendants)['size_bytes']
+ end
+ end
+
# https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs-gitlab/api.md#compliance-check
def supports_gitlab_api?
strong_memoize(:supports_gitlab_api) do
@@ -78,10 +84,10 @@ module ContainerRegistry
end
end
- def repository_details(path, with_size: false)
+ def repository_details(path, sizing: nil)
with_token_faraday do |faraday_client|
req = faraday_client.get("/gitlab/v1/repositories/#{path}/") do |req|
- req.params['size'] = 'self' if with_size
+ req.params['size'] = sizing if sizing
end
break {} unless req.success?
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 2ae4cad9f40..5561f27044c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -39298,6 +39298,9 @@ msgstr ""
msgid "To access this domain create a new DNS record"
msgstr ""
+msgid "To activate your trial, we need additional details from you."
+msgstr ""
+
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -39307,6 +39310,9 @@ msgstr ""
msgid "To ask someone to look at a merge request, select %{strongStart}Request attention%{strongEnd}. Select again to remove the request."
msgstr ""
+msgid "To complete registration, we need additional details from you."
+msgstr ""
+
msgid "To confirm, type %{phrase_code}"
msgstr ""
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js
index 83e0111ecca..2f6de03b73d 100644
--- a/spec/frontend/repository/components/blob_content_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_content_viewer_spec.js
@@ -25,6 +25,7 @@ import { redirectTo } from '~/lib/utils/url_utility';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import httpStatusCodes from '~/lib/utils/http_status';
+import LineHighlighter from '~/blob/line_highlighter';
import {
simpleViewerMock,
richViewerMock,
@@ -39,6 +40,7 @@ import {
jest.mock('~/repository/components/blob_viewers');
jest.mock('~/lib/utils/url_utility');
jest.mock('~/lib/utils/common_utils');
+jest.mock('~/blob/line_highlighter');
let wrapper;
let mockResolver;
@@ -173,20 +175,30 @@ describe('Blob content viewer component', () => {
});
describe('legacy viewers', () => {
+ const legacyViewerUrl = 'some_file.js?format=json&viewer=simple';
+ const fileType = 'text';
+ const highlightJs = false;
+
it('loads a legacy viewer when a the fileType is text and the highlightJs feature is turned off', async () => {
await createComponent({
- blob: { ...simpleViewerMock, fileType: 'text', highlightJs: false },
+ blob: { ...simpleViewerMock, fileType, highlightJs },
});
expect(mockAxios.history.get).toHaveLength(1);
- expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=simple');
+ expect(mockAxios.history.get[0].url).toBe(legacyViewerUrl);
});
it('loads a legacy viewer when a viewer component is not available', async () => {
await createComponent({ blob: { ...simpleViewerMock, fileType: 'unknown' } });
expect(mockAxios.history.get).toHaveLength(1);
- expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=simple');
+ expect(mockAxios.history.get[0].url).toBe(legacyViewerUrl);
+ });
+
+ it('loads the LineHighlighter', async () => {
+ mockAxios.onGet(legacyViewerUrl).replyOnce(httpStatusCodes.OK, 'test');
+ await createComponent({ blob: { ...simpleViewerMock, fileType, highlightJs } });
+ expect(LineHighlighter).toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js b/spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js
index 663ebd3e12f..4b44311b253 100644
--- a/spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js
@@ -2,9 +2,6 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { HIGHLIGHT_CLASS_NAME } from '~/vue_shared/components/blob_viewers/constants';
import SimpleViewer from '~/vue_shared/components/blob_viewers/simple_viewer.vue';
-import LineHighlighter from '~/blob/line_highlighter';
-
-jest.mock('~/blob/line_highlighter');
describe('Blob Simple Viewer component', () => {
let wrapper;
@@ -30,20 +27,6 @@ describe('Blob Simple Viewer component', () => {
wrapper.destroy();
});
- describe('refactorBlobViewer feature flag', () => {
- it('loads the LineHighlighter if refactorBlobViewer is enabled', () => {
- createComponent('', false, { refactorBlobViewer: true });
-
- expect(LineHighlighter).toHaveBeenCalled();
- });
-
- it('does not load the LineHighlighter if refactorBlobViewer is disabled', () => {
- createComponent('', false, { refactorBlobViewer: false });
-
- expect(LineHighlighter).not.toHaveBeenCalled();
- });
- });
-
it('does not fail if content is empty', () => {
const spy = jest.spyOn(window.console, 'error');
createComponent('');
diff --git a/spec/lib/container_registry/gitlab_api_client_spec.rb b/spec/lib/container_registry/gitlab_api_client_spec.rb
index 0fb3b53d42a..9fe74534292 100644
--- a/spec/lib/container_registry/gitlab_api_client_spec.rb
+++ b/spec/lib/container_registry/gitlab_api_client_spec.rb
@@ -174,31 +174,26 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
describe '#repository_details' do
let(:path) { 'namespace/path/to/repository' }
let(:response) { { foo: :bar, this: :is_a_test } }
- let(:with_size) { true }
- subject { client.repository_details(path, with_size: with_size) }
+ subject { client.repository_details(path, sizing: sizing) }
- context 'with size' do
- before do
- stub_repository_details(path, with_size: with_size, respond_with: response)
- end
+ [:self, :self_with_descendants, nil].each do |size_type|
+ context "with sizing #{size_type}" do
+ let(:sizing) { size_type }
- it { is_expected.to eq(response.stringify_keys.deep_transform_values(&:to_s)) }
- end
-
- context 'without_size' do
- let(:with_size) { false }
+ before do
+ stub_repository_details(path, sizing: sizing, respond_with: response)
+ end
- before do
- stub_repository_details(path, with_size: with_size, respond_with: response)
+ it { is_expected.to eq(response.stringify_keys.deep_transform_values(&:to_s)) }
end
-
- it { is_expected.to eq(response.stringify_keys.deep_transform_values(&:to_s)) }
end
context 'with non successful response' do
+ let(:sizing) { nil }
+
before do
- stub_repository_details(path, with_size: with_size, status_code: 404)
+ stub_repository_details(path, sizing: sizing, status_code: 404)
end
it { is_expected.to eq({}) }
@@ -263,6 +258,54 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
end
end
+ describe '.deduplicated_size' do
+ let(:path) { 'foo/bar' }
+ let(:response) { { 'size_bytes': 555 } }
+ let(:registry_enabled) { true }
+
+ subject { described_class.deduplicated_size(path) }
+
+ before do
+ stub_container_registry_config(enabled: registry_enabled, api_url: registry_api_url, key: 'spec/fixtures/x509_certificate_pk.key')
+ end
+
+ context 'with successful response' do
+ before do
+ expect(Auth::ContainerRegistryAuthenticationService).to receive(:pull_nested_repositories_access_token).with(path).and_return(token)
+ stub_repository_details(path, sizing: :self_with_descendants, status_code: 200, respond_with: response)
+ end
+
+ it { is_expected.to eq(555) }
+ end
+
+ context 'with unsuccessful response' do
+ before do
+ expect(Auth::ContainerRegistryAuthenticationService).to receive(:pull_nested_repositories_access_token).with(path).and_return(token)
+ stub_repository_details(path, sizing: :self_with_descendants, status_code: 404, respond_with: response)
+ end
+
+ it { is_expected.to eq(nil) }
+ end
+
+ context 'with the registry disabled' do
+ let(:registry_enabled) { false }
+
+ it { is_expected.to eq(nil) }
+ end
+
+ context 'with a nil path' do
+ let(:path) { nil }
+ let(:token) { nil }
+
+ before do
+ expect(Auth::ContainerRegistryAuthenticationService).not_to receive(:pull_nested_repositories_access_token)
+ stub_repository_details(path, sizing: :self_with_descendants, status_code: 401, respond_with: response)
+ end
+
+ it { is_expected.to eq(nil) }
+ end
+ end
+
def stub_pre_import(path, status_code, pre:)
import_type = pre ? 'pre' : 'final'
stub_request(:put, "#{registry_api_url}/gitlab/v1/import/#{path}/?import_type=#{import_type}")
@@ -303,11 +346,15 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
)
end
- def stub_repository_details(path, with_size: true, status_code: 200, respond_with: {})
+ def stub_repository_details(path, sizing: nil, status_code: 200, respond_with: {})
url = "#{registry_api_url}/gitlab/v1/repositories/#{path}/"
- url += "?size=self" if with_size
+ url += "?size=#{sizing}" if sizing
+
+ headers = { 'Accept' => described_class::JSON_TYPE }
+ headers['Authorization'] = "bearer #{token}" if token
+
stub_request(:get, url)
- .with(headers: { 'Accept' => described_class::JSON_TYPE, 'Authorization' => "bearer #{token}" })
+ .with(headers: headers)
.to_return(status: status_code, body: respond_with.to_json, headers: { 'Content-Type' => described_class::JSON_TYPE })
end
end
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb
index ed4084a510e..6a4f2fb3e30 100644
--- a/spec/models/container_repository_spec.rb
+++ b/spec/models/container_repository_spec.rb
@@ -652,7 +652,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do
context 'supports gitlab api on .com with a recent repository' do
before do
expect(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(true)
- expect(repository.gitlab_api_client).to receive(:repository_details).with(repository.path, with_size: true).and_return(response)
+ expect(repository.gitlab_api_client).to receive(:repository_details).with(repository.path, sizing: :self).and_return(response)
end
context 'with a size_bytes field' do
@@ -1076,6 +1076,43 @@ RSpec.describe ContainerRepository, :aggregate_failures do
end
end
+ describe '.all_migrated?' do
+ let_it_be(:project) { create(:project) }
+
+ subject { project.container_repositories.all_migrated? }
+
+ context 'with no repositories' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'with only recent repositories' do
+ let_it_be(:container_repository1) { create(:container_repository, project: project) }
+ let_it_be_with_reload(:container_repository2) { create(:container_repository, project: project) }
+
+ it { is_expected.to be_truthy }
+
+ context 'with one old non migrated repository' do
+ before do
+ container_repository2.update!(created_at: described_class::MIGRATION_PHASE_1_ENDED_AT - 3.months)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'with one old migrated repository' do
+ before do
+ container_repository2.update!(
+ created_at: described_class::MIGRATION_PHASE_1_ENDED_AT - 3.months,
+ migration_state: 'import_done',
+ migration_import_done_at: Time.zone.now
+ )
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
+
describe '.with_enabled_policy' do
let_it_be(:repository) { create(:container_repository) }
let_it_be(:repository2) { create(:container_repository) }
@@ -1271,11 +1308,12 @@ RSpec.describe ContainerRepository, :aggregate_failures do
let_it_be(:import_done_repository) { create(:container_repository, :import_done, migration_pre_import_done_at: 3.days.ago, migration_import_done_at: 2.days.ago) }
let_it_be(:import_aborted_repository) { create(:container_repository, :import_aborted, migration_pre_import_done_at: 5.days.ago, migration_aborted_at: 1.day.ago) }
let_it_be(:pre_import_done_repository) { create(:container_repository, :pre_import_done, migration_pre_import_done_at: 1.hour.ago) }
+ let_it_be(:import_skipped_repository) { create(:container_repository, :import_skipped, migration_skipped_at: 90.minutes.ago) }
subject { described_class.recently_done_migration_step }
it 'returns completed imports by done_at date' do
- expect(subject.to_a).to eq([pre_import_done_repository, import_aborted_repository, import_done_repository])
+ expect(subject.to_a).to eq([pre_import_done_repository, import_skipped_repository, import_aborted_repository, import_done_repository])
end
end
@@ -1296,13 +1334,15 @@ RSpec.describe ContainerRepository, :aggregate_failures do
describe '#last_import_step_done_at' do
let_it_be(:aborted_at) { Time.zone.now - 1.hour }
let_it_be(:pre_import_done_at) { Time.zone.now - 2.hours }
+ let_it_be(:skipped_at) { Time.zone.now - 3.hours }
subject { repository.last_import_step_done_at }
before do
repository.update_columns(
migration_pre_import_done_at: pre_import_done_at,
- migration_aborted_at: aborted_at
+ migration_aborted_at: aborted_at,
+ migration_skipped_at: skipped_at
)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 1a0e0274539..ead7f7d0786 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2715,6 +2715,39 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#container_repositories_size' do
+ let(:project) { build(:project) }
+
+ subject { project.container_repositories_size }
+
+ context 'on gitlab.com' do
+ where(:no_container_repositories, :all_migrated, :gitlab_api_supported, :returned_size, :expected_result) do
+ true | nil | nil | nil | 0
+ false | false | nil | nil | nil
+ false | true | false | nil | nil
+ false | true | true | 555 | 555
+ false | true | true | nil | nil
+ end
+
+ with_them do
+ before do
+ stub_container_registry_config(enabled: true, api_url: 'http://container-registry', key: 'spec/fixtures/x509_certificate_pk.key')
+ allow(Gitlab).to receive(:com?).and_return(true)
+ allow(project.container_repositories).to receive(:empty?).and_return(no_container_repositories)
+ allow(project.container_repositories).to receive(:all_migrated?).and_return(all_migrated)
+ allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(gitlab_api_supported)
+ allow(ContainerRegistry::GitlabApiClient).to receive(:deduplicated_size).with(project.full_path).and_return(returned_size)
+ end
+
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+
+ context 'not on gitlab.com' do
+ it { is_expected.to eq(nil) }
+ end
+ end
+
describe '#container_registry_enabled=' do
let_it_be_with_reload(:project) { create(:project) }
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 70b794f7d82..f5e11f46067 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -86,6 +86,13 @@ module CycleAnalyticsHelpers
wait_for_stages_to_load(ready_selector)
end
+ def select_value_stream(value_stream_name)
+ toggle_value_stream_dropdown
+
+ page.find('[data-testid="dropdown-value-streams"]').all('li button').find { |item| item.text == value_stream_name.to_s }.click
+ wait_for_requests
+ end
+
def toggle_dropdown(field)
page.within("[data-testid*='#{field}']") do
find('.dropdown-toggle').click
diff --git a/spec/support/shared_contexts/lib/container_registry/client_stubs_shared_context.rb b/spec/support/shared_contexts/lib/container_registry/client_stubs_shared_context.rb
index d857e683aa2..196173d4a63 100644
--- a/spec/support/shared_contexts/lib/container_registry/client_stubs_shared_context.rb
+++ b/spec/support/shared_contexts/lib/container_registry/client_stubs_shared_context.rb
@@ -8,8 +8,8 @@ RSpec.shared_context 'container registry client stubs' do
end
end
- def stub_container_registry_gitlab_api_repository_details(client, path:, size_bytes:)
- allow(client).to receive(:repository_details).with(path, with_size: true).and_return('size_bytes' => size_bytes)
+ def stub_container_registry_gitlab_api_repository_details(client, path:, size_bytes:, sizing: :self)
+ allow(client).to receive(:repository_details).with(path, sizing: sizing).and_return('size_bytes' => size_bytes)
end
def stub_container_registry_gitlab_api_network_error(client_method: :supports_gitlab_api?)
diff --git a/spec/workers/container_registry/migration/enqueuer_worker_spec.rb b/spec/workers/container_registry/migration/enqueuer_worker_spec.rb
index 953576f2599..8ad605eed3a 100644
--- a/spec/workers/container_registry/migration/enqueuer_worker_spec.rb
+++ b/spec/workers/container_registry/migration/enqueuer_worker_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe ContainerRegistry::Migration::EnqueuerWorker, :aggregate_failures, :clean_gitlab_redis_shared_state do
+ using RSpec::Parameterized::TableSyntax
include ExclusiveLeaseHelpers
let_it_be_with_reload(:container_repository) { create(:container_repository, created_at: 2.days.ago) }
@@ -131,14 +132,34 @@ RSpec.describe ContainerRegistry::Migration::EnqueuerWorker, :aggregate_failures
end
context 'too soon before previous completed import step' do
- before do
- create(:container_repository, :import_done, migration_import_done_at: 1.minute.ago)
- allow(ContainerRegistry::Migration).to receive(:enqueue_waiting_time).and_return(1.hour)
+ where(:state, :timestamp) do
+ :import_done | :migration_import_done_at
+ :pre_import_done | :migration_pre_import_done_at
+ :import_aborted | :migration_aborted_at
+ :import_skipped | :migration_skipped_at
end
- it_behaves_like 'no action' do
+ with_them do
before do
- expect_log_extra_metadata(waiting_time_passed: false, current_waiting_time_setting: 1.hour)
+ allow(ContainerRegistry::Migration).to receive(:enqueue_waiting_time).and_return(1.hour)
+ create(:container_repository, state, timestamp => 1.minute.ago)
+ end
+
+ it_behaves_like 'no action' do
+ before do
+ expect_log_extra_metadata(waiting_time_passed: false, current_waiting_time_setting: 1.hour)
+ end
+ end
+ end
+
+ context 'when last completed repository has nil timestamps' do
+ before do
+ allow(ContainerRegistry::Migration).to receive(:enqueue_waiting_time).and_return(1.hour)
+ create(:container_repository, migration_state: 'import_done')
+ end
+
+ it 'continues to try the next import' do
+ expect { subject }.to change { container_repository.reload.migration_state }
end
end
end
diff --git a/spec/workers/packages/cleanup_package_file_worker_spec.rb b/spec/workers/packages/cleanup_package_file_worker_spec.rb
index 23553ba472e..702026ed1c7 100644
--- a/spec/workers/packages/cleanup_package_file_worker_spec.rb
+++ b/spec/workers/packages/cleanup_package_file_worker_spec.rb
@@ -43,16 +43,36 @@ RSpec.describe Packages::CleanupPackageFileWorker do
end
end
- context 'with an error during the destroy' do
+ context 'with a package file to destroy' do
let_it_be(:package_file) { create(:package_file, :pending_destruction) }
- before do
- expect(worker).to receive(:log_metadata).and_raise('Error!')
+ context 'with an error during the destroy' do
+ before do
+ expect(worker).to receive(:log_metadata).and_raise('Error!')
+ end
+
+ it 'handles the error' do
+ expect { subject }.to change { Packages::PackageFile.error.count }.from(0).to(1)
+ expect(package_file.reload).to be_error
+ end
end
- it 'handles the error' do
- expect { subject }.to change { Packages::PackageFile.error.count }.from(0).to(1)
- expect(package_file.reload).to be_error
+ context 'when trying to destroy a destroyed record' do
+ before do
+ allow_next_found_instance_of(Packages::PackageFile) do |package_file|
+ destroy_method = package_file.method(:destroy!)
+
+ allow(package_file).to receive(:destroy!) do
+ destroy_method.call
+
+ raise 'Error!'
+ end
+ end
+ end
+
+ it 'handles the error' do
+ expect { subject }.to change { Packages::PackageFile.count }.by(-1)
+ end
end
end