From e40c68997d44209aed2baf3a8ec6be9ae99fb0b5 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 24 Feb 2022 12:12:57 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop_todo.yml | 17 --- .../monitoring/components/charts/bar.vue | 34 ++--- .../monitoring/components/charts/column.vue | 34 ++--- .../monitoring/components/charts/gauge.vue | 36 ++--- .../monitoring/components/charts/heatmap.vue | 34 ++--- .../components/charts/stacked_column.vue | 46 +++--- .../components/training_provider_list.vue | 12 +- .../graphql/utils/optimistic_response.js | 13 ++ .../vue_shared/components/web_ide_link.vue | 38 ++++- .../projects/pipelines/tests_controller.rb | 4 +- app/controllers/projects_controller.rb | 6 +- app/graphql/types/release_asset_link_type.rb | 12 +- app/graphql/types/release_links_type.rb | 24 +-- app/graphql/types/release_type.rb | 34 ++--- app/graphql/types/repository_type.rb | 22 +-- .../types/root_storage_statistics_type.rb | 16 +- app/graphql/types/task_completion_status.rb | 4 +- app/graphql/types/tree/blob_type.rb | 8 +- app/helpers/tree_helper.rb | 2 + app/helpers/web_ide_button_helper.rb | 4 + .../application_setting_implementation.rb | 2 +- app/models/ci/build.rb | 8 + app/models/ci/pipeline.rb | 2 +- app/models/concerns/ci/has_deployment_name.rb | 15 ++ app/services/issues/export_csv_service.rb | 2 +- .../projects/_project_templates.html.haml | 9 +- app/views/projects/blob/_header.html.haml | 2 +- app/workers/ci/build_finished_worker.rb | 1 + ...ontroller.yml => job_deployment_count.yml} | 8 +- ...users_executing_deployment_job_monthly.yml | 26 ++++ ..._users_executing_deployment_job_weekly.yml | 26 ++++ doc/user/analytics/ci_cd_analytics.md | 64 ++++---- lib/api/entities/issuable_time_stats.rb | 2 +- lib/container_registry/tag.rb | 2 +- lib/gitlab/ci/reports/test_suite_comparer.rb | 2 +- lib/gitlab/diff/file.rb | 4 +- .../known_events/ci_users.yml | 5 + lib/peek/views/detailed_view.rb | 2 +- lib/tasks/gitlab/db.rake | 1 + lib/tasks/gitlab/setup.rake | 12 +- locale/gitlab.pot | 6 + spec/controllers/projects_controller_spec.rb | 10 -- .../components/training_provider_list_spec.js | 26 ++-- .../components/web_ide_link_spec.js | 40 +++++ spec/helpers/tree_helper_spec.rb | 2 + spec/helpers/web_ide_button_helper_spec.rb | 45 ++++++ .../hll_redis_counter_spec.rb | 3 +- .../concerns/ci/has_deployment_name_spec.rb | 34 +++++ .../namespace/root_storage_statistics_spec.rb | 2 +- .../environment_serializer_shared_examples.rb | 17 ++- spec/tasks/gitlab/setup_rake_spec.rb | 141 ++++++++++++++++++ workhorse/internal/upload/body_uploader.go | 27 ---- .../{lfs/lfs.go => upload/lfs_preparer.go} | 15 +- .../lfs_preparer_test.go} | 16 +- .../upload/object_storage_preparer.go | 3 + workhorse/internal/upload/preparer.go | 33 ++++ workhorse/internal/upstream/routes.go | 3 +- 57 files changed, 678 insertions(+), 340 deletions(-) create mode 100644 app/assets/javascripts/security_configuration/graphql/utils/optimistic_response.js create mode 100644 app/models/concerns/ci/has_deployment_name.rb rename config/feature_flags/development/{strong_parameters_for_project_controller.yml => job_deployment_count.yml} (66%) create mode 100644 config/metrics/counts_28d/20220202160126_ci_users_executing_deployment_job_monthly.yml create mode 100644 config/metrics/counts_7d/20220202160120_ci_users_executing_deployment_job_weekly.yml create mode 100644 lib/gitlab/usage_data_counters/known_events/ci_users.yml create mode 100644 spec/helpers/web_ide_button_helper_spec.rb create mode 100644 spec/models/concerns/ci/has_deployment_name_spec.rb create mode 100644 spec/tasks/gitlab/setup_rake_spec.rb rename workhorse/internal/{lfs/lfs.go => upload/lfs_preparer.go} (74%) rename workhorse/internal/{lfs/lfs_test.go => upload/lfs_preparer_test.go} (72%) create mode 100644 workhorse/internal/upload/preparer.go diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ac8afd5c00c..ec23e500143 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -311,23 +311,6 @@ Performance/StringInclude: - 'spec/spec_helper.rb' - 'spec/support_specs/helpers/active_record/query_recorder_spec.rb' -# Offense count: 18 -# Cop supports --auto-correct. -Performance/Sum: - Exclude: - - 'app/controllers/projects/pipelines/tests_controller.rb' - - 'app/models/application_setting_implementation.rb' - - 'app/models/ci/pipeline.rb' - - 'app/services/issues/export_csv_service.rb' - - 'ee/spec/lib/gitlab/elastic/bulk_indexer_spec.rb' - - 'lib/api/entities/issuable_time_stats.rb' - - 'lib/container_registry/tag.rb' - - 'lib/gitlab/ci/reports/test_suite_comparer.rb' - - 'lib/gitlab/diff/file.rb' - - 'lib/gitlab/usage_data.rb' - - 'lib/peek/views/detailed_view.rb' - - 'spec/models/namespace/root_storage_statistics_spec.rb' - # Offense count: 15209 # Configuration parameters: Prefixes. # Prefixes: when, with, without diff --git a/app/assets/javascripts/monitoring/components/charts/bar.vue b/app/assets/javascripts/monitoring/components/charts/bar.vue index a4cef5ea256..1e0f4b10297 100644 --- a/app/assets/javascripts/monitoring/components/charts/bar.vue +++ b/app/assets/javascripts/monitoring/components/charts/bar.vue @@ -1,5 +1,4 @@ diff --git a/app/assets/javascripts/monitoring/components/charts/column.vue b/app/assets/javascripts/monitoring/components/charts/column.vue index 37251af2049..e8f54b1fa34 100644 --- a/app/assets/javascripts/monitoring/components/charts/column.vue +++ b/app/assets/javascripts/monitoring/components/charts/column.vue @@ -1,5 +1,4 @@ diff --git a/app/assets/javascripts/monitoring/components/charts/gauge.vue b/app/assets/javascripts/monitoring/components/charts/gauge.vue index 461ff06be72..0477ff19ffe 100644 --- a/app/assets/javascripts/monitoring/components/charts/gauge.vue +++ b/app/assets/javascripts/monitoring/components/charts/gauge.vue @@ -1,5 +1,4 @@ diff --git a/app/assets/javascripts/monitoring/components/charts/heatmap.vue b/app/assets/javascripts/monitoring/components/charts/heatmap.vue index ed888ef022c..12add274a90 100644 --- a/app/assets/javascripts/monitoring/components/charts/heatmap.vue +++ b/app/assets/javascripts/monitoring/components/charts/heatmap.vue @@ -1,5 +1,4 @@ diff --git a/app/assets/javascripts/monitoring/components/charts/stacked_column.vue b/app/assets/javascripts/monitoring/components/charts/stacked_column.vue index a53f899f752..0cf39448d6b 100644 --- a/app/assets/javascripts/monitoring/components/charts/stacked_column.vue +++ b/app/assets/javascripts/monitoring/components/charts/stacked_column.vue @@ -1,5 +1,4 @@ diff --git a/app/assets/javascripts/security_configuration/components/training_provider_list.vue b/app/assets/javascripts/security_configuration/components/training_provider_list.vue index dd470562f2d..ff7d14ed95a 100644 --- a/app/assets/javascripts/security_configuration/components/training_provider_list.vue +++ b/app/assets/javascripts/security_configuration/components/training_provider_list.vue @@ -10,6 +10,7 @@ import { TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL, } from '~/security_configuration/constants'; import dismissUserCalloutMutation from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql'; +import { updateSecurityTrainingOptimisticResponse } from '~/security_configuration/graphql/utils/optimistic_response'; import securityTrainingProvidersQuery from '../graphql/security_training_providers.query.graphql'; import configureSecurityTrainingProvidersMutation from '../graphql/configure_security_training_providers.mutation.graphql'; @@ -51,7 +52,6 @@ export default { data() { return { errorMessage: '', - providerLoadingId: null, securityTrainingProviders: [], hasTouchedConfiguration: false, }; @@ -99,8 +99,6 @@ export default { this.storeProvider({ ...provider, isEnabled: toggledIsEnabled }); }, async storeProvider({ id, isEnabled, isPrimary }) { - this.providerLoadingId = id; - try { const { data: { @@ -116,6 +114,11 @@ export default { isPrimary, }, }, + optimisticResponse: updateSecurityTrainingOptimisticResponse({ + id, + isEnabled, + isPrimary, + }), }); if (errors.length > 0) { @@ -126,8 +129,6 @@ export default { this.hasTouchedConfiguration = true; } catch { this.errorMessage = this.$options.i18n.configMutationErrorMessage; - } finally { - this.providerLoadingId = null; } }, trackProviderToggle(providerId, providerIsEnabled) { @@ -173,7 +174,6 @@ export default { :value="provider.isEnabled" :label="__('Training mode')" label-position="hidden" - :is-loading="providerLoadingId === provider.id" @change="toggleProvider(provider)" />
diff --git a/app/assets/javascripts/security_configuration/graphql/utils/optimistic_response.js b/app/assets/javascripts/security_configuration/graphql/utils/optimistic_response.js new file mode 100644 index 00000000000..8d1082d081a --- /dev/null +++ b/app/assets/javascripts/security_configuration/graphql/utils/optimistic_response.js @@ -0,0 +1,13 @@ +export const updateSecurityTrainingOptimisticResponse = (changes) => ({ + // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26 + // eslint-disable-next-line @gitlab/require-i18n-strings + __typename: 'Mutation', + securityTrainingUpdate: { + __typename: 'SecurityTrainingUpdatePayload', + training: { + __typename: 'ProjectSecurityTraining', + ...changes, + }, + errors: [], + }, +}); diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue index 82022d1f4d6..1227edbb50b 100644 --- a/app/assets/javascripts/vue_shared/components/web_ide_link.vue +++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue @@ -8,6 +8,7 @@ import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue'; const KEY_EDIT = 'edit'; const KEY_WEB_IDE = 'webide'; const KEY_GITPOD = 'gitpod'; +const KEY_PIPELINE_EDITOR = 'pipeline_editor'; export default { components: { @@ -64,6 +65,11 @@ export default { required: false, default: false, }, + showPipelineEditorButton: { + type: Boolean, + required: false, + default: false, + }, userPreferencesGitpodPath: { type: String, required: false, @@ -79,6 +85,11 @@ export default { required: false, default: '', }, + pipelineEditorUrl: { + type: String, + required: false, + default: '', + }, webIdeUrl: { type: String, required: false, @@ -117,14 +128,19 @@ export default { }, data() { return { - selection: KEY_WEB_IDE, + selection: this.showPipelineEditorButton ? KEY_PIPELINE_EDITOR : KEY_WEB_IDE, showEnableGitpodModal: false, showForkModal: false, }; }, computed: { actions() { - return [this.webIdeAction, this.editAction, this.gitpodAction].filter((action) => action); + return [ + this.pipelineEditorAction, + this.webIdeAction, + this.editAction, + this.gitpodAction, + ].filter((action) => action); }, editAction() { if (!this.showEditButton) { @@ -209,6 +225,24 @@ export default { this.showGitpodButton && this.userPreferencesGitpodPath && this.userProfileEnableGitpodPath ); }, + pipelineEditorAction() { + if (!this.showPipelineEditorButton) { + return null; + } + + const secondaryText = __('Edit, lint, and visualize your pipeline.'); + + return { + key: KEY_PIPELINE_EDITOR, + text: __('Edit in pipeline editor'), + secondaryText, + tooltip: secondaryText, + attrs: { + 'data-qa-selector': 'pipeline_editor_button', + }, + href: this.pipelineEditorUrl, + }; + }, gitpodAction() { if (!this.computedShowGitpodButton) { return null; diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb index 25ec7ab1335..602fc02686a 100644 --- a/app/controllers/projects/pipelines/tests_controller.rb +++ b/app/controllers/projects/pipelines/tests_controller.rb @@ -42,9 +42,9 @@ module Projects end def test_suite - suite = builds.map do |build| + suite = builds.sum do |build| build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new) - end.sum + end Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, project).load! diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 94d65e8cf49..7b45bed426b 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -345,11 +345,7 @@ class ProjectsController < Projects::ApplicationController private def refs_params - if Feature.enabled?(:strong_parameters_for_project_controller, @project, default_enabled: :yaml) - params.permit(:search, :sort, :ref, find: []) - else - params - end + params.permit(:search, :sort, :ref, find: []) end # Render project landing depending of which features are available diff --git a/app/graphql/types/release_asset_link_type.rb b/app/graphql/types/release_asset_link_type.rb index 02961f2f73f..33dcb5125e3 100644 --- a/app/graphql/types/release_asset_link_type.rb +++ b/app/graphql/types/release_asset_link_type.rb @@ -7,21 +7,21 @@ module Types authorize :read_release + field :external, GraphQL::Types::Boolean, null: true, method: :external?, + description: 'Indicates the link points to an external resource.' field :id, GraphQL::Types::ID, null: false, description: 'ID of the link.' + field :link_type, Types::ReleaseAssetLinkTypeEnum, null: true, + description: 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`.' field :name, GraphQL::Types::String, null: true, description: 'Name of the link.' field :url, GraphQL::Types::String, null: true, description: 'URL of the link.' - field :link_type, Types::ReleaseAssetLinkTypeEnum, null: true, - description: 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`.' - field :external, GraphQL::Types::Boolean, null: true, method: :external?, - description: 'Indicates the link points to an external resource.' - field :direct_asset_url, GraphQL::Types::String, null: true, - description: 'Direct asset URL of the link.' field :direct_asset_path, GraphQL::Types::String, null: true, method: :filepath, description: 'Relative path for the direct asset link.' + field :direct_asset_url, GraphQL::Types::String, null: true, + description: 'Direct asset URL of the link.' def direct_asset_url return object.url unless object.filepath diff --git a/app/graphql/types/release_links_type.rb b/app/graphql/types/release_links_type.rb index 37ad52ce6d0..b7a1a5a9dbe 100644 --- a/app/graphql/types/release_links_type.rb +++ b/app/graphql/types/release_links_type.rb @@ -10,25 +10,25 @@ module Types present_using ReleasePresenter - field :self_url, GraphQL::Types::String, null: true, - description: 'HTTP URL of the release.' - field :edit_url, GraphQL::Types::String, null: true, - description: "HTTP URL of the release's edit page.", - authorize: :update_release - field :opened_merge_requests_url, GraphQL::Types::String, null: true, - description: 'HTTP URL of the merge request page, filtered by this release and `state=open`.', - authorize: :download_code - field :merged_merge_requests_url, GraphQL::Types::String, null: true, - description: 'HTTP URL of the merge request page , filtered by this release and `state=merged`.', + field :closed_issues_url, GraphQL::Types::String, null: true, + description: 'HTTP URL of the issues page, filtered by this release and `state=closed`.', authorize: :download_code field :closed_merge_requests_url, GraphQL::Types::String, null: true, description: 'HTTP URL of the merge request page , filtered by this release and `state=closed`.', authorize: :download_code + field :edit_url, GraphQL::Types::String, null: true, + description: "HTTP URL of the release's edit page.", + authorize: :update_release + field :merged_merge_requests_url, GraphQL::Types::String, null: true, + description: 'HTTP URL of the merge request page , filtered by this release and `state=merged`.', + authorize: :download_code field :opened_issues_url, GraphQL::Types::String, null: true, description: 'HTTP URL of the issues page, filtered by this release and `state=open`.', authorize: :download_code - field :closed_issues_url, GraphQL::Types::String, null: true, - description: 'HTTP URL of the issues page, filtered by this release and `state=closed`.', + field :opened_merge_requests_url, GraphQL::Types::String, null: true, + description: 'HTTP URL of the merge request page, filtered by this release and `state=open`.', authorize: :download_code + field :self_url, GraphQL::Types::String, null: true, + description: 'HTTP URL of the release.' end end diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb index fbc3779ea9b..95b6b43bb46 100644 --- a/app/graphql/types/release_type.rb +++ b/app/graphql/types/release_type.rb @@ -13,30 +13,30 @@ module Types present_using ReleasePresenter - field :tag_name, GraphQL::Types::String, null: true, method: :tag, - description: 'Name of the tag associated with the release.' - field :tag_path, GraphQL::Types::String, null: true, - description: 'Relative web path to the tag associated with the release.', - authorize: :download_code - field :description, GraphQL::Types::String, null: true, - description: 'Description (also known as "release notes") of the release.' - field :name, GraphQL::Types::String, null: true, - description: 'Name of the release.' - field :created_at, Types::TimeType, null: true, - description: 'Timestamp of when the release was created.' - field :released_at, Types::TimeType, null: true, - description: 'Timestamp of when the release was released.' - field :upcoming_release, GraphQL::Types::Boolean, null: true, method: :upcoming_release?, - description: 'Indicates the release is an upcoming release.' field :assets, Types::ReleaseAssetsType, null: true, method: :itself, description: 'Assets of the release.' + field :created_at, Types::TimeType, null: true, + description: 'Timestamp of when the release was created.' + field :description, GraphQL::Types::String, null: true, + description: 'Description (also known as "release notes") of the release.' + field :evidences, Types::EvidenceType.connection_type, null: true, + description: 'Evidence for the release.' field :links, Types::ReleaseLinksType, null: true, method: :itself, description: 'Links of the release.' field :milestones, Types::MilestoneType.connection_type, null: true, description: 'Milestones associated to the release.', resolver: ::Resolvers::ReleaseMilestonesResolver - field :evidences, Types::EvidenceType.connection_type, null: true, - description: 'Evidence for the release.' + field :name, GraphQL::Types::String, null: true, + description: 'Name of the release.' + field :released_at, Types::TimeType, null: true, + description: 'Timestamp of when the release was released.' + field :tag_name, GraphQL::Types::String, null: true, method: :tag, + description: 'Name of the tag associated with the release.' + field :tag_path, GraphQL::Types::String, null: true, + description: 'Relative web path to the tag associated with the release.', + authorize: :download_code + field :upcoming_release, GraphQL::Types::Boolean, null: true, method: :upcoming_release?, + description: 'Indicates the release is an upcoming release.' field :author, Types::UserType, null: true, description: 'User that created the release.' diff --git a/app/graphql/types/repository_type.rb b/app/graphql/types/repository_type.rb index fc9860900c9..aa02f0058da 100644 --- a/app/graphql/types/repository_type.rb +++ b/app/graphql/types/repository_type.rb @@ -6,17 +6,6 @@ module Types authorize :download_code - field :root_ref, GraphQL::Types::String, null: true, calls_gitaly: true, - description: 'Default branch of the repository.' - field :empty, GraphQL::Types::Boolean, null: false, method: :empty?, calls_gitaly: true, - description: 'Indicates repository has no visible content.' - field :exists, GraphQL::Types::Boolean, null: false, method: :exists?, calls_gitaly: true, - description: 'Indicates a corresponding Git repository exists on disk.' - field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver, calls_gitaly: true, - description: 'Tree of the repository.' - field :paginated_tree, Types::Tree::TreeType.connection_type, null: true, resolver: Resolvers::PaginatedTreeResolver, calls_gitaly: true, - max_page_size: 100, - description: 'Paginated tree of the repository.' field :blobs, Types::Repository::BlobType.connection_type, null: true, resolver: Resolvers::BlobsResolver, calls_gitaly: true, description: 'Blobs contained within the repository' field :branch_names, [GraphQL::Types::String], null: true, calls_gitaly: true, @@ -26,5 +15,16 @@ module Types description: 'Shows a disk path of the repository.', null: true, authorize: :read_storage_disk_path + field :empty, GraphQL::Types::Boolean, null: false, method: :empty?, calls_gitaly: true, + description: 'Indicates repository has no visible content.' + field :exists, GraphQL::Types::Boolean, null: false, method: :exists?, calls_gitaly: true, + description: 'Indicates a corresponding Git repository exists on disk.' + field :paginated_tree, Types::Tree::TreeType.connection_type, null: true, resolver: Resolvers::PaginatedTreeResolver, calls_gitaly: true, + max_page_size: 100, + description: 'Paginated tree of the repository.' + field :root_ref, GraphQL::Types::String, null: true, calls_gitaly: true, + description: 'Default branch of the repository.' + field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver, calls_gitaly: true, + description: 'Tree of the repository.' end end diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb index 4dcadf1274f..467331c5643 100644 --- a/app/graphql/types/root_storage_statistics_type.rb +++ b/app/graphql/types/root_storage_statistics_type.rb @@ -6,15 +6,15 @@ module Types authorize :read_statistics - field :storage_size, GraphQL::Types::Float, null: false, description: 'Total storage in bytes.' - field :repository_size, GraphQL::Types::Float, null: false, description: 'Git repository size in bytes.' - field :lfs_objects_size, GraphQL::Types::Float, null: false, description: 'LFS objects size in bytes.' field :build_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI artifacts size in bytes.' - field :packages_size, GraphQL::Types::Float, null: false, description: 'Packages size in bytes.' - field :wiki_size, GraphQL::Types::Float, null: false, description: 'Wiki size in bytes.' - field :snippets_size, GraphQL::Types::Float, null: false, description: 'Snippets size in bytes.' - field :pipeline_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI pipeline artifacts size in bytes.' - field :uploads_size, GraphQL::Types::Float, null: false, description: 'Uploads size in bytes.' field :dependency_proxy_size, GraphQL::Types::Float, null: false, description: 'Dependency Proxy sizes in bytes.' + field :lfs_objects_size, GraphQL::Types::Float, null: false, description: 'LFS objects size in bytes.' + field :packages_size, GraphQL::Types::Float, null: false, description: 'Packages size in bytes.' + field :pipeline_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI pipeline artifacts size in bytes.' + field :repository_size, GraphQL::Types::Float, null: false, description: 'Git repository size in bytes.' + field :snippets_size, GraphQL::Types::Float, null: false, description: 'Snippets size in bytes.' + field :storage_size, GraphQL::Types::Float, null: false, description: 'Total storage in bytes.' + field :uploads_size, GraphQL::Types::Float, null: false, description: 'Uploads size in bytes.' + field :wiki_size, GraphQL::Types::Float, null: false, description: 'Wiki size in bytes.' end end diff --git a/app/graphql/types/task_completion_status.rb b/app/graphql/types/task_completion_status.rb index 3aa19ff9413..9a979b04d37 100644 --- a/app/graphql/types/task_completion_status.rb +++ b/app/graphql/types/task_completion_status.rb @@ -8,10 +8,10 @@ module Types graphql_name 'TaskCompletionStatus' description 'Completion status of tasks' - field :count, GraphQL::Types::Int, null: false, - description: 'Number of total tasks.' field :completed_count, GraphQL::Types::Int, null: false, description: 'Number of completed tasks.' + field :count, GraphQL::Types::Int, null: false, + description: 'Number of total tasks.' end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb index bcff65be652..284542e1d2a 100644 --- a/app/graphql/types/tree/blob_type.rb +++ b/app/graphql/types/tree/blob_type.rb @@ -9,15 +9,15 @@ module Types implements Types::Tree::EntryType present_using BlobPresenter - field :web_url, GraphQL::Types::String, null: true, - description: 'Web URL of the blob.' - field :web_path, GraphQL::Types::String, null: true, - description: 'Web path of the blob.' field :lfs_oid, GraphQL::Types::String, null: true, calls_gitaly: true, description: 'LFS ID of the blob.' field :mode, GraphQL::Types::String, null: true, description: 'Blob mode in numeric format.' + field :web_path, GraphQL::Types::String, null: true, + description: 'Web path of the blob.' + field :web_url, GraphQL::Types::String, null: true, + description: 'Web URL of the blob.' def lfs_oid Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(object.repository, object.id).find diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 23a9601aed7..2fef4ae98a9 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -203,9 +203,11 @@ module TreeHelper show_edit_button: show_edit_button?(options), show_web_ide_button: show_web_ide_button?, show_gitpod_button: show_gitpod_button?, + show_pipeline_editor_button: show_pipeline_editor_button?(@project, @path), web_ide_url: web_ide_url, edit_url: edit_url(options), + pipeline_editor_url: project_ci_pipeline_editor_path(@project, branch_name: @ref), gitpod_url: gitpod_url, user_preferences_gitpod_path: profile_preferences_path(anchor: 'user_gitpod_enabled'), diff --git a/app/helpers/web_ide_button_helper.rb b/app/helpers/web_ide_button_helper.rb index 6c73d365e8e..9ec22a659d3 100644 --- a/app/helpers/web_ide_button_helper.rb +++ b/app/helpers/web_ide_button_helper.rb @@ -29,6 +29,10 @@ module WebIdeButtonHelper show_web_ide_button? && Gitlab::CurrentSettings.gitpod_enabled end + def show_pipeline_editor_button?(project, path) + can_view_pipeline_editor?(project) && path == project.ci_config_path_or_default + end + def can_push_code? current_user&.can?(:push_code, @project) end diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 52c83db3078..2c56deef524 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -403,7 +403,7 @@ module ApplicationSettingImplementation def normalized_repository_storage_weights strong_memoize(:normalized_repository_storage_weights) do repository_storages_weights = repository_storages_weighted.slice(*Gitlab.config.repositories.storages.keys) - weights_total = repository_storages_weights.values.reduce(:+) + weights_total = repository_storages_weights.values.sum repository_storages_weights.transform_values do |w| next w if weights_total == 0 diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index c4d1a2c740b..ae43053ff16 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -10,6 +10,8 @@ module Ci include Presentable include Importable include Ci::HasRef + include HasDeploymentName + extend ::Gitlab::Utils::Override BuildArchivedError = Class.new(StandardError) @@ -35,6 +37,8 @@ module Ci DEGRADATION_THRESHOLD_VARIABLE_NAME = 'DEGRADATION_THRESHOLD' RUNNERS_STATUS_CACHE_EXPIRATION = 1.minute + DEPLOYMENT_NAMES = %w[deploy release rollout].freeze + has_one :deployment, as: :deployable, class_name: 'Deployment' has_one :pending_state, class_name: 'Ci::BuildPendingState', inverse_of: :build has_one :queuing_entry, class_name: 'Ci::PendingBuild', foreign_key: :build_id @@ -1123,6 +1127,10 @@ module Ci .include?(exit_code) end + def track_deployment_usage + Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id) if user_id.present? && count_user_deployment? + end + protected def run_status_commit_hooks! diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 20e92bddf45..752d42cec17 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -653,7 +653,7 @@ module Ci def coverage coverage_array = latest_statuses.map(&:coverage).compact if coverage_array.size >= 1 - coverage_array.reduce(:+) / coverage_array.size + coverage_array.sum / coverage_array.size end end diff --git a/app/models/concerns/ci/has_deployment_name.rb b/app/models/concerns/ci/has_deployment_name.rb new file mode 100644 index 00000000000..fe288134872 --- /dev/null +++ b/app/models/concerns/ci/has_deployment_name.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Ci + module HasDeploymentName + extend ActiveSupport::Concern + + def count_user_deployment? + Feature.enabled?(:job_deployment_count) && deployment_name? + end + + def deployment_name? + self.class::DEPLOYMENT_NAMES.any? { |n| name.downcase.include?(n) } + end + end +end diff --git a/app/services/issues/export_csv_service.rb b/app/services/issues/export_csv_service.rb index 3809d8bc347..a30644e2105 100644 --- a/app/services/issues/export_csv_service.rb +++ b/app/services/issues/export_csv_service.rb @@ -52,7 +52,7 @@ module Issues # rubocop: disable CodeReuse/ActiveRecord def issue_time_spent(issue) - issue.timelogs.map(&:time_spent).sum + issue.timelogs.sum(&:time_spent) end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml index 68489fba06c..d00ed2afa3c 100644 --- a/app/views/projects/_project_templates.html.haml +++ b/app/views/projects/_project_templates.html.haml @@ -1,11 +1,10 @@ - f ||= local_assigns[:f] .project-templates-buttons - %ul.nav-tabs.nav-links.nav.scrolling-tabs - %li.built-in-tab - %a.nav-link.active{ href: "#built-in", data: { toggle: 'tab'} } - = _('Built-in') - = gl_tab_counter_badge Gitlab::ProjectTemplate.all.count + Gitlab::SampleDataTemplate.all.count + = gl_tabs_nav({ class: 'nav-links scrolling-tabs gl-display-flex gl-flex-grow-1 gl-flex-nowrap gl-border-0' }) do + = gl_tab_link_to '#built-in', tab_class: 'built-in-tab', class: 'active', data: { toggle: 'tab' } do + = _('Built-in') + = gl_tab_counter_badge Gitlab::ProjectTemplate.all.count + Gitlab::SampleDataTemplate.all.count .tab-content .project-templates-buttons.import-buttons.tab-pane.active#built-in diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index 9362f8418be..51cb333f8f2 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -6,7 +6,7 @@ .file-actions.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-md-justify-content-end< = render 'projects/blob/viewer_switcher', blob: blob unless blame = render 'shared/web_ide_button', blob: blob - - if can_view_pipeline_editor?(@project) && @path == @project.ci_config_path_or_default + - if show_pipeline_editor_button?(@project, @path) = link_to "Pipeline Editor", project_ci_pipeline_editor_path(@project, branch_name: @ref), class: "btn gl-button btn-confirm-secondary gl-ml-3" .btn-group{ role: "group", class: ("gl-ml-3" if current_user) }> = render_if_exists 'projects/blob/header_file_locks_link' diff --git a/app/workers/ci/build_finished_worker.rb b/app/workers/ci/build_finished_worker.rb index 56cfaa7e674..70c234bd4c7 100644 --- a/app/workers/ci/build_finished_worker.rb +++ b/app/workers/ci/build_finished_worker.rb @@ -39,6 +39,7 @@ module Ci # We execute these async as these are independent operations. BuildHooksWorker.perform_async(build.id) ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat? + build.track_deployment_usage if build.failed? && !build.auto_retry_expected? ::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id) diff --git a/config/feature_flags/development/strong_parameters_for_project_controller.yml b/config/feature_flags/development/job_deployment_count.yml similarity index 66% rename from config/feature_flags/development/strong_parameters_for_project_controller.yml rename to config/feature_flags/development/job_deployment_count.yml index 458bfc4c485..9a101da9a99 100644 --- a/config/feature_flags/development/strong_parameters_for_project_controller.yml +++ b/config/feature_flags/development/job_deployment_count.yml @@ -1,8 +1,8 @@ --- -name: strong_parameters_for_project_controller -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79956 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352251 +name: job_deployment_count +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79272 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351591 milestone: '14.8' type: development -group: group::source code +group: group::release default_enabled: false diff --git a/config/metrics/counts_28d/20220202160126_ci_users_executing_deployment_job_monthly.yml b/config/metrics/counts_28d/20220202160126_ci_users_executing_deployment_job_monthly.yml new file mode 100644 index 00000000000..d33dd895022 --- /dev/null +++ b/config/metrics/counts_28d/20220202160126_ci_users_executing_deployment_job_monthly.yml @@ -0,0 +1,26 @@ +--- +key_path: redis_hll_counters.ci_users.ci_users_executing_deployment_job_monthly +description: Monthly counts of times users have executed deployment jobs +product_section: ops +product_stage: release +product_group: group::release +product_category: continuous_delivery +value_type: number +status: active +milestone: "14.8" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79272 +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +performance_indicator_type: [] +options: + events: + - ci_users_executing_deployment_job +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate diff --git a/config/metrics/counts_7d/20220202160120_ci_users_executing_deployment_job_weekly.yml b/config/metrics/counts_7d/20220202160120_ci_users_executing_deployment_job_weekly.yml new file mode 100644 index 00000000000..ca5f67e0ac5 --- /dev/null +++ b/config/metrics/counts_7d/20220202160120_ci_users_executing_deployment_job_weekly.yml @@ -0,0 +1,26 @@ +--- +key_path: redis_hll_counters.ci_users.ci_users_executing_deployment_job_weekly +description: Weekly counts of times users have executed deployment jobs +product_section: ops +product_stage: release +product_group: group::release +product_category: continuous_delivery +value_type: number +status: active +milestone: "14.8" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79272 +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +performance_indicator_type: [] +options: + events: + - ci_users_executing_deployment_job +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate diff --git a/doc/user/analytics/ci_cd_analytics.md b/doc/user/analytics/ci_cd_analytics.md index 1bc0c2b8fb0..8e231d18f41 100644 --- a/doc/user/analytics/ci_cd_analytics.md +++ b/doc/user/analytics/ci_cd_analytics.md @@ -4,13 +4,13 @@ group: Release info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# CI/CD Analytics **(FREE)** +# CI/CD analytics **(FREE)** ## Pipeline success and duration charts > [Renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/38318) to CI/CD Analytics in GitLab 12.8. -CI/CD Analytics shows the history of your pipeline successes and failures, as well as how long each pipeline +CI/CD analytics shows the history of your pipeline successes and failures, as well as how long each pipeline ran. View successful pipelines: @@ -39,21 +39,14 @@ To view CI/CD analytics: > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/275991) in GitLab 13.7. > - [Added support](https://gitlab.com/gitlab-org/gitlab/-/issues/291746) for lead time for changes in GitLab 13.10. -Customer experience is a key metric. Users want to measure platform stability and other -post-deployment performance KPIs, and set targets for customer behavior, experience, and financial -impact. Tracking and measuring these indicators solves an important pain point. Similarly, creating -views that manage products, not projects or repositories, provides users with a more relevant data set. -Since GitLab is a tool for the entire DevOps life-cycle, information from different workflows is -integrated and can be used to measure the success of the teams. - The DevOps Research and Assessment ([DORA](https://cloud.google.com/blog/products/devops-sre/the-2019-accelerate-state-of-devops-elite-performance-productivity-and-scaling)) -team developed four key metrics that the industry has widely adopted. You can use these metrics as -performance indicators for software development teams: +team developed several key metrics that you can use as performance indicators for software development +teams: - Deployment frequency: How often an organization successfully releases to production. - Lead time for changes: The amount of time it takes for code to reach production. - Change failure rate: The percentage of deployments that cause a failure in production. -- Time to restore service: How long it takes an organization to recover from a failure in +- Time to restore service: How long it takes for an organization to recover from a failure in production. ### Supported metrics in GitLab @@ -62,39 +55,48 @@ The following table shows the supported metrics, at which level they are support | Metric | Level | API version | Chart (UI) version | Comments | |---------------------------|---------------------|--------------------------------------|---------------------------------------|-----------| -| `deployment_frequency` | Project-level | [13.7+](../../api/dora/metrics.md) | [13.8+](#deployment-frequency-charts) | The [old API endpoint](../../api/dora4_project_analytics.md) was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/323713) in 13.10. | -| `deployment_frequency` | Group-level | [13.10+](../../api/dora/metrics.md) | [13.12+](#deployment-frequency-charts) | | -| `lead_time_for_changes` | Project-level | [13.10+](../../api/dora/metrics.md) | [13.11+](#lead-time-charts) | Unit in seconds. Aggregation method is median. | -| `lead_time_for_changes` | Group-level | [13.10+](../../api/dora/metrics.md) | [14.0+](#lead-time-charts) | Unit in seconds. Aggregation method is median. | +| `deployment_frequency` | Project-level | [13.7+](../../api/dora/metrics.md) | [13.8+](#view-deployment-frequency-chart) | The [old API endpoint](../../api/dora4_project_analytics.md) was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/323713) in 13.10. | +| `deployment_frequency` | Group-level | [13.10+](../../api/dora/metrics.md) | [13.12+](#view-deployment-frequency-chart) | | +| `lead_time_for_changes` | Project-level | [13.10+](../../api/dora/metrics.md) | [13.11+](#view-lead-time-for-changes-chart) | Unit in seconds. Aggregation method is median. | +| `lead_time_for_changes` | Group-level | [13.10+](../../api/dora/metrics.md) | [14.0+](#view-lead-time-for-changes-chart) | Unit in seconds. Aggregation method is median. | | `change_failure_rate` | Project/Group-level | To be supported | To be supported | | | `time_to_restore_service` | Project/Group-level | To be supported | To be supported | | -### Deployment frequency charts +## View deployment frequency chart **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/275991) in GitLab 13.8. -The **Analytics > CI/CD Analytics** page shows information about the deployment +The deployment frequency charts show information about the deployment frequency to the `production` environment. The environment must be part of the [production deployment tier](../../ci/environments/index.md#deployment-tier-of-environments) for its deployment information to appear on the graphs. +The deployment frequency chart is available for groups and projects. + +To view the deployment frequency chart: + +1. On the top bar, select **Menu > Projects** and find your project. +1. On the left sidebar, select **Analytics > CI/CD Analytics**. +1. Select the **Deployment frequency** tab. + ![Deployment frequency](img/deployment_frequency_charts_v13_12.png) -These charts are available for both groups and projects. - -### Lead time charts +## View lead time for changes chart **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250329) in GitLab 13.11. -The charts in the **Lead Time** tab show information about how long it takes -merge requests to be deployed to a production environment. +The lead time for changes chart shows information about how long it takes for +merge requests to be deployed to a production environment. This chart is available for groups and projects. + +- Small lead times indicate fast, efficient deployment + processes. +- For time periods in which no merge requests were deployed, the charts render a + red, dashed line. + +To view the lead time for changes chart: + +1. On the top bar, select **Menu > Projects** and find your project. +1. On the left sidebar, select **Analytics > CI/CD Analytics**. +1. Select the **Lead time** tab. ![Lead time](img/lead_time_chart_v13_11.png) - -Smaller values are better. Small lead times indicate fast, efficient deployment -processes. - -For time periods in which no merge requests were deployed, the charts render a -red, dashed line. - -These charts are available for both groups and projects. diff --git a/lib/api/entities/issuable_time_stats.rb b/lib/api/entities/issuable_time_stats.rb index 7c3452a10a1..f93b4651b1f 100644 --- a/lib/api/entities/issuable_time_stats.rb +++ b/lib/api/entities/issuable_time_stats.rb @@ -18,7 +18,7 @@ module API # rubocop: disable CodeReuse/ActiveRecord def total_time_spent # Avoids an N+1 query since timelogs are preloaded - object.timelogs.map(&:time_spent).sum + object.timelogs.sum(&:time_spent) end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb index 2a32f950457..04a8e1d2e8f 100644 --- a/lib/container_registry/tag.rb +++ b/lib/container_registry/tag.rb @@ -104,7 +104,7 @@ module ContainerRegistry def total_size return unless layers - layers.map(&:size).sum if v2? + layers.sum(&:size) if v2? end # rubocop: enable CodeReuse/ActiveRecord diff --git a/lib/gitlab/ci/reports/test_suite_comparer.rb b/lib/gitlab/ci/reports/test_suite_comparer.rb index 287a03cefe2..7fa744d047c 100644 --- a/lib/gitlab/ci/reports/test_suite_comparer.rb +++ b/lib/gitlab/ci/reports/test_suite_comparer.rb @@ -106,7 +106,7 @@ module Gitlab private def max_tests(*used) - [DEFAULT_MAX_TESTS - used.map(&:count).sum, DEFAULT_MIN_TESTS].max + [DEFAULT_MAX_TESTS - used.sum(&:count), DEFAULT_MIN_TESTS].max end end end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index 6fc6c8bb36c..39250542260 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -296,13 +296,13 @@ module Gitlab # rubocop: disable CodeReuse/ActiveRecord def size - valid_blobs.map(&:size).sum + valid_blobs.sum(&:size) end # rubocop: enable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord def raw_size - valid_blobs.map(&:raw_size).sum + valid_blobs.sum(&:raw_size) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/lib/gitlab/usage_data_counters/known_events/ci_users.yml b/lib/gitlab/usage_data_counters/known_events/ci_users.yml new file mode 100644 index 00000000000..63498a35858 --- /dev/null +++ b/lib/gitlab/usage_data_counters/known_events/ci_users.yml @@ -0,0 +1,5 @@ +- name: ci_users_executing_deployment_job + category: ci_users + redis_slot: ci_users + aggregation: weekly + feature_flag: job_deployment_count diff --git a/lib/peek/views/detailed_view.rb b/lib/peek/views/detailed_view.rb index 4cc2e85c7bb..1301c6aa6fd 100644 --- a/lib/peek/views/detailed_view.rb +++ b/lib/peek/views/detailed_view.rb @@ -23,7 +23,7 @@ module Peek private def duration - detail_store.map { |entry| entry[:duration] }.sum * 1000 + detail_store.sum { |entry| entry[:duration] } * 1000 end def calls diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index 5d4eca9a964..c47528fc4ee 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -192,6 +192,7 @@ namespace :gitlab do end Rake::Task['db:test:purge'].enhance(['gitlab:db:clear_all_connections']) + Rake::Task['db:drop'].enhance(['gitlab:db:clear_all_connections']) # During testing, db:test:load restores the database schema from scratch # which does not include dynamic partitions. We cannot rely on application diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 705519d1741..a5289476378 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -47,13 +47,15 @@ namespace :gitlab do # will work. def self.terminate_all_connections cmd = <<~SQL - SELECT pg_terminate_backend(pg_stat_activity.pid) - FROM pg_stat_activity - WHERE datname = current_database() + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE datname = current_database() AND pid <> pg_backend_pid(); SQL - ActiveRecord::Base.connection.execute(cmd)&.result_status == PG::PGRES_TUPLES_OK - rescue ActiveRecord::NoDatabaseError + Gitlab::Database::EachDatabase.each_database_connection do |connection| + connection.execute(cmd) + rescue ActiveRecord::NoDatabaseError + end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fd478ca870e..02d5965721c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -13214,6 +13214,9 @@ msgstr "" msgid "Edit in Web IDE" msgstr "" +msgid "Edit in pipeline editor" +msgstr "" + msgid "Edit in single-file editor" msgstr "" @@ -13256,6 +13259,9 @@ msgstr "" msgid "Edit your most recent comment in a thread (from an empty textarea)" msgstr "" +msgid "Edit, lint, and visualize your pipeline." +msgstr "" + msgid "Edited" msgstr "" diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 08d1d88fcda..f21e00b5522 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -1211,16 +1211,6 @@ RSpec.describe ProjectsController do expect(response).to have_gitlab_http_status(:success) end - - context 'when "strong_parameters_for_project_controller" FF is disabled' do - before do - stub_feature_flags(strong_parameters_for_project_controller: false) - end - - it 'raises an exception' do - expect { request }.to raise_error(TypeError) - end - end end end diff --git a/spec/frontend/security_configuration/components/training_provider_list_spec.js b/spec/frontend/security_configuration/components/training_provider_list_spec.js index 4d92f0cca22..688986af25b 100644 --- a/spec/frontend/security_configuration/components/training_provider_list_spec.js +++ b/spec/frontend/security_configuration/components/training_provider_list_spec.js @@ -12,6 +12,7 @@ import { TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL, } from '~/security_configuration/constants'; import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue'; +import { updateSecurityTrainingOptimisticResponse } from '~/security_configuration/graphql/utils/optimistic_response'; import securityTrainingProvidersQuery from '~/security_configuration/graphql/security_training_providers.query.graphql'; import configureSecurityTrainingProvidersMutation from '~/security_configuration/graphql/configure_security_training_providers.mutation.graphql'; import dismissUserCalloutMutation from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql'; @@ -159,17 +160,6 @@ describe('TrainingProviderList component', () => { await toggleFirstProvider(); }); - it.each` - loading | wait | desc - ${true} | ${false} | ${'enables loading of GlToggle when mutation is called'} - ${false} | ${true} | ${'disables loading of GlToggle when mutation is complete'} - `('$desc', async ({ loading, wait }) => { - if (wait) { - await waitForMutationToBeLoaded(); - } - expect(findFirstToggle().props('isLoading')).toBe(loading); - }); - it('calls mutation when toggle is changed', () => { expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith( expect.objectContaining({ @@ -186,6 +176,20 @@ describe('TrainingProviderList component', () => { ); }); + it('returns an optimistic response when calling the mutation', () => { + const optimisticResponse = updateSecurityTrainingOptimisticResponse({ + id: securityTrainingProviders[0].id, + isEnabled: true, + isPrimary: false, + }); + + expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith( + expect.objectContaining({ + optimisticResponse, + }), + ); + }); + it('dismisses the callout when the feature gets first enabled', async () => { // wait for configuration update mutation to complete await waitForMutationToBeLoaded(); diff --git a/spec/frontend/vue_shared/components/web_ide_link_spec.js b/spec/frontend/vue_shared/components/web_ide_link_spec.js index 5589cbfd08f..e79935f8fa6 100644 --- a/spec/frontend/vue_shared/components/web_ide_link_spec.js +++ b/spec/frontend/vue_shared/components/web_ide_link_spec.js @@ -12,6 +12,7 @@ import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_help const TEST_EDIT_URL = '/gitlab-test/test/-/edit/main/'; const TEST_WEB_IDE_URL = '/-/ide/project/gitlab-test/test/edit/main/-/'; const TEST_GITPOD_URL = 'https://gitpod.test/'; +const TEST_PIPELINE_EDITOR_URL = '/-/ci/editor?branch_name="main"'; const TEST_USER_PREFERENCES_GITPOD_PATH = '/-/profile/preferences#user_gitpod_enabled'; const TEST_USER_PROFILE_ENABLE_GITPOD_PATH = '/-/profile?user%5Bgitpod_enabled%5D=true'; const forkPath = '/some/fork/path'; @@ -66,6 +67,16 @@ const ACTION_GITPOD_ENABLE = { href: undefined, handle: expect.any(Function), }; +const ACTION_PIPELINE_EDITOR = { + href: TEST_PIPELINE_EDITOR_URL, + key: 'pipeline_editor', + secondaryText: 'Edit, lint, and visualize your pipeline.', + tooltip: 'Edit, lint, and visualize your pipeline.', + text: 'Edit in pipeline editor', + attrs: { + 'data-qa-selector': 'pipeline_editor_button', + }, +}; describe('Web IDE link component', () => { let wrapper; @@ -76,6 +87,7 @@ describe('Web IDE link component', () => { editUrl: TEST_EDIT_URL, webIdeUrl: TEST_WEB_IDE_URL, gitpodUrl: TEST_GITPOD_URL, + pipelineEditorUrl: TEST_PIPELINE_EDITOR_URL, forkPath, ...props, }, @@ -106,6 +118,10 @@ describe('Web IDE link component', () => { props: {}, expectedActions: [ACTION_WEB_IDE, ACTION_EDIT], }, + { + props: { showPipelineEditorButton: true }, + expectedActions: [ACTION_PIPELINE_EDITOR, ACTION_WEB_IDE, ACTION_EDIT], + }, { props: { webIdeText: 'Test Web IDE' }, expectedActions: [{ ...ACTION_WEB_IDE_EDIT_FORK, text: 'Test Web IDE' }, ACTION_EDIT], @@ -193,12 +209,34 @@ describe('Web IDE link component', () => { expect(findActionsButton().props('actions')).toEqual(expectedActions); }); + describe('when pipeline editor action is available', () => { + beforeEach(() => { + createComponent({ + showEditButton: false, + showWebIdeButton: true, + showGitpodButton: true, + showPipelineEditorButton: true, + userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH, + userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH, + gitpodEnabled: true, + }); + }); + + it('selected Pipeline Editor by default', () => { + expect(findActionsButton().props()).toMatchObject({ + actions: [ACTION_PIPELINE_EDITOR, ACTION_WEB_IDE, ACTION_GITPOD], + selectedKey: ACTION_PIPELINE_EDITOR.key, + }); + }); + }); + describe('with multiple actions', () => { beforeEach(() => { createComponent({ showEditButton: false, showWebIdeButton: true, showGitpodButton: true, + showPipelineEditorButton: false, userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH, userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH, gitpodEnabled: true, @@ -240,6 +278,7 @@ describe('Web IDE link component', () => { props: { showWebIdeButton: true, showEditButton: false, + showPipelineEditorButton: false, forkPath, forkModalId: 'edit-modal', }, @@ -249,6 +288,7 @@ describe('Web IDE link component', () => { props: { showWebIdeButton: false, showEditButton: true, + showPipelineEditorButton: false, forkPath, forkModalId: 'webide-modal', }, diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb index 1a0ecd5d903..026432adf99 100644 --- a/spec/helpers/tree_helper_spec.rb +++ b/spec/helpers/tree_helper_spec.rb @@ -116,9 +116,11 @@ RSpec.describe TreeHelper do show_edit_button: false, show_web_ide_button: true, show_gitpod_button: false, + show_pipeline_editor_button: false, edit_url: '', web_ide_url: "/-/ide/project/#{project.full_path}/edit/#{sha}", + pipeline_editor_url: "/#{project.full_path}/-/ci/editor?branch_name=#{@ref}", gitpod_url: '', user_preferences_gitpod_path: user_preferences_gitpod_path, diff --git a/spec/helpers/web_ide_button_helper_spec.rb b/spec/helpers/web_ide_button_helper_spec.rb new file mode 100644 index 00000000000..3dd46021c11 --- /dev/null +++ b/spec/helpers/web_ide_button_helper_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WebIdeButtonHelper do + describe '#show_pipeline_editor_button?' do + subject(:result) { helper.show_pipeline_editor_button?(project, path) } + + let_it_be(:project) { build(:project) } + + context 'when can view pipeline editor' do + before do + allow(helper).to receive(:can_view_pipeline_editor?).and_return(true) + end + + context 'when path is ci config path' do + let(:path) { project.ci_config_path_or_default } + + it 'returns true' do + expect(result).to eq(true) + end + end + + context 'when path is not config path' do + let(:path) { '/' } + + it 'returns false' do + expect(result).to eq(false) + end + end + end + + context 'when can not view pipeline editor' do + before do + allow(helper).to receive(:can_view_pipeline_editor?).and_return(false) + end + + let(:path) { project.ci_config_path_or_default } + + it 'returns false' do + expect(result).to eq(false) + end + end + end +end diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb index f07a9a494c0..efec3b2f9ce 100644 --- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb @@ -51,7 +51,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s 'network_policies', 'geo', 'growth', - 'work_items' + 'work_items', + 'ci_users' ) end end diff --git a/spec/models/concerns/ci/has_deployment_name_spec.rb b/spec/models/concerns/ci/has_deployment_name_spec.rb new file mode 100644 index 00000000000..8c7338638b1 --- /dev/null +++ b/spec/models/concerns/ci/has_deployment_name_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::HasDeploymentName do + describe 'deployment_name?' do + let(:build) { create(:ci_build) } + + subject { build.branch? } + + it 'does detect deployment names' do + build.name = 'deployment' + + expect(build.deployment_name?).to be_truthy + end + + it 'does detect partial deployment names' do + build.name = 'do a really cool deploy' + + expect(build.deployment_name?).to be_truthy + end + + it 'does not detect non-deployment names' do + build.name = 'testing' + + expect(build.deployment_name?).to be_falsy + end + + it 'is case insensitive' do + build.name = 'DEPLOY' + expect(build.deployment_name?).to be_truthy + end + end +end diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb index 11852828eab..c399a0084fb 100644 --- a/spec/models/namespace/root_storage_statistics_spec.rb +++ b/spec/models/namespace/root_storage_statistics_spec.rb @@ -178,7 +178,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do snippets = create_list(:personal_snippet, 3, :repository, author: user) snippets.each { |s| s.statistics.refresh! } - total_personal_snippets_size = snippets.map { |s| s.statistics.repository_size }.sum + total_personal_snippets_size = snippets.sum { |s| s.statistics.repository_size } root_storage_statistics.recalculate! diff --git a/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb index 9d7ae6bcb3d..c30f93f7ea3 100644 --- a/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb +++ b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb @@ -1,13 +1,19 @@ # frozen_string_literal: true -RSpec.shared_examples 'avoid N+1 on environments serialization' do +RSpec.shared_examples 'avoid N+1 on environments serialization' do |ee: false| + # Investigating in https://gitlab.com/gitlab-org/gitlab/-/issues/353209 + let(:query_threshold) { 50 + (ee ? 4 : 0) } + it 'avoids N+1 database queries with grouping', :request_store do create_environment_with_associations(project) control = ActiveRecord::QueryRecorder.new { serialize(grouping: true) } + create_environment_with_associations(project) create_environment_with_associations(project) - expect { serialize(grouping: true) }.not_to exceed_query_limit(control.count) + expect { serialize(grouping: true) } + .not_to exceed_query_limit(control.count) + .with_threshold(query_threshold) end it 'avoids N+1 database queries without grouping', :request_store do @@ -15,9 +21,12 @@ RSpec.shared_examples 'avoid N+1 on environments serialization' do control = ActiveRecord::QueryRecorder.new { serialize(grouping: false) } + create_environment_with_associations(project) create_environment_with_associations(project) - expect { serialize(grouping: false) }.not_to exceed_query_limit(control.count) + expect { serialize(grouping: false) } + .not_to exceed_query_limit(control.count) + .with_threshold(query_threshold) end it 'does not preload for environments that does not exist in the page', :request_store do @@ -35,7 +44,7 @@ RSpec.shared_examples 'avoid N+1 on environments serialization' do end def serialize(grouping:, query: nil) - query ||= { page: 1, per_page: 1 } + query ||= { page: 1, per_page: 20 } request = double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query) EnvironmentSerializer.new(current_user: user, project: project).yield_self do |serializer| diff --git a/spec/tasks/gitlab/setup_rake_spec.rb b/spec/tasks/gitlab/setup_rake_spec.rb new file mode 100644 index 00000000000..6e4d5087517 --- /dev/null +++ b/spec/tasks/gitlab/setup_rake_spec.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +require 'rake_helper' + +RSpec.describe 'gitlab:setup namespace rake tasks', :silence_stdout do + before do + Rake.application.rake_require 'active_record/railties/databases' + Rake.application.rake_require 'tasks/seed_fu' + Rake.application.rake_require 'tasks/gitlab/setup' + end + + describe 'setup' do + subject(:setup_task) { run_rake_task('gitlab:setup') } + + let(:storages) do + { + 'name1' => 'some details', + 'name2' => 'other details' + } + end + + let(:server_service1) { double(:server_service) } + let(:server_service2) { double(:server_service) } + + let(:connections) { Gitlab::Database.database_base_models.values.map(&:connection) } + + before do + allow(Gitlab).to receive_message_chain('config.repositories.storages').and_return(storages) + + stub_warn_user_is_not_gitlab + + allow(main_object).to receive(:ask_to_continue) + end + + it 'sets up the application', :aggregate_failures do + expect_gitaly_connections_to_be_checked + + expect_connections_to_be_terminated + expect_database_to_be_setup + + setup_task + end + + context 'when an environment variable is set to force execution' do + before do + stub_env('force', 'yes') + end + + it 'sets up the application without prompting the user', :aggregate_failures do + expect_gitaly_connections_to_be_checked + + expect(main_object).not_to receive(:ask_to_continue) + + expect_connections_to_be_terminated + expect_database_to_be_setup + + setup_task + end + end + + context 'when the gitaly connection check raises an error' do + it 'exits the task without setting up the database', :aggregate_failures do + expect(Gitlab::GitalyClient::ServerService).to receive(:new).with('name1').and_return(server_service1) + expect(server_service1).to receive(:info).and_raise(GRPC::Unavailable) + + expect_connections_not_to_be_terminated + expect_database_not_to_be_setup + + expect { setup_task }.to output(/Failed to connect to Gitaly/).to_stdout + .and raise_error(SystemExit) { |error| expect(error.status).to eq(1) } + end + end + + context 'when the task is aborted' do + it 'exits without setting up the database', :aggregate_failures do + expect_gitaly_connections_to_be_checked + + expect(main_object).to receive(:ask_to_continue).and_raise(Gitlab::TaskAbortedByUserError) + + expect_connections_not_to_be_terminated + expect_database_not_to_be_setup + + expect { setup_task }.to output(/Quitting/).to_stdout + .and raise_error(SystemExit) { |error| expect(error.status).to eq(1) } + end + end + + context 'when in the production environment' do + it 'sets up the database without terminating connections', :aggregate_failures do + expect_gitaly_connections_to_be_checked + + expect(Rails.env).to receive(:production?).and_return(true) + + expect_connections_not_to_be_terminated + expect_database_to_be_setup + + setup_task + end + end + + context 'when the database is not found when terminating connections' do + it 'continues setting up the database', :aggregate_failures do + expect_gitaly_connections_to_be_checked + + expect(connections).to all(receive(:execute).and_raise(ActiveRecord::NoDatabaseError)) + + expect_database_to_be_setup + + setup_task + end + end + + def expect_gitaly_connections_to_be_checked + expect(Gitlab::GitalyClient::ServerService).to receive(:new).with('name1').and_return(server_service1) + expect(server_service1).to receive(:info) + + expect(Gitlab::GitalyClient::ServerService).to receive(:new).with('name2').and_return(server_service2) + expect(server_service2).to receive(:info) + end + + def expect_connections_to_be_terminated + expect(connections).to all(receive(:execute).with(/SELECT pg_terminate_backend/)) + end + + def expect_connections_not_to_be_terminated + connections.each do |connection| + expect(connection).not_to receive(:execute) + end + end + + def expect_database_to_be_setup + expect(Rake::Task['db:reset']).to receive(:invoke) + expect(Rake::Task['db:seed_fu']).to receive(:invoke) + end + + def expect_database_not_to_be_setup + expect(Rake::Task['db:reset']).not_to receive(:invoke) + expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) + end + end +end diff --git a/workhorse/internal/upload/body_uploader.go b/workhorse/internal/upload/body_uploader.go index 6c53bd9241b..1464f5ed5fd 100644 --- a/workhorse/internal/upload/body_uploader.go +++ b/workhorse/internal/upload/body_uploader.go @@ -16,33 +16,6 @@ type PreAuthorizer interface { PreAuthorizeHandler(next api.HandleFunc, suffix string) http.Handler } -// Verifier is an optional pluggable behavior for upload paths. If -// Verify() returns an error, Workhorse will return an error response to -// the client instead of propagating the request to Rails. The motivating -// use case is Git LFS, where Workhorse checks the size and SHA256 -// checksum of the uploaded file. -type Verifier interface { - // Verify can abort the upload by returning an error - Verify(handler *filestore.FileHandler) error -} - -// Preparer is a pluggable behavior that interprets a Rails API response -// and either tells Workhorse how to handle the upload, via the -// SaveFileOpts and Verifier, or it rejects the request by returning a -// non-nil error. Its intended use is to make sure the upload gets stored -// in the right location: either a local directory, or one of several -// supported object storage backends. -type Preparer interface { - Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error) -} - -type DefaultPreparer struct{} - -func (s *DefaultPreparer) Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error) { - opts, err := filestore.GetOpts(a) - return opts, nil, err -} - // RequestBody is a request middleware. It will store the request body to // a location by determined an api.Response value. It then forwards the // request to gitlab-rails without the original request body. diff --git a/workhorse/internal/lfs/lfs.go b/workhorse/internal/upload/lfs_preparer.go similarity index 74% rename from workhorse/internal/lfs/lfs.go rename to workhorse/internal/upload/lfs_preparer.go index e26f59046ea..29ef5c4a5c9 100644 --- a/workhorse/internal/lfs/lfs.go +++ b/workhorse/internal/upload/lfs_preparer.go @@ -1,8 +1,4 @@ -/* -In this file we handle git lfs objects downloads and uploads -*/ - -package lfs +package upload import ( "fmt" @@ -10,7 +6,6 @@ import ( "gitlab.com/gitlab-org/gitlab/workhorse/internal/api" "gitlab.com/gitlab-org/gitlab/workhorse/internal/config" "gitlab.com/gitlab-org/gitlab/workhorse/internal/filestore" - "gitlab.com/gitlab-org/gitlab/workhorse/internal/upload" ) type object struct { @@ -31,14 +26,16 @@ func (l *object) Verify(fh *filestore.FileHandler) error { } type uploadPreparer struct { - objectPreparer upload.Preparer + objectPreparer Preparer } -func NewLfsUploadPreparer(c config.Config, objectPreparer upload.Preparer) upload.Preparer { +// NewLfs returns a new preparer instance which adds capability to a wrapped +// preparer to set options required for a LFS upload. +func NewLfsPreparer(c config.Config, objectPreparer Preparer) Preparer { return &uploadPreparer{objectPreparer: objectPreparer} } -func (l *uploadPreparer) Prepare(a *api.Response) (*filestore.SaveFileOpts, upload.Verifier, error) { +func (l *uploadPreparer) Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error) { opts, _, err := l.objectPreparer.Prepare(a) if err != nil { return nil, nil, err diff --git a/workhorse/internal/lfs/lfs_test.go b/workhorse/internal/upload/lfs_preparer_test.go similarity index 72% rename from workhorse/internal/lfs/lfs_test.go rename to workhorse/internal/upload/lfs_preparer_test.go index 63b2628343e..6be4a7c2955 100644 --- a/workhorse/internal/lfs/lfs_test.go +++ b/workhorse/internal/upload/lfs_preparer_test.go @@ -1,17 +1,15 @@ -package lfs_test +package upload import ( "testing" "gitlab.com/gitlab-org/gitlab/workhorse/internal/api" "gitlab.com/gitlab-org/gitlab/workhorse/internal/config" - "gitlab.com/gitlab-org/gitlab/workhorse/internal/lfs" - "gitlab.com/gitlab-org/gitlab/workhorse/internal/upload" "github.com/stretchr/testify/require" ) -func TestLfsUploadPreparerWithConfig(t *testing.T) { +func TestLfsPreparerWithConfig(t *testing.T) { lfsOid := "abcd1234" creds := config.S3Credentials{ AwsAccessKeyID: "test-key", @@ -36,8 +34,8 @@ func TestLfsUploadPreparerWithConfig(t *testing.T) { }, } - uploadPreparer := upload.NewObjectStoragePreparer(c) - lfsPreparer := lfs.NewLfsUploadPreparer(c, uploadPreparer) + uploadPreparer := NewObjectStoragePreparer(c) + lfsPreparer := NewLfsPreparer(c, uploadPreparer) opts, verifier, err := lfsPreparer.Prepare(r) require.NoError(t, err) @@ -48,11 +46,11 @@ func TestLfsUploadPreparerWithConfig(t *testing.T) { require.NotNil(t, verifier) } -func TestLfsUploadPreparerWithNoConfig(t *testing.T) { +func TestLfsPreparerWithNoConfig(t *testing.T) { c := config.Config{} r := &api.Response{RemoteObject: api.RemoteObject{ID: "the upload ID"}} - uploadPreparer := upload.NewObjectStoragePreparer(c) - lfsPreparer := lfs.NewLfsUploadPreparer(c, uploadPreparer) + uploadPreparer := NewObjectStoragePreparer(c) + lfsPreparer := NewLfsPreparer(c, uploadPreparer) opts, verifier, err := lfsPreparer.Prepare(r) require.NoError(t, err) diff --git a/workhorse/internal/upload/object_storage_preparer.go b/workhorse/internal/upload/object_storage_preparer.go index 241cfc2d9d2..ecf3a249ecd 100644 --- a/workhorse/internal/upload/object_storage_preparer.go +++ b/workhorse/internal/upload/object_storage_preparer.go @@ -11,6 +11,9 @@ type ObjectStoragePreparer struct { credentials config.ObjectStorageCredentials } +// NewObjectStoragePreparer returns a new preparer instance which is responsible for +// setting the object storage credentials and settings needed by an uploader +// to upload to object storage. func NewObjectStoragePreparer(c config.Config) Preparer { return &ObjectStoragePreparer{credentials: c.ObjectStorageCredentials, config: c.ObjectStorageConfig} } diff --git a/workhorse/internal/upload/preparer.go b/workhorse/internal/upload/preparer.go new file mode 100644 index 00000000000..4c656e6a3f1 --- /dev/null +++ b/workhorse/internal/upload/preparer.go @@ -0,0 +1,33 @@ +package upload + +import ( + "gitlab.com/gitlab-org/gitlab/workhorse/internal/api" + "gitlab.com/gitlab-org/gitlab/workhorse/internal/filestore" +) + +// Verifier is an optional pluggable behavior for upload paths. If +// Verify() returns an error, Workhorse will return an error response to +// the client instead of propagating the request to Rails. The motivating +// use case is Git LFS, where Workhorse checks the size and SHA256 +// checksum of the uploaded file. +type Verifier interface { + // Verify can abort the upload by returning an error + Verify(handler *filestore.FileHandler) error +} + +// Preparer is a pluggable behavior that interprets a Rails API response +// and either tells Workhorse how to handle the upload, via the +// SaveFileOpts and Verifier, or it rejects the request by returning a +// non-nil error. Its intended use is to make sure the upload gets stored +// in the right location: either a local directory, or one of several +// supported object storage backends. +type Preparer interface { + Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error) +} + +type DefaultPreparer struct{} + +func (s *DefaultPreparer) Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error) { + opts, err := filestore.GetOpts(a) + return opts, nil, err +} diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go index a7c5af60d1a..1219617a05a 100644 --- a/workhorse/internal/upstream/routes.go +++ b/workhorse/internal/upstream/routes.go @@ -20,7 +20,6 @@ import ( "gitlab.com/gitlab-org/gitlab/workhorse/internal/git" "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper" "gitlab.com/gitlab-org/gitlab/workhorse/internal/imageresizer" - "gitlab.com/gitlab-org/gitlab/workhorse/internal/lfs" proxypkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/proxy" "gitlab.com/gitlab-org/gitlab/workhorse/internal/queueing" "gitlab.com/gitlab-org/gitlab/workhorse/internal/redis" @@ -408,7 +407,7 @@ func createUploadPreparers(cfg config.Config) uploadPreparers { return uploadPreparers{ artifacts: defaultPreparer, - lfs: lfs.NewLfsUploadPreparer(cfg, defaultPreparer), + lfs: upload.NewLfsPreparer(cfg, defaultPreparer), packages: defaultPreparer, uploads: defaultPreparer, }