diff options
85 files changed, 1014 insertions, 444 deletions
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index d3ae3df6050..0dbe58d1e10 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -20,7 +20,12 @@ - | if [[ "${CACHE_ASSETS_AS_PACKAGE}" == "true" ]]; then source scripts/gitlab_component_helpers.sh - gitlab_assets_archive_doesnt_exist || run_timed_command "download_and_extract_gitlab_assets" + + if ! gitlab_assets_archive_doesnt_exist; then + # We remove all assets from the native cache as they could pollute the fresh assets from the package + rm -rf public/assets/ app/assets/javascripts/locale/**/app.js + run_timed_command "download_and_extract_gitlab_assets" + fi fi - assets_compile_script - echo -n "${GITLAB_ASSETS_HASH}" > "cached-assets-hash.txt" diff --git a/.rubocop.yml b/.rubocop.yml index 41ebc6a4c16..d0cf328e719 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -319,6 +319,10 @@ Rails/SkipsModelValidations: Rails/HasManyOrHasOneDependent: Enabled: false +# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94019#note_1139714728 +Rails/CreateTableWithTimestamps: + Enabled: false + # GitLab ################################################################### Gitlab/ModuleWithInstanceVariables: diff --git a/.rubocop_todo/rails/create_table_with_timestamps.yml b/.rubocop_todo/rails/create_table_with_timestamps.yml deleted file mode 100644 index 6e60fa3e1d5..00000000000 --- a/.rubocop_todo/rails/create_table_with_timestamps.yml +++ /dev/null @@ -1,69 +0,0 @@ ---- -Rails/CreateTableWithTimestamps: - # Offense count: 63 - # Temporarily disabled due to too many offenses - Enabled: false - Exclude: - - 'db/migrate/20210305180331_create_ci_unit_tests.rb' - - 'db/migrate/20210305182855_create_ci_unit_test_failures.rb' - - 'db/migrate/20210317035357_create_dast_profiles_pipelines.rb' - - 'db/migrate/20210317104301_create_in_product_marketing_emails.rb' - - 'db/migrate/20210323125809_create_status_check_responses_table.rb' - - 'db/migrate/20210329191850_add_finding_signature_table.rb' - - 'db/migrate/20210411212813_add_clusters_integrations_prometheus.rb' - - 'db/migrate/20210423054022_create_dast_site_profiles_pipelines.rb' - - 'db/migrate/20210429032320_add_escalation_rules.rb' - - 'db/migrate/20210429131525_create_user_credit_card_validations.rb' - - 'db/migrate/20210511104929_add_epic_board_recent_visits_table.rb' - - 'db/migrate/20210512120122_add_pending_builds_table.rb' - - 'db/migrate/20210527194558_create_ci_job_token_project_scope_links.rb' - - 'db/migrate/20210601123341_add_running_builds_table.rb' - - 'db/migrate/20210602122213_add_upcoming_reconciliations.rb' - - 'db/migrate/20210604032738_create_dast_site_profiles_builds.rb' - - 'db/migrate/20210604051330_create_dast_scanner_profiles_builds.rb' - - 'db/migrate/20210604082145_create_external_status_checks_table.rb' - - 'db/migrate/20210713211008_create_banned_users.rb' - - 'db/migrate/20210729081739_create_project_topics.rb' - - 'db/migrate/20210729202143_create_incident_management_issuable_escalation_statuses.rb' - - 'db/migrate/20210730101609_create_analytics_cycle_analytics_stage_event_hashes.rb' - - 'db/migrate/20210809014850_create_agent_group_authorizations.rb' - - 'db/migrate/20210812171704_create_project_ci_feature_usages.rb' - - 'db/migrate/20210813101742_create_zentao_tracker_data.rb' - - 'db/migrate/20210813111909_create_ci_build_trace_metadata.rb' - - 'db/migrate/20210819185500_create_external_audit_event_destinations_table.rb' - - 'db/migrate/20210823172643_create_user_group_callout.rb' - - 'db/migrate/20210823213417_create_dependency_proxy_image_ttl_group_policies.rb' - - 'db/migrate/20210913010411_create_agent_project_authorizations.rb' - - 'db/migrate/20210922215740_create_issue_customer_relations_contacts.rb' - - 'db/migrate/20211004062942_create_coverage_fuzzing_corpuses.rb' - - 'db/migrate/20211004122540_create_member_tasks.rb' - - 'db/migrate/20211011004242_create_content_blocked_states.rb' - - 'db/migrate/20211011140930_create_ci_namespace_mirrors.rb' - - 'db/migrate/20211011140931_create_ci_project_mirrors.rb' - - 'db/migrate/20211011140932_create_namespaces_sync_events.rb' - - 'db/migrate/20211011141239_create_projects_sync_events.rb' - - 'db/migrate/20211028132247_create_packages_npm_metadata.rb' - - 'db/migrate/20211101132310_add_reindexing_queue.rb' - - 'db/migrate/20211101165656_create_upload_states.rb' - - 'db/migrate/20211110014701_create_agent_activity_events.rb' - - 'db/migrate/20211110092710_create_issue_emails.rb' - - 'db/migrate/20211111112425_create_merge_requests_compliance_violations.rb' - - 'db/migrate/20211115132613_create_incident_management_timeline_events.rb' - - 'db/migrate/20211117174209_create_vulnerability_reads.rb' - - 'db/migrate/20211119111006_create_job_artifact_states.rb' - - 'db/migrate/20211119154221_create_pages_deployment_states.rb' - - 'db/migrate/20211119195201_create_deployment_approvals.rb' - - 'db/migrate/20211201143042_create_lfs_object_states.rb' - - 'db/migrate/20211216220939_add_group_crm_settings.rb' - - 'db/migrate/20220110170953_create_ci_secure_files.rb' - - 'db/migrate/20220112205111_create_security_training_providers.rb' - - 'db/migrate/20220113125401_create_security_trainings.rb' - - 'db/migrate/20220120033115_create_alert_management_alert_metric_images.rb' - - 'db/migrate/20220204093120_create_analytics_cycle_analytics_aggregations.rb' - - 'db/migrate/20220211125954_create_related_epic_links.rb' - - 'db/migrate/20220216110023_create_saved_replies.rb' - - 'db/migrate/20220301175426_create_project_build_artifacts_size_refresh.rb' - - 'db/migrate/20220302110724_add_group_features_table.rb' - - 'db/migrate/20220314184009_create_protected_environment_approval_rules.rb' - - 'db/migrate/20220425120604_create_packages_cleanup_policies.rb' - - 'db/migrate/20220503102855_add_namespace_ci_cd_settings_table.rb' diff --git a/.secretsignore b/.secretsignore new file mode 100644 index 00000000000..071423bd3c1 --- /dev/null +++ b/.secretsignore @@ -0,0 +1,66 @@ +# This file is for defining paths and secrets that will be ignored by ripsecret + +doc/* +spec/* +ee/spec/* +qa/* +*_spec.rb +config/gitlab.yml.example +workhorse/testdata/localhost.key +db/fixtures/**/*.rb + +[secrets] +AUTO_DEVOPS_DOMAIN +BACKWARD_DIRECTION +CI_BUILD_BEFORE_SHA +CI_BUILD_REF_NAME +CI_BUILD_REF_SLUG +CI_COMMIT_BRANCH +CI_COMMIT_REF_SLUG +CI_DEFAULT_BRANCH +CI_DEPLOY_FREEZE +CI_DEPLOY_PASSWORD +CI_ENVIRONMENT_SLUG +CI_ENVIRONMENT_URL +CI_GITLAB_FIPS_MODE +CI_JOB_NAME_SLUG +CI_JOB_STARTED_AT +CI_PAGES_DOMAIN +CI_PROJECT_NAME +CI_PROJECT_PATH +CI_PROJECT_PATH_SLUG +CI_PROJECT_VISIBILITY +CI_REGISTRY_IMAGE +CI_REGISTRY_PASSWORD +CI_REPOSITORY_URL +CROWDIN_API_KEY +DAST_API_PROFILE +DAST_PASSWORD_BASE64 +DAST_SUBMIT_FIELD +DAST_USERNAME_FIELD +DORA_METRICS_KEYS +ESCALATION_STATUS +FIFTY_PACKAGE_FILES +FORTY_PACKAGE_FILES +FORWARD_DIRECTION +GITLAB_FEATURES +GITLAB_USER_EMAIL +GITLAB_USER_LOGIN +GITLAB_USER_NAME +HARBOR_PASSWORD +HARBOR_USERNAME +KUBE_CA_PEM_FILE +KUBE_SERVICE_ACCOUNT +NAVSOURCE_VALUE +ONE_HUNDRED_TAGS +ONE_PACKAGE_FILE +STAGING_ENABLED +TEN_PACKAGE_FILES +THIRTY_PACKAGE_FILES +TRIGGER_PAYLOAD +TWENTY_FIVE_TAGS +TWENTY_PACKAGE_FILES +YOUR-ACCESSKEYID +YOUR-CLIENT-SECRET +YOUR_AUTH0_CLIENT_SECRET +sbdMsxcgW2Xs75Q2uHc9FhUCZSEV3fSg diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index f76ff29f1bb..071bcd3cbff 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -4b3f2921b5f0d659b44aee6323d82fc3698a8ede +4b21b45f897007e0d3428353913edac8d1ecc422 diff --git a/GITLAB_METRICS_EXPORTER_VERSION b/GITLAB_METRICS_EXPORTER_VERSION index 007fdc31184..6d47f7a2be5 100644 --- a/GITLAB_METRICS_EXPORTER_VERSION +++ b/GITLAB_METRICS_EXPORTER_VERSION @@ -1 +1 @@ -2a92165653c54fd23ead433e2cb477d6663c607d +f2d7e32cb5e3d8886a2bac5da2703b31f6a38d88 diff --git a/app/assets/javascripts/content_editor/extensions/suggestions.js b/app/assets/javascripts/content_editor/extensions/suggestions.js index 8976b9cafee..a9628c78add 100644 --- a/app/assets/javascripts/content_editor/extensions/suggestions.js +++ b/app/assets/javascripts/content_editor/extensions/suggestions.js @@ -17,7 +17,7 @@ function createSuggestionPlugin({ char, dataSource, search, - limit = Infinity, + limit = 15, nodeType, nodeProps = {}, }) { diff --git a/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue b/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue index 403bad3db11..41cd650f932 100644 --- a/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue +++ b/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue @@ -7,18 +7,12 @@ export default { components: { GlModal, }, - computed: { - primaryProps() { - return { - text: __('Reset'), - attributes: [{ variant: 'danger' }, { category: 'primary' }], - }; - }, - cancelProps() { - return { - text: __('Cancel'), - }; - }, + primaryProps: { + text: __('Reset'), + attributes: [{ variant: 'danger' }, { category: 'primary' }], + }, + cancelProps: { + text: __('Cancel'), }, methods: { onReset() { @@ -33,8 +27,8 @@ export default { modal-id="confirmResetIntegration" size="sm" :title="s__('Integrations|Reset integration?')" - :action-primary="primaryProps" - :action-cancel="cancelProps" + :action-primary="$options.primaryProps" + :action-cancel="$options.cancelProps" @primary="onReset" > <p> diff --git a/app/assets/javascripts/jobs/components/table/cells/duration_cell.vue b/app/assets/javascripts/jobs/components/table/cells/duration_cell.vue index 120f01db8f0..d1b2da4d115 100644 --- a/app/assets/javascripts/jobs/components/table/cells/duration_cell.vue +++ b/app/assets/javascripts/jobs/components/table/cells/duration_cell.vue @@ -1,15 +1,16 @@ <script> -import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; -import { formatDate, getTimeago, durationTimeFormatted } from '~/lib/utils/datetime_utility'; +import { GlIcon } from '@gitlab/ui'; +import { durationTimeFormatted } from '~/lib/utils/datetime_utility'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import timeagoMixin from '~/vue_shared/mixins/timeago'; export default { iconSize: 12, - directives: { - GlTooltip: GlTooltipDirective, - }, components: { GlIcon, + TimeAgoTooltip, }, + mixins: [timeagoMixin], props: { job: { type: Object, @@ -23,12 +24,6 @@ export default { duration() { return this.job?.duration; }, - timeFormatted() { - return getTimeago().format(this.finishedTime); - }, - tooltipTitle() { - return formatDate(this.finishedTime); - }, durationFormatted() { return durationTimeFormatted(this.duration); }, @@ -44,15 +39,7 @@ export default { </div> <div v-if="finishedTime" data-testid="job-finished-time"> <gl-icon name="calendar" :size="$options.iconSize" data-testid="finished-time-icon" /> - <time - v-gl-tooltip - :title="tooltipTitle" - :datetime="finishedTime" - data-placement="top" - data-container="body" - > - {{ timeFormatted }} - </time> + <time-ago-tooltip :time="finishedTime" /> </div> </div> </template> diff --git a/app/assets/javascripts/webhooks/components/form_url_app.vue b/app/assets/javascripts/webhooks/components/form_url_app.vue index a156b638e21..4fafeff8804 100644 --- a/app/assets/javascripts/webhooks/components/form_url_app.vue +++ b/app/assets/javascripts/webhooks/components/form_url_app.vue @@ -70,12 +70,13 @@ export default { getInitialItems() { return isEmpty(this.initialUrlVariables) ? [{}] : cloneDeep(this.initialUrlVariables); }, - isEditingItem(key) { + isEditingItem(index, key) { if (isEmpty(this.initialUrlVariables)) { return false; } - return this.initialUrlVariables.some((item) => item.key === key); + const item = this.initialUrlVariables[index]; + return item && item.key === key; }, keyInvalidFeedback(key) { if (this.isValidated && isEmpty(key)) { @@ -84,8 +85,8 @@ export default { return null; }, - valueInvalidFeedback(key, value) { - if (this.isEditingItem(key)) { + valueInvalidFeedback(index, key, value) { + if (this.isEditingItem(index, key)) { return null; } @@ -93,6 +94,10 @@ export default { return this.$options.i18n.inputRequired; } + if (!isEmpty(value) && !this.url?.includes(value)) { + return this.$options.i18n.valuePartOfUrl; + } + return null; }, isValid() { @@ -105,7 +110,8 @@ export default { if ( this.maskEnabled && this.items.some( - ({ key, value }) => this.keyInvalidFeedback(key) || this.valueInvalidFeedback(key, value), + ({ key, value }, index) => + this.keyInvalidFeedback(key) || this.valueInvalidFeedback(index, key, value), ) ) { return false; @@ -145,6 +151,7 @@ export default { urlLabel: __('URL'), urlPlaceholder: 'http://example.com/trigger-ci.json', urlPreview: s__('Webhooks|URL preview'), + valuePartOfUrl: s__('Webhooks|Must match part of URL'), }, }; </script> @@ -186,9 +193,9 @@ export default { :index="index" :item-key="key" :item-value="value" - :is-editing="isEditingItem(key)" + :is-editing="isEditingItem(index, key)" :key-invalid-feedback="keyInvalidFeedback(key)" - :value-invalid-feedback="valueInvalidFeedback(key, value)" + :value-invalid-feedback="valueInvalidFeedback(index, key, value)" @input="onItemInput" @remove="removeItem" /> diff --git a/app/controllers/projects/ml/experiments_controller.rb b/app/controllers/projects/ml/experiments_controller.rb index d19cc93aedd..749586791ac 100644 --- a/app/controllers/projects/ml/experiments_controller.rb +++ b/app/controllers/projects/ml/experiments_controller.rb @@ -25,7 +25,7 @@ module Projects private def check_feature_flag - render_404 unless Feature.enabled?(:ml_experiment_tracking) + render_404 unless Feature.enabled?(:ml_experiment_tracking, @project) end end end diff --git a/app/controllers/projects/packages/infrastructure_registry_controller.rb b/app/controllers/projects/packages/infrastructure_registry_controller.rb index f1410bf6043..733df9fdb45 100644 --- a/app/controllers/projects/packages/infrastructure_registry_controller.rb +++ b/app/controllers/projects/packages/infrastructure_registry_controller.rb @@ -5,7 +5,7 @@ module Projects class InfrastructureRegistryController < Projects::ApplicationController include PackagesAccess - feature_category :infrastructure_as_code + feature_category :package_registry urgency :low def show diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb index ad3b2bc98e7..ffe95bf4fee 100644 --- a/app/controllers/projects/registry/repositories_controller.rb +++ b/app/controllers/projects/registry/repositories_controller.rb @@ -22,7 +22,11 @@ module Projects def destroy image.delete_scheduled! - DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) # rubocop:disable CodeReuse/Worker + + unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker) + DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) # rubocop:disable CodeReuse/Worker + end + track_package_event(:delete_repository, :container) respond_to do |format| diff --git a/app/controllers/terraform/services_controller.rb b/app/controllers/terraform/services_controller.rb index e7b9a94fd8e..7ebe1d9ba98 100644 --- a/app/controllers/terraform/services_controller.rb +++ b/app/controllers/terraform/services_controller.rb @@ -3,7 +3,7 @@ class Terraform::ServicesController < ApplicationController skip_before_action :authenticate_user! - feature_category :infrastructure_as_code + feature_category :package_registry def index render json: { 'modules.v1' => "/api/#{::API::API.version}/packages/terraform/modules/v1/" } diff --git a/app/graphql/mutations/container_repositories/destroy.rb b/app/graphql/mutations/container_repositories/destroy.rb index 2a45291be22..fe1c3fe4e61 100644 --- a/app/graphql/mutations/container_repositories/destroy.rb +++ b/app/graphql/mutations/container_repositories/destroy.rb @@ -21,9 +21,11 @@ module Mutations container_repository = authorized_find!(id: id) container_repository.delete_scheduled! - # rubocop:disable CodeReuse/Worker - DeleteContainerRepositoryWorker.perform_async(current_user.id, container_repository.id) - # rubocop:enable CodeReuse/Worker + + unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker) + DeleteContainerRepositoryWorker.perform_async(current_user.id, container_repository.id) # rubocop:disable CodeReuse/Worker + end + track_event(:delete_repository, :container) { diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 6c09e15f56f..f08c1a2ff0a 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -74,24 +74,6 @@ module BlobHelper ref) end - def modify_file_button(project = @project, ref = @ref, path = @path, blob:, label:, action:, btn_class:, modal_type:) - return unless current_user - return unless blob - - common_classes = "btn gl-button btn-default btn-#{btn_class}" - base_button = button_tag(label, class: "#{common_classes} disabled", disabled: true) - - if !on_top_of_branch?(project, ref) - modify_file_button_tooltip(base_button, _("You can only %{action} files when you are on a branch") % { action: action }) - elsif blob.stored_externally? - modify_file_button_tooltip(base_button, _("It is not possible to %{action} files that are stored in LFS using the web interface") % { action: action }) - elsif can_modify_blob?(blob, project, ref) - button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' - elsif can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) - edit_fork_button_tag(common_classes, project, label, edit_modify_file_fork_params(action), action) - end - end - def can_modify_blob?(blob, project = @project, ref = @ref) !blob.stored_externally? && can_edit_tree?(project, ref) end @@ -346,12 +328,4 @@ module BlobHelper @path.to_s.end_with?(Ci::Pipeline::CONFIG_EXTENSION) || @path.to_s == @project.ci_config_path_or_default end - - private - - def modify_file_button_tooltip(button, tooltip_message) - # Disabled buttons with tooltips should have the tooltip attached - # to a wrapper element https://bootstrap-vue.org/docs/components/tooltip#disabled-elements - content_tag(:span, button, class: 'btn-group has-tooltip', title: tooltip_message, data: { container: 'body' }) - end end diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb index bd75f2631d5..7da4e31b472 100644 --- a/app/models/container_repository.rb +++ b/app/models/container_repository.rb @@ -70,6 +70,7 @@ class ContainerRepository < ApplicationRecord scope :with_migration_pre_import_started_at_nil_or_before, ->(timestamp) { where("COALESCE(migration_pre_import_started_at, '01-01-1970') < ?", timestamp) } scope :with_migration_pre_import_done_at_nil_or_before, ->(timestamp) { where("COALESCE(migration_pre_import_done_at, '01-01-1970') < ?", timestamp) } scope :with_stale_ongoing_cleanup, ->(threshold) { cleanup_ongoing.where('expiration_policy_started_at < ?', threshold) } + scope :with_stale_delete_at, ->(threshold) { where('delete_started_at < ?', threshold) } scope :import_in_process, -> { where(migration_state: %w[pre_importing pre_import_done importing]) } scope :recently_done_migration_step, -> do diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index e03fbf6b44e..862842440a6 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -291,6 +291,15 @@ :weight: 1 :idempotent: false :tags: [] +- :name: cronjob:container_registry_cleanup + :worker_name: ContainerRegistry::CleanupWorker + :feature_category: :container_registry + :has_external_dependencies: false + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: cronjob:container_registry_migration_enqueuer :worker_name: ContainerRegistry::Migration::EnqueuerWorker :feature_category: :container_registry diff --git a/app/workers/container_registry/cleanup_worker.rb b/app/workers/container_registry/cleanup_worker.rb new file mode 100644 index 00000000000..8350ae3431b --- /dev/null +++ b/app/workers/container_registry/cleanup_worker.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module ContainerRegistry + class CleanupWorker + include ApplicationWorker + # we don't have any project, user or group context here + include CronjobQueue # rubocop:disable Scalability/CronWorkerContext + + data_consistency :always + idempotent! + + feature_category :container_registry + + STALE_DELETE_THRESHOLD = 30.minutes.freeze + BATCH_SIZE = 200 + + def perform + return unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker) + + log_counts + + reset_stale_deletes + + enqueue_delete_container_repository_jobs if ContainerRepository.delete_scheduled.exists? + end + + private + + def reset_stale_deletes + ContainerRepository.delete_ongoing.each_batch(of: BATCH_SIZE) do |batch| + batch.with_stale_delete_at(STALE_DELETE_THRESHOLD.ago).update_all( + status: :delete_scheduled, + delete_started_at: nil + ) + end + end + + def enqueue_delete_container_repository_jobs + ContainerRegistry::DeleteContainerRepositoryWorker.perform_with_capacity + end + + def log_counts + ::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do + log_extra_metadata_on_done( + :delete_scheduled_container_repositories_count, + ContainerRepository.delete_scheduled.count + ) + log_extra_metadata_on_done( + :stale_delete_container_repositories_count, + stale_delete_container_repositories.count + ) + end + end + + def stale_delete_container_repositories + ContainerRepository.delete_ongoing.with_stale_delete_at(STALE_DELETE_THRESHOLD.ago) + end + end +end diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb index 3aff4b42629..adb6d38fd12 100644 --- a/app/workers/pages_worker.rb +++ b/app/workers/pages_worker.rb @@ -11,7 +11,7 @@ class PagesWorker # rubocop:disable Scalability/IdempotentWorker worker_resource_boundary :cpu def perform(action, *arg) - send(action, *arg) # rubocop:disable GitlabSecurity/PublicSend + deploy(*arg) if action == 'deploy' end def deploy(build_id) diff --git a/config/feature_flags/development/ci_variable_expansion_in_rules_exists.yml b/config/feature_flags/development/container_registry_delete_repository_with_cron_worker.yml index dec187db4ab..ef531228398 100644 --- a/config/feature_flags/development/ci_variable_expansion_in_rules_exists.yml +++ b/config/feature_flags/development/container_registry_delete_repository_with_cron_worker.yml @@ -1,8 +1,8 @@ --- -name: ci_variable_expansion_in_rules_exists -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101639 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381046 +name: container_registry_delete_repository_with_cron_worker +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101918 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/378818 milestone: '15.6' type: development -group: group::pipeline authoring +group: group::container registry default_enabled: false diff --git a/config/feature_flags/development/ml_experiment_tracking.yml b/config/feature_flags/development/ml_experiment_tracking.yml index 2749cbc3fc1..19f14196591 100644 --- a/config/feature_flags/development/ml_experiment_tracking.yml +++ b/config/feature_flags/development/ml_experiment_tracking.yml @@ -2,7 +2,8 @@ name: ml_experiment_tracking introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95689 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371669 -milestone: '15.4' +milestone: '15.6' type: development group: group::incubation default_enabled: false +log_state_changes: true diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 60695f3321c..da4277c8146 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -572,6 +572,9 @@ Settings.cron_jobs['container_registry_migration_observer_worker']['job_class'] Settings.cron_jobs['container_registry_migration_enqueuer_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['container_registry_migration_enqueuer_worker']['cron'] ||= '15,45 */1 * * *' Settings.cron_jobs['container_registry_migration_enqueuer_worker']['job_class'] = 'ContainerRegistry::Migration::EnqueuerWorker' +Settings.cron_jobs['cleanup_container_registry_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['cleanup_container_registry_worker']['cron'] ||= '*/5 * * * *' +Settings.cron_jobs['cleanup_container_registry_worker']['job_class'] = 'ContainerRegistry::CleanupWorker' Settings.cron_jobs['image_ttl_group_policy_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['image_ttl_group_policy_worker']['cron'] ||= '40 0 * * *' Settings.cron_jobs['image_ttl_group_policy_worker']['job_class'] = 'DependencyProxy::ImageTtlGroupPolicyWorker' diff --git a/config/open_api.yml b/config/open_api.yml index 5bae48920fb..dc01b2730c3 100644 --- a/config/open_api.yml +++ b/config/open_api.yml @@ -49,10 +49,14 @@ metadata: description: Operations related to linting a CI config file - name: group_export description: Operations related to exporting groups + - name: group_import + description: Operations related to importing groups - name: merge_requests description: Operations related to merge requests - name: metadata description: Operations related to metadata of the GitLab instance + - name: metrics_user_starred_dashboards + description: Operations related to User-starred metrics dashboards - name: project_export description: Operations related to exporting projects - name: project_hooks diff --git a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml index db5109204a6..9e308fbecce 100644 --- a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml +++ b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml @@ -11,11 +11,13 @@ The `POST` method operation on the `/api/v4/runners` endpoint is deprecated. This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint - and method in GitLab 16.0, and introduce a new - [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8. - This new architecture introduces a new method for registering runners and eliminates the legacy + and method in GitLab 16.0. + + In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance, + as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/). + This new architecture introduces a new method for registering runners and will eliminate the legacy [runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens). - We plan to introduce a new method of binding a GitLab Runner to a GitLab instance in GitLab 15.8. This will be the only supported method starting in GitLab 16.0. + From GitLab 16.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods. end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end. tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] diff --git a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml index c3fd97aa296..330f1b1f39e 100644 --- a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml +++ b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml @@ -8,7 +8,12 @@ stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381111 # (required) Link to the deprecation issue in GitLab body: | # (required) Do not modify this line, instead modify the lines below. - The [`runnerRegistrationToken`](https://docs.gitlab.com/runner/install/kubernetes.html#required-configuration) parameter to use the GitLab Helm Chart to install a runner on Kubernetes is deprecated. GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/). As part of the new architecture, we plan to introduce a single runner authentication token, `runnerToken`, and a unique system ID saved to the `config.toml` to allow traceability between jobs and runners. - We plan to introduce a new method of binding a GitLab Runner to a GitLab instance in GitLab 15.8. This will be the only supported method starting in GitLab 16.0. + The [`runnerRegistrationToken`](https://docs.gitlab.com/runner/install/kubernetes.html#required-configuration) parameter to use the GitLab Helm Chart to install a runner on Kubernetes is deprecated. + + As part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/), in GitLab 15.8 we plan to introduce: + + - A new method to bind runners to a GitLab instance. + - A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners. + From GitLab 16.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods. end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. end_of_support_date: "2023-05-22" # (optional) The date of the milestone release when support for this feature will end. diff --git a/db/post_migrate/20221107094359_recount_epic_cache_counts.rb b/db/post_migrate/20221107094359_recount_epic_cache_counts.rb new file mode 100644 index 00000000000..37ab952edba --- /dev/null +++ b/db/post_migrate/20221107094359_recount_epic_cache_counts.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class RecountEpicCacheCounts < Gitlab::Database::Migration[2.0] + MIGRATION = 'RecountEpicCacheCounts' + DELAY_INTERVAL = 2.minutes.to_i + BATCH_SIZE = 200 + MAX_BATCH_SIZE = 1000 + SUB_BATCH_SIZE = 20 + + disable_ddl_transaction! + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :epics, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + max_batch_size: MAX_BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE, + gitlab_schema: :gitlab_main + ) + end + + def down + delete_batched_background_migration(MIGRATION, :epics, :id, []) + end +end diff --git a/db/schema_migrations/20221107094359 b/db/schema_migrations/20221107094359 new file mode 100644 index 00000000000..f5cb6814e61 --- /dev/null +++ b/db/schema_migrations/20221107094359 @@ -0,0 +1 @@ +47d2ac5130583e1a5d0b89d73f32d4af208f8800fc62726bce8ca86e3ce0ed40
\ No newline at end of file diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 3134afc1041..1f73d8bff38 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -502,7 +502,7 @@ For your app, complete the following steps on Casdoor: ensure the Casdoor app has the following `Redirect URI`: `https://gitlab.example.com/users/auth/openid_connect/callback`. -See the [Casdoor documentation](https://casdoor.org/docs/integration/gitlab) for more details. +See the [Casdoor documentation](https://casdoor.org/docs/integration/ruby/gitlab) for more details. Example Omnibus GitLab configuration (file path: `/etc/gitlab/gitlab.rb`): diff --git a/doc/architecture/blueprints/runner_scaling/index.md b/doc/architecture/blueprints/runner_scaling/index.md index 8459f8e673a..f7c477b4154 100644 --- a/doc/architecture/blueprints/runner_scaling/index.md +++ b/doc/architecture/blueprints/runner_scaling/index.md @@ -209,7 +209,7 @@ easier to understand how it performs. ## Details -How the abstraction for the custom provider will look exactly is something that +How the abstraction will look exactly is something that we will need to prototype, PoC and decide in a data-informed way. There are a few proposals that we should describe in detail, develop requirements for, PoC and score. We will choose the solution that seems to support our goals the @@ -256,6 +256,10 @@ them each separately. to the Runner system. These details are highly dependent on the VM architecture and operating system as well as Executor type. +See also Glossary below. + +#### Current state + The current architecture has several points of coupling between concerns. Coupling reduces opportunities for abstraction (e.g. community supported plugins) and increases complexity, making the code harder to understand, @@ -390,7 +394,7 @@ for by the plugin. Rationale: [Description of the Custom Executor Provider proposal](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/28848#note_823321515) -### Fleeting VM provider +### Taskscaler provider We can introduce a more simple version of the `Machine` abstraction in the form of a "Fleeting" interface. Fleeting provides a low-level interface to @@ -411,6 +415,22 @@ component so it can be used by multiple Runner Executors (not just `docker+autos Rationale: [Description of the InstanceGroup / Fleeting proposal](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/28848#note_823430883) POC: [Merge request](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3315) +## Glossary + +- **[GitLab Runner](../../../development/documentation/styleguide/word_list.md#gitlab-runner)** - the software application that you can choose to install and manage, whose source code is hosted at `gitlab.com/gitlab-org/gitlab-runner`. +- **[runners](../../../development/documentation/styleguide/word_list.md#runner-runners)** - the runner is the agent that's responsible for running GitLab CI/CD jobs in an environment and reporting the results to a GitLab instance. It /1/ retrieves jobs from GitLab, /2/ configures a local or remote build environment, and /3/ executes jobs within the provisioned environment, passing along log data and status updates to GitLab. +- **runner manager** - the runner process is often referred to as the `Runner Manager` as it manages multiple runners, which are the `[[runners]]` workers defined in the runners `config.toml` file. +- **executor** - a concrete environment which can be prepared and used to run a job. A new executor is created for each job. +- **executor provider** - an implementation capable of providing executors on demand. Executor providers are registered on import and initialized once when a runner starts up. +- **custom executor** - works as an interface between GitLab Runner and a set of binaries or shell scripts with environment variable inputs that enable executing CI jobs in any host computing environment. New custom executors can be added to the system without making any changes to the GitLab Runner codebase. +- **custom executor provider** - a new abstraction, proposed under the custom provider heading in the plugin boundary proposal section above, which allows new executor providers to be created without modifying the GitLab Runner codebase. The protocol could be similar to custom executors or done over gRPC. This abstraction places all the mechanics of producing executors within the plugin, delegating autoscaling and lifecycle management concerns to each implementation. +- **taskscaler** - a new library, proposed under the taskscaler provider heading in the plugin boundary proposal section above, which is parameterized with a concrete executor provider and a fleeting provider. Taskscaler is responsible for the autoscaling concern and can be used to autoscale any executor provider using any VM shape. Taskscaler is also responsible for the runner-specific aspect of VM lifecycle and keeps track of how many jobs are using a give VM and how many times a VM has been used. +- **fleeting** - a new library proposed along with taskscaler which provides abstractions for cloud provider VMs. +- **fleeting instance group** - the abstraction that fleeting uses to represent a pool of like VMs. This would represent a GCP IGM or an AWS ASG (without the autoscaling). Instance groups can be increased, decreased or can provide connection details for a specific VM. +- **fleeting plugin** - a concrete implementation of a fleeting instance group representing a specific IGM or ASG (when initialized). There will be N of these, one for each provider, each in its own project. We will own and maintain the core ones but some will be community supported. A new fleeting plugin can be created without making any changes to the runner, taskscaler or fleeting code bases. This makes it analogous to the custom executor provider in terms of self-service and decoupling, but along a different line of concerns. +- **fleeting plugin Google Compute** - the fleeting plugin which creates GCP instances. This lives in a separate project from the fleeting and taskscaler. +- **fleeting plugin AWS** - the fleeting plugin which creates AWS instances. This lives in a separate project from the fleeting and taskscaler. + ## Status Status: RFC. diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 023cd7e9b02..a18a705fd31 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -3432,7 +3432,7 @@ relative to `refs/heads/branch1` and the pipeline source is a merge request even #### `rules:exists` > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24021) in GitLab 12.4. -> - CI/CD variable support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/283881) in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_variable_expansion_in_rules_exists`. Disabled by default. +> - CI/CD variable support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/283881) in GitLab 15.6. Use `exists` to run a job when certain files exist in the repository. diff --git a/doc/development/code_review.md b/doc/development/code_review.md index 586610ca784..90f33319365 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -28,6 +28,13 @@ The reviewer can: - Give you a second opinion on the chosen solution and implementation. - Help look for bugs, logic problems, or uncovered edge cases. +If the merge request is trivial (for example, fixing a typo or a tiny refactor that doesn't change the behavior or any data), +you can skip the reviewer step and directly ask a [maintainer](https://about.gitlab.com/handbook/engineering/workflow/code-review/#maintainer). +Otherwise, a merge request should always be first reviewed by a reviewer in each +[category (e.g. backend, database)](#approval-guidelines) +the MR touches, as maintainers may not have the relevant domain knowledge, and +also to spread the workload. + For assistance with security scans or comments, include the Application Security Team (`@gitlab-com/gl-security/appsec`). The reviewers use the [reviewer functionality](../user/project/merge_requests/getting_started.md#reviewer) in the sidebar. diff --git a/doc/development/database/database_migration_pipeline.md b/doc/development/database/database_migration_pipeline.md index 148dc1e94a0..e5ab837bf94 100644 --- a/doc/development/database/database_migration_pipeline.md +++ b/doc/development/database/database_migration_pipeline.md @@ -22,7 +22,9 @@ For security reasons, access to the pipeline is restricted to database maintaine When the pipeline starts, a bot notifies you with a comment in the merge request. When it finishes, the comment gets updated with the test results. -There are three sections which are described below. + +The comment contains testing information for both the `main` and `ci` databases. +Each database tested has four sections which are described below. ## Summary @@ -44,12 +46,23 @@ The next section of the comment contains detailed information for each migration | Queries | Every query executed during the migration, along with the number of calls, timings, and the number of the changed rows. | | Runtime histogram | Indicates the distribution of query times for the migration. | +## Background Migration Details + +The next section of the comment contains detailed information about each batched background migration, including: + +| Result | Description | +|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Sampling Information | The number of batches sampled during this test run. Sampled batches are chosen uniformly across the table's ID range. Sampling runs for 30 minutes, split evenly across each background migration to test. | +| Aggregated Query Information | Aggregate data about each query executed across all the sampled batches, along with the number of calls, timings, and the number of changed rows. | +| Batch runtime histogram | A histogram of timings for each sampled batch from the background migration. | +| Query runtime histogram | A histogram of timings for all queries executed in any batch of this background migration. | + ## Clone details and artifacts Some additional information is included at the bottom of the comment: -| Result | Description | -|----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Migrations pending on GitLab.com | A summary of migrations not deployed yet to GitLab.com. This information is useful when testing a migration that was merged but not deployed yet. | -| Clone details | A link to the `Postgres.ai` thin clone created for this testing pipeline, along with information about its expiry. This can be used to further explore the results of running the migration. Only accessible by database maintainers or with an access request. | -| Artifacts | A link to the pipeline's artifacts. Full query logs for each migration (ending in `.log`) are available there and only accessible by database maintainers or with an access request. | +| Result | Description | +|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Migrations pending on GitLab.com | A summary of migrations not deployed yet to GitLab.com. This information is useful when testing a migration that was merged but not deployed yet. | +| Clone details | A link to the `Postgres.ai` thin clone created for this testing pipeline, along with information about its expiry. This can be used to further explore the results of running the migration. Only accessible by database maintainers or with an access request. | +| Artifacts | A link to the pipeline's artifacts. Full query logs for each migration (ending in `.log`) are available there and only accessible by database maintainers or with an access request. Details of the specific batched background migration batches sampled are also available. | diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md index 58f6b02c1f1..aa7344f8506 100644 --- a/doc/development/testing_guide/best_practices.md +++ b/doc/development/testing_guide/best_practices.md @@ -425,6 +425,11 @@ results are available, and not just the first failure. when you need an ID/IID/access level that doesn't actually exists. Using 123, 1234, or even 999 is brittle as these IDs could actually exist in the database in the context of a CI run. +- All top-level `RSpec.describe` blocks should have [`feature_category`](https://about.gitlab.com/categories.json) metadata set. + Consider splitting the file in the case there are identified multiple feature categories in same file. + If no `feature_category` is identified then use `not_owned`. This information is used in flaky test + issues created in order to identify the group owning the feature. + Eg: `RSpec.describe Admin::Geo::SettingsController, :geo, feature_category: :geo_replication do`. ### Coverage diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 38d8f0049db..8019eccc421 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -38,7 +38,7 @@ you to use. The URL to your GitLab installation, such as `https://gitlab.example.com/users/auth`. Leaving this field empty - [results in an `Invalid redirect_uri` message](https://confluence.atlassian.com/bitbucket/oauth-faq-338365710.html). + results in an `Invalid redirect_uri` message. WARNING: To help prevent an [OAuth 2 covert redirect](https://oauth.net/advisories/2014-1-covert-redirect/) diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index 21ebb746a61..147bf5b1715 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -76,11 +76,13 @@ Review the details carefully before upgrading. The `POST` method operation on the `/api/v4/runners` endpoint is deprecated. This endpoint and method [registers](https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner) a runner with a GitLab instance at the instance, group, or project level through the API. We plan to remove this endpoint -and method in GitLab 16.0, and introduce a new -[GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8. -This new architecture introduces a new method for registering runners and eliminates the legacy +and method in GitLab 16.0. + +In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance, +as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/). +This new architecture introduces a new method for registering runners and will eliminate the legacy [runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens). -We plan to introduce a new method of binding a GitLab Runner to a GitLab instance in GitLab 15.8. This will be the only supported method starting in GitLab 16.0. +From GitLab 16.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods. </div> @@ -110,8 +112,13 @@ WARNING: This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). Review the details carefully before upgrading. -The [`runnerRegistrationToken`](https://docs.gitlab.com/runner/install/kubernetes.html#required-configuration) parameter to use the GitLab Helm Chart to install a runner on Kubernetes is deprecated. GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/). As part of the new architecture, we plan to introduce a single runner authentication token, `runnerToken`, and a unique system ID saved to the `config.toml` to allow traceability between jobs and runners. -We plan to introduce a new method of binding a GitLab Runner to a GitLab instance in GitLab 15.8. This will be the only supported method starting in GitLab 16.0. +The [`runnerRegistrationToken`](https://docs.gitlab.com/runner/install/kubernetes.html#required-configuration) parameter to use the GitLab Helm Chart to install a runner on Kubernetes is deprecated. + +As part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/), in GitLab 15.8 we plan to introduce: + +- A new method to bind runners to a GitLab instance. +- A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners. +From GitLab 16.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods. </div> </div> diff --git a/doc/update/zero_downtime.md b/doc/update/zero_downtime.md index aebd27f84a9..eb1d6cef606 100644 --- a/doc/update/zero_downtime.md +++ b/doc/update/zero_downtime.md @@ -395,7 +395,7 @@ HA. #### In the application node -According to [official Redis documentation](https://redis.io/docs/manual/admin/#upgrading-or-restarting-a-redis-instance-without-downtime), +According to [official Redis documentation](https://redis.io/docs/management/admin/#upgrading-or-restarting-a-redis-instance-without-downtime), the easiest way to update an HA instance using Sentinel is to upgrade the secondaries one after the other, perform a manual failover from current primary (running old version) to a recently upgraded secondary (running a new diff --git a/doc/user/project/integrations/webex_teams.md b/doc/user/project/integrations/webex_teams.md index 930ca8e99b8..be4528ba70d 100644 --- a/doc/user/project/integrations/webex_teams.md +++ b/doc/user/project/integrations/webex_teams.md @@ -13,7 +13,7 @@ You can configure GitLab to send notifications to a Webex Teams space: ## Create a webhook for the space -1. Go to the [Incoming Webhooks app page](https://apphub.webex.com/applications/incoming-webhooks-cisco-systems-38054-23307). +1. Go to the [Incoming Webhooks app page](https://apphub.webex.com/applications/incoming-webhooks-cisco-systems-38054-23307-75252). 1. Select **Connect**, and sign in to Webex Teams if required. 1. Enter a name for the webhook and select the space to receive the notifications. 1. Select **ADD**. diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 1cf902d2290..c2952b23615 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -707,6 +707,14 @@ A few things to remember: ## Troubleshooting issue boards +### `There was a problem fetching users` on group issue board when filtering by Author or Assignee + +If you get a banner with `There was a problem fetching users` error when filtering by author or assignee on +group issue board, make sure that you are added as a member to the current group. +Non-members do not have permission to list group members when filtering by author or assignee on issue boards. + +To fix this error, you should add all of your users to the top-level group with at least the Guest role. + ### Use Rails console to fix issue boards not loading and timing out If you see issue board not loading and timing out in UI, use Rails console to call the Issue Rebalancing service to fix it: diff --git a/lefthook.yml b/lefthook.yml index a2819358bdf..03542a437e3 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -79,3 +79,7 @@ pre-push: files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD glob: 'data/removals/*.yml' run: echo "Changes to removals files detected. Checking removals..\n"; bundle exec rake gitlab:docs:check_removals + secrets-detection: + tags: secrets + files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD + run: 'if command -v ripsecrets > /dev/null 2>&1; then ripsecrets --strict-ignore {files}; else echo "WARNING: ripsecrets is not installed. Please install it."; fi' diff --git a/lib/api/api.rb b/lib/api/api.rb index 557721ecaf0..9d2e5f545d2 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -179,6 +179,7 @@ module API mount ::API::BroadcastMessages mount ::API::BulkImports mount ::API::Ci::Jobs + mount ::API::Ci::PipelineSchedules mount ::API::Ci::ResourceGroups mount ::API::Ci::Runner mount ::API::Ci::Runners @@ -200,6 +201,7 @@ module API mount ::API::FreezePeriods mount ::API::GroupClusters mount ::API::GroupExport + mount ::API::GroupImport mount ::API::GroupVariables mount ::API::ImportBitbucketServer mount ::API::ImportGithub @@ -208,6 +210,7 @@ module API mount ::API::Lint mount ::API::MergeRequestDiffs mount ::API::Metadata + mount ::API::Metrics::UserStarredDashboards mount ::API::PersonalAccessTokens::SelfInformation mount ::API::PersonalAccessTokens mount ::API::ProjectClusters @@ -255,6 +258,7 @@ module API mount ::API::Boards mount ::API::Branches mount ::API::Ci::JobArtifacts + mount ::API::Ci::Pipelines mount ::API::Ci::PipelineSchedules mount ::API::Ci::SecureFiles mount ::API::Ci::Triggers @@ -278,7 +282,6 @@ module API mount ::API::GroupBoards mount ::API::GroupContainerRepositories mount ::API::GroupDebianDistributions - mount ::API::GroupImport mount ::API::GroupLabels mount ::API::GroupMilestones mount ::API::GroupPackages @@ -295,7 +298,6 @@ module API mount ::API::MergeRequestApprovals mount ::API::MergeRequests mount ::API::Metrics::Dashboard::Annotations - mount ::API::Metrics::UserStarredDashboards mount ::API::Namespaces mount ::API::Notes mount ::API::NotificationSettings diff --git a/lib/api/ci/pipeline_schedules.rb b/lib/api/ci/pipeline_schedules.rb index ab2a76a05d3..afb3754f2ae 100644 --- a/lib/api/ci/pipeline_schedules.rb +++ b/lib/api/ci/pipeline_schedules.rb @@ -11,16 +11,24 @@ module API urgency :low params do - requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project', + documentation: { example: 18 } end resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get all pipeline schedules' do - success Entities::Ci::PipelineSchedule + success code: 200, model: Entities::Ci::PipelineSchedule + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] + is_array true end params do use :pagination optional :scope, type: String, values: %w[active inactive], - desc: 'The scope of pipeline schedules' + desc: 'The scope of pipeline schedules', + documentation: { example: 'active' } end # rubocop: disable CodeReuse/ActiveRecord get ':id/pipeline_schedules' do @@ -33,34 +41,51 @@ module API # rubocop: enable CodeReuse/ActiveRecord desc 'Get a single pipeline schedule' do - success Entities::Ci::PipelineScheduleDetails + success code: 200, model: Entities::Ci::PipelineScheduleDetails + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } end get ':id/pipeline_schedules/:pipeline_schedule_id' do present pipeline_schedule, with: Entities::Ci::PipelineScheduleDetails, user: current_user end desc 'Get all pipelines triggered from a pipeline schedule' do - success Entities::Ci::PipelineBasic + success code: 200, model: Entities::Ci::PipelineBasic + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] + is_array true end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule ID' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule ID', documentation: { example: 13 } end get ':id/pipeline_schedules/:pipeline_schedule_id/pipelines' do present paginate(pipeline_schedule.pipelines), with: Entities::Ci::PipelineBasic end desc 'Create a new pipeline schedule' do - success Entities::Ci::PipelineScheduleDetails + success code: 201, model: Entities::Ci::PipelineScheduleDetails + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :description, type: String, desc: 'The description of pipeline schedule' - requires :ref, type: String, desc: 'The branch/tag name will be triggered', allow_blank: false - requires :cron, type: String, desc: 'The cron' - optional :cron_timezone, type: String, default: 'UTC', desc: 'The timezone' - optional :active, type: Boolean, default: true, desc: 'The activation of pipeline schedule' + requires :description, type: String, desc: 'The description of pipeline schedule', documentation: { example: 'Test schedule pipeline' } + requires :ref, type: String, desc: 'The branch/tag name will be triggered', allow_blank: false, documentation: { example: 'develop' } + requires :cron, type: String, desc: 'The cron', documentation: { example: '* * * * *' } + optional :cron_timezone, type: String, default: 'UTC', desc: 'The timezone', documentation: { example: 'Asia/Tokyo' } + optional :active, type: Boolean, default: true, desc: 'The activation of pipeline schedule', documentation: { example: true } end post ':id/pipeline_schedules' do authorize! :create_pipeline_schedule, user_project @@ -77,15 +102,21 @@ module API end desc 'Edit a pipeline schedule' do - success Entities::Ci::PipelineScheduleDetails + success code: 200, model: Entities::Ci::PipelineScheduleDetails + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' - optional :description, type: String, desc: 'The description of pipeline schedule' - optional :ref, type: String, desc: 'The branch/tag name will be triggered' - optional :cron, type: String, desc: 'The cron' - optional :cron_timezone, type: String, desc: 'The timezone' - optional :active, type: Boolean, desc: 'The activation of pipeline schedule' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } + optional :description, type: String, desc: 'The description of pipeline schedule', documentation: { example: 'Test schedule pipeline' } + optional :ref, type: String, desc: 'The branch/tag name will be triggered', documentation: { example: 'develop' } + optional :cron, type: String, desc: 'The cron', documentation: { example: '* * * * *' } + optional :cron_timezone, type: String, desc: 'The timezone', documentation: { example: 'Asia/Tokyo' } + optional :active, type: Boolean, desc: 'The activation of pipeline schedule', documentation: { example: true } end put ':id/pipeline_schedules/:pipeline_schedule_id' do authorize! :update_pipeline_schedule, pipeline_schedule @@ -98,10 +129,16 @@ module API end desc 'Take ownership of a pipeline schedule' do - success Entities::Ci::PipelineScheduleDetails + success code: 201, model: Entities::Ci::PipelineScheduleDetails + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } end post ':id/pipeline_schedules/:pipeline_schedule_id/take_ownership' do authorize! :take_ownership_pipeline_schedule, pipeline_schedule @@ -114,10 +151,16 @@ module API end desc 'Delete a pipeline schedule' do - success Entities::Ci::PipelineScheduleDetails + success code: 204 + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' }, + { code: 412, message: 'Precondition Failed' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } end delete ':id/pipeline_schedules/:pipeline_schedule_id' do authorize! :admin_pipeline_schedule, pipeline_schedule @@ -127,9 +170,15 @@ module API desc 'Play a scheduled pipeline immediately' do detail 'This feature was added in GitLab 12.8' + success code: 201 + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } end post ':id/pipeline_schedules/:pipeline_schedule_id/play' do authorize! :play_pipeline_schedule, pipeline_schedule @@ -145,13 +194,20 @@ module API end desc 'Create a new pipeline schedule variable' do - success Entities::Ci::Variable + success code: 201, model: Entities::Ci::Variable + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' - requires :key, type: String, desc: 'The key of the variable' - requires :value, type: String, desc: 'The value of the variable' - optional :variable_type, type: String, values: ::Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } + requires :key, type: String, desc: 'The key of the variable', documentation: { example: 'NEW_VARIABLE' } + requires :value, type: String, desc: 'The value of the variable', documentation: { example: 'new value' } + optional :variable_type, type: String, values: ::Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var', + documentation: { default: 'env_var' } end post ':id/pipeline_schedules/:pipeline_schedule_id/variables' do authorize! :update_pipeline_schedule, pipeline_schedule @@ -166,13 +222,20 @@ module API end desc 'Edit a pipeline schedule variable' do - success Entities::Ci::Variable + success code: 200, model: Entities::Ci::Variable + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' - requires :key, type: String, desc: 'The key of the variable' - optional :value, type: String, desc: 'The value of the variable' - optional :variable_type, type: String, values: ::Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } + requires :key, type: String, desc: 'The key of the variable', documentation: { example: 'NEW_VARIABLE' } + optional :value, type: String, desc: 'The value of the variable', documentation: { example: 'new value' } + optional :variable_type, type: String, values: ::Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file', + documentation: { default: 'env_var' } end put ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do authorize! :update_pipeline_schedule, pipeline_schedule @@ -185,11 +248,16 @@ module API end desc 'Delete a pipeline schedule variable' do - success Entities::Ci::Variable + success code: 202, model: Entities::Ci::Variable + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] end params do - requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' - requires :key, type: String, desc: 'The key of the variable' + requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id', documentation: { example: 13 } + requires :key, type: String, desc: 'The key of the variable', documentation: { example: 'NEW_VARIABLE' } end delete ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do authorize! :admin_pipeline_schedule, pipeline_schedule diff --git a/lib/api/entities/ci/pipeline_schedule.rb b/lib/api/entities/ci/pipeline_schedule.rb index f1596b7d285..58496bded03 100644 --- a/lib/api/entities/ci/pipeline_schedule.rb +++ b/lib/api/entities/ci/pipeline_schedule.rb @@ -4,9 +4,15 @@ module API module Entities module Ci class PipelineSchedule < Grape::Entity - expose :id - expose :description, :ref, :cron, :cron_timezone, :next_run_at, :active - expose :created_at, :updated_at + expose :id, documentation: { type: 'integer', example: 13 } + expose :description, documentation: { type: 'string', example: 'Test schedule pipeline' } + expose :ref, documentation: { type: 'string', example: 'develop' } + expose :cron, documentation: { type: 'string', example: '* * * * *' } + expose :cron_timezone, documentation: { type: 'string', example: 'Asia/Tokyo' } + expose :next_run_at, documentation: { type: 'dateTime', example: '2017-05-19T13:41:00.000Z' } + expose :active, documentation: { type: 'boolean', example: true } + expose :created_at, documentation: { type: 'dateTime', example: '2017-05-19T13:31:08.849Z' } + expose :updated_at, documentation: { type: 'dateTime', example: '2017-05-19T13:40:17.727Z' } expose :owner, using: ::API::Entities::UserBasic end end diff --git a/lib/api/entities/metrics/user_starred_dashboard.rb b/lib/api/entities/metrics/user_starred_dashboard.rb index d774160e3ea..1d2a8a39547 100644 --- a/lib/api/entities/metrics/user_starred_dashboard.rb +++ b/lib/api/entities/metrics/user_starred_dashboard.rb @@ -4,7 +4,10 @@ module API module Entities module Metrics class UserStarredDashboard < Grape::Entity - expose :id, :dashboard_path, :user_id, :project_id + expose :id, documentation: { type: 'integer', example: 5 } + expose :dashboard_path, documentation: { type: 'string', example: 'config/prometheus/common_metrics.yml' } + expose :user_id, documentation: { type: 'integer', example: 1 } + expose :project_id, documentation: { type: 'integer', example: 20 } end end end diff --git a/lib/api/group_import.rb b/lib/api/group_import.rb index cef9b542c9e..609a7ed0ef0 100644 --- a/lib/api/group_import.rb +++ b/lib/api/group_import.rb @@ -32,6 +32,7 @@ module API resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Workhorse authorize the group import upload' do detail 'This feature was introduced in GitLab 12.8' + tags ['group_import'] end post 'import/authorize' do require_gitlab_workhorse! @@ -49,7 +50,15 @@ module API desc 'Create a new group import' do detail 'This feature was introduced in GitLab 12.8' - success Entities::Group + success code: 202 + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 400, message: 'Bad request' }, + { code: 503, message: 'Service unavailable' } + ] + consumes ['multipart/form-data'] + tags ['group_import'] end params do requires :path, type: String, desc: 'Group path' diff --git a/lib/api/metrics/user_starred_dashboards.rb b/lib/api/metrics/user_starred_dashboards.rb index 4d5396acccb..0a91e914d52 100644 --- a/lib/api/metrics/user_starred_dashboards.rb +++ b/lib/api/metrics/user_starred_dashboards.rb @@ -6,14 +6,22 @@ module API feature_category :metrics urgency :low + USER_STARRED_DASHBOARDS_TAGS = %w[user_starred_dashboards].freeze + resource :projects do - desc 'Marks selected metrics dashboard as starred' do + desc 'Add a star to a dashboard' do + detail 'Marks selected metrics dashboard as starred. Introduced in GitLab 13.0.' success Entities::Metrics::UserStarredDashboard + failure [ + { code: 400, message: 'Bad request' }, + { code: 404, message: 'Not found' } + ] + tags USER_STARRED_DASHBOARDS_TAGS end params do requires :dashboard_path, type: String, allow_blank: false, coerce_with: ->(val) { CGI.unescape(val) }, - desc: 'Url encoded path to a file defining the dashboard to which the star should be added' + desc: 'URL-encoded path to file defining the dashboard which should be marked as favorite' end post ':id/metrics/user_starred_dashboards' do @@ -26,7 +34,15 @@ module API end end - desc 'Remove star from selected metrics dashboard' + desc 'Remove a star from a dashboard' do + detail 'Remove star from selected metrics dashboard. Introduced in GitLab 13.0.' + success code: 200 + failure [ + { code: 400, message: 'Bad request' }, + { code: 404, message: 'Not found' } + ] + tags USER_STARRED_DASHBOARDS_TAGS + end params do optional :dashboard_path, type: String, allow_blank: false, coerce_with: ->(val) { CGI.unescape(val) }, diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb index 2ee7b73c74c..c5add42decc 100644 --- a/lib/api/project_container_repositories.rb +++ b/lib/api/project_container_repositories.rb @@ -47,8 +47,12 @@ module API end delete ':id/registry/repositories/:repository_id', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do authorize_admin_container_image! + repository.delete_scheduled! + + unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker) + DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) # rubocop:disable CodeReuse/Worker + end - DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) # rubocop:disable CodeReuse/Worker track_package_event('delete_repository', :container, user: current_user, project: user_project, namespace: user_project.namespace) status :accepted diff --git a/lib/api/terraform/modules/v1/packages.rb b/lib/api/terraform/modules/v1/packages.rb index 79f98616073..5624784228e 100644 --- a/lib/api/terraform/modules/v1/packages.rb +++ b/lib/api/terraform/modules/v1/packages.rb @@ -21,7 +21,7 @@ module API module_version: SEMVER_REGEX }.freeze - feature_category :infrastructure_as_code + feature_category :package_registry urgency :low after_validation do diff --git a/lib/gitlab/background_migration/recount_epic_cache_counts.rb b/lib/gitlab/background_migration/recount_epic_cache_counts.rb new file mode 100644 index 00000000000..42f84a33a5a --- /dev/null +++ b/lib/gitlab/background_migration/recount_epic_cache_counts.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # rubocop: disable Style/Documentation + class RecountEpicCacheCounts < Gitlab::BackgroundMigration::BatchedMigrationJob + def perform; end + end + # rubocop: enable Style/Documentation + end +end + +# rubocop: disable Layout/LineLength +# we just want to re-enqueue the previous BackfillEpicCacheCounts migration, +# because it's a EE-only migation and it's a module, we just prepend new +# RecountEpicCacheCounts with existing batched migration module (which is same in both cases) +Gitlab::BackgroundMigration::RecountEpicCacheCounts.prepend_mod_with('Gitlab::BackgroundMigration::BackfillEpicCacheCounts') +# rubocop: enable Layout/LineLength diff --git a/lib/gitlab/ci/build/rules/rule/clause/exists.rb b/lib/gitlab/ci/build/rules/rule/clause/exists.rb index 5617d153bc8..c55615bb83b 100644 --- a/lib/gitlab/ci/build/rules/rule/clause/exists.rb +++ b/lib/gitlab/ci/build/rules/rule/clause/exists.rb @@ -23,12 +23,8 @@ module Gitlab private def separate_globs(context) - if ::Feature.enabled?(:ci_variable_expansion_in_rules_exists, context.project) - expanded_globs = expand_globs(context) - expanded_globs.partition(&method(:exact_glob?)) - else - @globs.partition(&method(:exact_glob?)) - end + expanded_globs = expand_globs(context) + expanded_globs.partition(&method(:exact_glob?)) end def expand_globs(context) diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb index 76d4a05bf30..5ec04b4889e 100644 --- a/lib/gitlab/ci/pipeline/chain/command.rb +++ b/lib/gitlab/ci/pipeline/chain/command.rb @@ -117,7 +117,7 @@ module Gitlab logger.observe(:pipeline_size_count, pipeline.total_size) metrics.pipeline_size_histogram - .observe({ source: pipeline.source.to_s }, pipeline.total_size) + .observe({ source: pipeline.source.to_s, plan: project.actual_plan_name }, pipeline.total_size) end def observe_jobs_count_in_alive_pipelines diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake index b58d9473794..3d3bdc560ac 100644 --- a/lib/tasks/gitlab/assets.rake +++ b/lib/tasks/gitlab/assets.rake @@ -84,7 +84,7 @@ namespace :gitlab do puts "Assets SHA256 for `HEAD`: #{Tasks::Gitlab::Assets.head_assets_sha256.inspect}" if Tasks::Gitlab::Assets.head_assets_sha256 != Tasks::Gitlab::Assets.master_assets_sha256 - FileUtils.rm_r(Tasks::Gitlab::Assets::PUBLIC_ASSETS_DIR) if Dir.exist?(Tasks::Gitlab::Assets::PUBLIC_ASSETS_DIR) + FileUtils.rm_rf([Tasks::Gitlab::Assets::PUBLIC_ASSETS_DIR] + Dir.glob('app/assets/javascripts/locale/**/app.js')) # gettext:po_to_json needs to run before rake:assets:precompile because # app/assets/javascripts/locale/**/app.js are pre-compiled by Sprockets diff --git a/locale/gitlab.pot b/locale/gitlab.pot index bba68acf262..86179ee79dd 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -22794,9 +22794,6 @@ msgstr "" msgid "Issue|Title" msgstr "" -msgid "It is not possible to %{action} files that are stored in LFS using the web interface" -msgstr "" - msgid "It looks like you have some draft commits in this branch." msgstr "" @@ -45647,6 +45644,9 @@ msgstr "" msgid "Webhooks|Merge request events" msgstr "" +msgid "Webhooks|Must match part of URL" +msgstr "" + msgid "Webhooks|Pipeline events" msgstr "" @@ -46632,9 +46632,6 @@ msgstr "" msgid "You can now submit a merge request to get this change into the original project." msgstr "" -msgid "You can only %{action} files when you are on a branch" -msgstr "" - msgid "You can only add up to %{max_contacts} contacts at one time" msgstr "" diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index be27cf06b1b..7094bfcdf1c 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -346,22 +346,23 @@ EOF if [ -n "${REVIEW_APPS_EE_LICENSE_FILE}" ]; then HELM_CMD=$(cat << EOF ${HELM_CMD} \ - --set global.gitlab.license.secret="shared-gitlab-license" + --set global.gitlab.license.secret="shared-gitlab-license" EOF ) fi HELM_CMD=$(cat << EOF ${HELM_CMD} \ - --version="${CI_PIPELINE_ID}-${CI_JOB_ID}" \ - -f "${base_config_file}" \ - -v "${HELM_LOG_VERBOSITY:-1}" \ - "${release}" "gitlab-${GITLAB_HELM_CHART_REF}" + --version="${CI_PIPELINE_ID}-${CI_JOB_ID}" \ + -f "${base_config_file}" \ + -v "${HELM_LOG_VERBOSITY:-1}" \ + "${release}" "gitlab-${GITLAB_HELM_CHART_REF}" EOF ) + # Pretty-print the command for display echoinfo "Deploying with:" - echoinfo "${HELM_CMD}" + echo "${HELM_CMD}" | sed 's/ /\n\t/g' run_timed_command "eval \"${HELM_CMD}\"" } diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb index e057c56fc53..f4f5c182850 100644 --- a/spec/controllers/projects/registry/repositories_controller_spec.rb +++ b/spec/controllers/projects/registry/repositories_controller_spec.rb @@ -103,10 +103,11 @@ RSpec.describe Projects::Registry::RepositoriesController do stub_container_registry_tags(repository: :any, tags: []) end - it 'schedules a job to delete a repository' do - expect(DeleteContainerRepositoryWorker).to receive(:perform_async).with(user.id, repository.id) + it 'marks the repository as delete_scheduled' do + expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async).with(user.id, repository.id) - delete_repository(repository) + expect { delete_repository(repository) } + .to change { repository.reload.status }.from(nil).to('delete_scheduled') expect(repository.reload).to be_delete_scheduled expect(response).to have_gitlab_http_status(:no_content) @@ -119,6 +120,22 @@ RSpec.describe Projects::Registry::RepositoriesController do expect_snowplow_event(category: anything, action: 'delete_repository') end + + context 'with container_registry_delete_repository_with_cron_worker disabled' do + before do + stub_feature_flags(container_registry_delete_repository_with_cron_worker: false) + end + + it 'schedules a job to delete a repository' do + expect(DeleteContainerRepositoryWorker).to receive(:perform_async).with(user.id, repository.id) + + expect { delete_repository(repository) } + .to change { repository.reload.status }.from(nil).to('delete_scheduled') + + expect(repository.reload).to be_delete_scheduled + expect(response).to have_gitlab_http_status(:no_content) + end + end end end end diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb index 54685441300..e99af734c43 100644 --- a/spec/features/projects/container_registry_spec.rb +++ b/spec/features/projects/container_registry_spec.rb @@ -56,10 +56,11 @@ RSpec.describe 'Container Registry', :js do expect(page).to have_content 'my/image' end - it 'user removes entire container repository', :sidekiq_might_not_need_inline do + it 'user removes entire container repository' do visit_container_registry - expect_any_instance_of(ContainerRepository).to receive(:delete_tags!).and_return(true) + expect_any_instance_of(ContainerRepository).to receive(:delete_scheduled!).and_call_original + expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async) find('[title="Remove repository"]').click expect(find('.modal .modal-title')).to have_content _('Remove repository') diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js index 203a00577f1..0eb08f0cf55 100644 --- a/spec/frontend/editor/schema/ci/ci_schema_spec.js +++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js @@ -46,11 +46,7 @@ import ProjectPathIncludeInvalidVariableYaml from './yaml_tests/negative_tests/p import ProjectPathIncludeLeadSlashYaml from './yaml_tests/negative_tests/project_path/include/leading_slash.yml'; import ProjectPathIncludeNoSlashYaml from './yaml_tests/negative_tests/project_path/include/no_slash.yml'; import ProjectPathIncludeTailSlashYaml from './yaml_tests/negative_tests/project_path/include/tailing_slash.yml'; -import ProjectPathTriggerIncludeEmptyYaml from './yaml_tests/negative_tests/project_path/trigger/include/empty.yml'; -import ProjectPathTriggerIncludeInvalidVariableYaml from './yaml_tests/negative_tests/project_path/trigger/include/invalid_variable.yml'; -import ProjectPathTriggerIncludeLeadSlashYaml from './yaml_tests/negative_tests/project_path/trigger/include/leading_slash.yml'; -import ProjectPathTriggerIncludeNoSlashYaml from './yaml_tests/negative_tests/project_path/trigger/include/no_slash.yml'; -import ProjectPathTriggerIncludeTailSlashYaml from './yaml_tests/negative_tests/project_path/trigger/include/tailing_slash.yml'; +import ProjectPathTriggerIncludeYaml from './yaml_tests/negative_tests/project_path/trigger/trigger_include.yml'; import ProjectPathTriggerMinimalEmptyYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/empty.yml'; import ProjectPathTriggerMinimalInvalidVariableYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/invalid_variable.yml'; import ProjectPathTriggerMinimalLeadSlashYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/leading_slash.yml'; @@ -80,7 +76,7 @@ const ajv = new Ajv({ ajv.addKeyword('markdownDescription'); AjvFormats(ajv); -const schema = ajv.compile(CiSchema); +const ajvSchema = ajv.compile(CiSchema); describe('positive tests', () => { it.each( @@ -108,7 +104,11 @@ describe('positive tests', () => { ProjectPathYaml, }), )('schema validates %s', (_, input) => { - expect(input).toValidateJsonSchema(schema); + // We construct a new "JSON" from each main key that is inside a + // file which allow us to make sure each blob is valid. + Object.keys(input).forEach((key) => { + expect({ [key]: input[key] }).toValidateJsonSchema(ajvSchema); + }); }); }); @@ -145,11 +145,7 @@ describe('negative tests', () => { ProjectPathIncludeLeadSlashYaml, ProjectPathIncludeNoSlashYaml, ProjectPathIncludeTailSlashYaml, - ProjectPathTriggerIncludeEmptyYaml, - ProjectPathTriggerIncludeInvalidVariableYaml, - ProjectPathTriggerIncludeLeadSlashYaml, - ProjectPathTriggerIncludeNoSlashYaml, - ProjectPathTriggerIncludeTailSlashYaml, + ProjectPathTriggerIncludeYaml, ProjectPathTriggerMinimalEmptyYaml, ProjectPathTriggerMinimalInvalidVariableYaml, ProjectPathTriggerMinimalLeadSlashYaml, @@ -162,6 +158,10 @@ describe('negative tests', () => { ProjectPathTriggerProjectTailSlashYaml, }), )('schema validates %s', (_, input) => { - expect(input).not.toValidateJsonSchema(schema); + // We construct a new "JSON" from each main key that is inside a + // file which allow us to make sure each blob is invalid. + Object.keys(input).forEach((key) => { + expect({ [key]: input[key] }).not.toValidateJsonSchema(ajvSchema); + }); }); }); diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml index 1e16bb55405..6afd8baa0e8 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml @@ -1,6 +1,3 @@ -stages: - - prepare - # invalid trigger:include trigger missing file property: stage: prepare diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/empty.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/empty.yml deleted file mode 100644 index ee2bb3e8ace..00000000000 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/empty.yml +++ /dev/null @@ -1,5 +0,0 @@ -trigger-include: - trigger: - include: - - file: '/path/to/child-pipeline.yml' - project: '' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/invalid_variable.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/invalid_variable.yml deleted file mode 100644 index 770305be0dc..00000000000 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/invalid_variable.yml +++ /dev/null @@ -1,5 +0,0 @@ -trigger-include: - trigger: - include: - - file: '/path/to/child-pipeline.yml' - project: 'slug#' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/leading_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/leading_slash.yml deleted file mode 100644 index 82fd77cf0d3..00000000000 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/leading_slash.yml +++ /dev/null @@ -1,5 +0,0 @@ -trigger-include: - trigger: - include: - - file: '/path/to/child-pipeline.yml' - project: '/slug' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/no_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/no_slash.yml deleted file mode 100644 index f4ea59c7945..00000000000 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/no_slash.yml +++ /dev/null @@ -1,5 +0,0 @@ -trigger-include: - trigger: - include: - - file: '/path/to/child-pipeline.yml' - project: 'slug' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/tailing_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/tailing_slash.yml deleted file mode 100644 index a0195c03352..00000000000 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/tailing_slash.yml +++ /dev/null @@ -1,5 +0,0 @@ -trigger-include: - trigger: - include: - - file: '/path/to/child-pipeline.yml' - project: 'slug/' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/trigger_include.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/trigger_include.yml new file mode 100644 index 00000000000..6527db04a62 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/trigger_include.yml @@ -0,0 +1,29 @@ +trigger-include-empty: + trigger: + include: + - file: '/path/to/child-pipeline.yml' + project: '' + +trigger-include-invalid: + trigger: + include: + - file: '/path/to/child-pipeline.yml' + project: 'slug#' + +trigger-include-leading-slash: + trigger: + include: + - file: '/path/to/child-pipeline.yml' + project: '/slug' + +trigger-include-no-slash: + trigger: + include: + - file: '/path/to/child-pipeline.yml' + project: 'slug' + +trigger-include-trailing-slash: + trigger: + include: + - file: '/path/to/child-pipeline.yml' + project: 'slug/' diff --git a/spec/frontend/webhooks/components/form_url_app_spec.js b/spec/frontend/webhooks/components/form_url_app_spec.js index 93f468af1e3..45a39d2dd58 100644 --- a/spec/frontend/webhooks/components/form_url_app_spec.js +++ b/spec/frontend/webhooks/components/form_url_app_spec.js @@ -190,16 +190,17 @@ describe('FormUrlApp', () => { }); it.each` - key | value | keyInvalidFeedback | valueInvalidFeedback | scrollToElementCalls - ${null} | ${null} | ${inputRequiredText} | ${inputRequiredText} | ${1} - ${null} | ${'value'} | ${inputRequiredText} | ${null} | ${1} - ${'key'} | ${null} | ${null} | ${inputRequiredText} | ${1} - ${'key'} | ${'value'} | ${null} | ${null} | ${0} + key | value | keyInvalidFeedback | valueInvalidFeedback | scrollToElementCalls + ${null} | ${null} | ${inputRequiredText} | ${inputRequiredText} | ${1} + ${null} | ${'random'} | ${inputRequiredText} | ${FormUrlApp.i18n.valuePartOfUrl} | ${1} + ${null} | ${'secret'} | ${inputRequiredText} | ${null} | ${1} + ${'key'} | ${null} | ${null} | ${inputRequiredText} | ${1} + ${'key'} | ${'secret'} | ${null} | ${null} | ${0} `( 'when key is `$key` and value is `$value`', async ({ key, value, keyInvalidFeedback, valueInvalidFeedback, scrollToElementCalls }) => { createComponent({ - props: { initialUrl: 'url' }, + props: { initialUrl: 'http://example.com?password=secret' }, }); findRadioGroup().vm.$emit('input', true); await nextTick(); diff --git a/spec/graphql/mutations/container_repositories/destroy_spec.rb b/spec/graphql/mutations/container_repositories/destroy_spec.rb index 97da7846339..9f3ff8da80b 100644 --- a/spec/graphql/mutations/container_repositories/destroy_spec.rb +++ b/spec/graphql/mutations/container_repositories/destroy_spec.rb @@ -20,11 +20,10 @@ RSpec.describe Mutations::ContainerRepositories::Destroy do end shared_examples 'destroying the container repository' do - it 'destroys the container repistory' do + it 'marks the repository as delete_scheduled' do expect(::Packages::CreateEventService) .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original - expect(DeleteContainerRepositoryWorker) - .to receive(:perform_async).with(user.id, container_repository.id) + expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async) expect { subject }.to change { ::Packages::Event.count }.by(1) expect(container_repository.reload.delete_scheduled?).to be true @@ -56,6 +55,23 @@ RSpec.describe Mutations::ContainerRepositories::Destroy do it_behaves_like params[:shared_examples_name] end + + context 'with container_registry_delete_repository_with_cron_worker disabled' do + before do + project.add_maintainer(user) + stub_feature_flags(container_registry_delete_repository_with_cron_worker: false) + end + + it 'enqueues a removal job' do + expect(::Packages::CreateEventService) + .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original + expect(DeleteContainerRepositoryWorker) + .to receive(:perform_async).with(user.id, container_repository.id) + + expect { subject }.to change { ::Packages::Event.count }.by(1) + expect(container_repository.reload.delete_scheduled?).to be true + end + end end end end diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb index c31d1c1fbf4..647653f8e9e 100644 --- a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb +++ b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb @@ -24,24 +24,14 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do allow(context).to receive(:variables_hash).and_return(variables_hash) end - context 'when the ci_variables_rules_exists FF is disabled' do - before do - stub_feature_flags(ci_variable_expansion_in_rules_exists: false) - end - - it { is_expected.to be_falsey } + context 'when the context has the specified variables' do + it { is_expected.to be_truthy } end - context 'when the ci_variables_rules_exists FF is enabled' do - context 'when the context has the specified variables' do - it { is_expected.to be_truthy } - end + context 'when variable expansion does not match' do + let(:variables_hash) { {} } - context 'when variable expansion does not match' do - let(:variables_hash) { {} } - - it { is_expected.to be_falsey } - end + it { is_expected.to be_falsey } end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb index 6e8b6e40928..9126c6dab21 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb @@ -409,4 +409,21 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do end end end + + describe '#observe_pipeline_size' do + let(:command) { described_class.new(project: project) } + + let(:pipeline) { instance_double(Ci::Pipeline, total_size: 5, project: project, source: "schedule") } + + it 'logs the pipeline total size to histogram' do + histogram = instance_double(Prometheus::Client::Histogram) + + expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_size_histogram) + .and_return(histogram) + expect(histogram).to receive(:observe) + .with({ source: pipeline.source, plan: project.actual_plan_name }, pipeline.total_size) + + command.observe_pipeline_size(pipeline) + end + end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb index c69aa661b05..31086f6ae4a 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb @@ -80,7 +80,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Sequence do subject.build! expect(histogram).to have_received(:observe) - .with({ source: 'push' }, 0) + .with({ source: 'push', plan: project.actual_plan_name }, 0) end describe 'active jobs by pipeline plan histogram' do diff --git a/spec/migrations/recount_epic_cache_counts_spec.rb b/spec/migrations/recount_epic_cache_counts_spec.rb new file mode 100644 index 00000000000..56aa96cb40c --- /dev/null +++ b/spec/migrations/recount_epic_cache_counts_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe RecountEpicCacheCounts, :migration do + let(:migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules a batched background migration' do + migrate! + + expect(migration).to have_scheduled_batched_migration( + table_name: :epics, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + max_batch_size: described_class::MAX_BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb index 00588b5df5f..9af53bae204 100644 --- a/spec/models/container_repository_spec.rb +++ b/spec/models/container_repository_spec.rb @@ -1296,6 +1296,16 @@ RSpec.describe ContainerRepository, :aggregate_failures do it { is_expected.to contain_exactly(repository1, repository3) } end + describe '.with_stale_delete_at' do + let_it_be(:repository1) { create(:container_repository, delete_started_at: 1.day.ago) } + let_it_be(:repository2) { create(:container_repository, delete_started_at: 25.minutes.ago) } + let_it_be(:repository3) { create(:container_repository, delete_started_at: 1.week.ago) } + + subject { described_class.with_stale_delete_at(27.minutes.ago) } + + it { is_expected.to contain_exactly(repository1, repository3) } + end + describe '.waiting_for_cleanup' do let_it_be(:repository_cleanup_scheduled) { create(:container_repository, :cleanup_scheduled) } let_it_be(:repository_cleanup_unfinished) { create(:container_repository, :cleanup_unfinished) } diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb index c4121cfed42..5a27d39ecbc 100644 --- a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb @@ -33,11 +33,11 @@ RSpec.describe 'Destroying a container repository' do end shared_examples 'destroying the container repository' do - it 'destroy the container repository' do + it 'marks the container repository as delete_scheduled' do expect(::Packages::CreateEventService) .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original expect(DeleteContainerRepositoryWorker) - .to receive(:perform_async).with(user.id, container_repository.id) + .not_to receive(:perform_async) expect { subject }.to change { ::Packages::Event.count }.by(1) @@ -80,6 +80,25 @@ RSpec.describe 'Destroying a container repository' do it_behaves_like params[:shared_examples_name] end + + context 'with container_registry_delete_repository_with_cron_worker disabled' do + before do + project.add_maintainer(user) + stub_feature_flags(container_registry_delete_repository_with_cron_worker: false) + end + + it 'enqueues a removal job' do + expect(::Packages::CreateEventService) + .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original + expect(DeleteContainerRepositoryWorker) + .to receive(:perform_async).with(user.id, container_repository.id) + + expect { subject }.to change { ::Packages::Event.count }.by(1) + + expect(container_repository_mutation_response).to match_schema('graphql/container_repository') + expect(container_repository_mutation_response['status']).to eq('DELETE_SCHEDULED') + end + end end context 'with invalid id' do diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb index 04981afc151..c07d2e11363 100644 --- a/spec/requests/api/invitations_spec.rb +++ b/spec/requests/api/invitations_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe API::Invitations do let_it_be(:maintainer) { create(:user, username: 'maintainer_user') } + let_it_be(:maintainer2) { create(:user, username: 'user-with-maintainer-role') } let_it_be(:developer) { create(:user) } let_it_be(:access_requester) { create(:user) } let_it_be(:stranger) { create(:user) } @@ -31,8 +32,8 @@ RSpec.describe API::Invitations do api("/#{source.model_name.plural}/#{source.id}/invitations", user) end - def invite_member_by_email(source, source_type, email, created_by) - create(:"#{source_type}_member", invite_token: '123', invite_email: email, source: source, user: nil, created_by: created_by) + def invite_member_by_email(source, source_type, email, created_by, access_level: :developer) + create(:"#{source_type}_member", access_level, invite_token: '123', invite_email: email, source: source, user: nil, created_by: created_by) end shared_examples 'POST /:source_type/:id/invitations' do |source_type| @@ -44,15 +45,42 @@ RSpec.describe API::Invitations do end end - context 'when authenticated as a non-member or member with insufficient rights' do - %i[access_requester stranger developer].each do |type| - context "as a #{type}" do - it 'returns 403' do - user = public_send(type) + context 'when authenticated as a non-member or member with insufficient membership management rights' do + context 'when the user does not have rights to manage members' do + %i[access_requester stranger developer].each do |type| + context "as a #{type}" do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + post invitations_url(source, public_send(type)), + params: { email: email, access_level: Member::MAINTAINER } + end + end + end + end + end - post invitations_url(source, user), params: { email: email, access_level: Member::MAINTAINER } + context 'when the user has the rights to manage members but tries to manage members with a higher access level' do + let(:maintainer) { maintainer2 } + + before do + source.add_maintainer(maintainer) + end + + context 'when an invitee is added as OWNER' do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + post invitations_url(source, maintainer), + params: { email: email, access_level: Member::OWNER } + end + end + end - expect(response).to have_gitlab_http_status(:forbidden) + context 'when an access_requester is added as OWNER' do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + post invitations_url(source, maintainer), + params: { user_id: access_requester.email, access_level: Member::OWNER } + end end end end @@ -503,14 +531,12 @@ RSpec.describe API::Invitations do end end - %i[developer access_requester stranger].each do |type| - context "when authenticated as a #{type}" do - it 'returns 403' do - user = public_send(type) - - get invitations_url(source, user) - - expect(response).to have_gitlab_http_status(:forbidden) + %i[access_requester stranger developer].each do |type| + context "as a #{type}" do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + get invitations_url(source, public_send(type)) + end end end end @@ -581,14 +607,14 @@ RSpec.describe API::Invitations do end context 'when authenticated as a non-member or member with insufficient rights' do - %i[access_requester stranger].each do |type| - context "as a #{type}" do - it 'returns 403' do - user = public_send(type) - - delete invite_api(source, user, invite.invite_email) - - expect(response).to have_gitlab_http_status(:forbidden) + context 'when the user does not have rights to manage members' do + %i[access_requester stranger].each do |type| + context "as a #{type}" do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + delete invite_api(source, public_send(type), invite.invite_email) + end + end end end end @@ -612,6 +638,23 @@ RSpec.describe API::Invitations do expect(response).to have_gitlab_http_status(:no_content) end.to change { source.members.count }.by(-1) end + + context 'when MAINTAINER tries to remove invitation of an OWNER' do + let_it_be(:maintainer) { maintainer2 } + let!(:owner_invite) do + invite_member_by_email(source, source_type, 'owner@owner.com', developer, access_level: :owner) + end + + before do + source.add_maintainer(maintainer) + end + + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + delete invite_api(source, maintainer, owner_invite.invite_email) + end + end + end end it 'returns 404 if member does not exist' do @@ -659,14 +702,15 @@ RSpec.describe API::Invitations do end context 'when authenticated as a non-member or member with insufficient rights' do - %i[access_requester stranger].each do |type| - context "as a #{type}" do - it 'returns 403' do - user = public_send(type) - - put update_api(source, user, invite.invite_email), params: { access_level: Member::MAINTAINER } - - expect(response).to have_gitlab_http_status(:forbidden) + context 'when the user does not have rights to manage members' do + %i[access_requester stranger].each do |type| + context "as a #{type}" do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + put update_api(source, public_send(type), invite.invite_email), + params: { access_level: Member::MAINTAINER } + end + end end end end @@ -681,6 +725,21 @@ RSpec.describe API::Invitations do expect(json_response['access_level']).to eq(Member::MAINTAINER) expect(invite.reload.access_level).to eq(Member::MAINTAINER) end + + context 'MAINTAINER tries to update access level to OWNER' do + let_it_be(:maintainer) { maintainer2 } + + before do + source.add_maintainer(maintainer) + end + + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + put update_api(source, maintainer, invite.invite_email), + params: { access_level: Member::OWNER } + end + end + end end it 'returns 409 if member does not exist' do diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index 9df9c75b020..69be574f38a 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -270,39 +270,42 @@ RSpec.describe API::Members do end end - context 'when authenticated as a non-member or member with insufficient rights' do - %i[access_requester stranger developer].each do |type| - context "as a #{type}" do - it 'returns 403' do - user = public_send(type) - post api("/#{source_type.pluralize}/#{source.id}/members", user), - params: { user_id: access_requester.id, access_level: Member::MAINTAINER } - - expect(response).to have_gitlab_http_status(:forbidden) + context 'when authenticated as a non-member or member with insufficient membership management rights' do + context 'when the user does not have rights to manage members' do + %i[access_requester stranger developer].each do |type| + context "as a #{type}" do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + post api("/#{source_type.pluralize}/#{source.id}/members", public_send(type)), + params: { user_id: access_requester.id, access_level: Member::MAINTAINER } + end + end end end + end - context 'adding a member of higher access level' do - before do - # the other 'maintainer' is in fact an owner of the group! - source.add_maintainer(maintainer2) - end + context 'when the user has the rights to manage members but tries to manage members with a higher access level' do + # the other 'maintainer' is in fact an owner of the group! + let(:maintainer) { maintainer2 } - context 'when an access requester' do - it 'is not successful' do - post api("/#{source_type.pluralize}/#{source.id}/members", maintainer2), - params: { user_id: access_requester.id, access_level: Member::OWNER } + before do + source.add_maintainer(maintainer) + end - expect(response).to have_gitlab_http_status(:forbidden) + context 'when an access requester is added as OWNER' do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + post api("/#{source_type.pluralize}/#{source.id}/members", maintainer), + params: { user_id: access_requester.id, access_level: Member::OWNER } end end + end - context 'when a totally new user' do - it 'is not successful' do - post api("/#{source_type.pluralize}/#{source.id}/members", maintainer2), + context 'when a totally new user is added as OWNER' do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + post api("/#{source_type.pluralize}/#{source.id}/members", maintainer), params: { user_id: stranger.id, access_level: Member::OWNER } - - expect(response).to have_gitlab_http_status(:forbidden) end end end @@ -561,27 +564,31 @@ RSpec.describe API::Members do context 'when authenticated as a non-member or member with insufficient rights' do %i[access_requester stranger developer].each do |type| context "as a #{type}" do - it 'returns 403' do - user = public_send(type) - put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user), - params: { access_level: Member::MAINTAINER } - - expect(response).to have_gitlab_http_status(:forbidden) + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", public_send(type)), + params: { access_level: Member::MAINTAINER } + end end end end context 'as a maintainer updating a member to one with higher access level than themselves' do + # the other 'maintainer' is in fact an owner of the group! + let(:maintainer) { maintainer2 } + before do # the other 'maintainer' is in fact an owner of the group! source.add_maintainer(maintainer2) end - it 'returns 403' do - put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer2), - params: { access_level: Member::OWNER } - - expect(response).to have_gitlab_http_status(:forbidden) + context 'updating a member to OWNER' do + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer), + params: { access_level: Member::OWNER } + end + end end end end @@ -600,18 +607,19 @@ RSpec.describe API::Members do context 'when updating a member with higher access level' do let(:owner) { create(:user) } + # the other 'maintainer' is in fact an owner of the group! + let(:maintainer) { maintainer2 } before do source.add_owner(owner) - # the other 'maintainer' is in fact an owner of the group! - source.add_maintainer(maintainer2) + source.add_maintainer(maintainer) end - it 'returns 403' do - put api("/#{source_type.pluralize}/#{source.id}/members/#{owner.id}", maintainer2), - params: { access_level: Member::DEVELOPER } - - expect(response).to have_gitlab_http_status(:forbidden) + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + put api("/#{source_type.pluralize}/#{source.id}/members/#{owner.id}", maintainer), + params: { access_level: Member::OWNER } + end end end end @@ -676,11 +684,10 @@ RSpec.describe API::Members do context 'when authenticated as a non-member or member with insufficient rights' do %i[access_requester stranger].each do |type| context "as a #{type}" do - it 'returns 403' do - user = public_send(type) - delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user) - - expect(response).to have_gitlab_http_status(:forbidden) + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", public_send(type)) + end end end end @@ -709,18 +716,18 @@ RSpec.describe API::Members do context 'when attempting to delete a member with higher access level' do let(:owner) { create(:user) } + # the other 'maintainer' is in fact an owner of the group! + let(:maintainer) { maintainer2 } before do source.add_owner(owner) - # the other 'maintainer' is in fact an owner of the group! - source.add_maintainer(maintainer2) + source.add_maintainer(maintainer) end - it 'returns 403' do - delete api("/#{source_type.pluralize}/#{source.id}/members/#{owner.id}", maintainer2), - params: { access_level: Member::DEVELOPER } - - expect(response).to have_gitlab_http_status(:forbidden) + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + delete api("/#{source_type.pluralize}/#{source.id}/members/#{owner.id}", maintainer) + end end end @@ -799,11 +806,11 @@ RSpec.describe API::Members do end context 'adding owner to project' do - it 'returns 403' do - post api("/projects/#{project.id}/members", maintainer), - params: { user_id: stranger.id, access_level: Member::OWNER } - - expect(response).to have_gitlab_http_status(:forbidden) + it_behaves_like 'a 403 response when user does not have rights to manage members of a specific access level' do + let(:route) do + post api("/projects/#{project.id}/members", maintainer), + params: { user_id: access_requester.id, access_level: Member::OWNER } + end end end diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index 506e60d19a6..52ec06d76a9 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -138,14 +138,26 @@ RSpec.describe API::ProjectContainerRepositories do context 'for maintainer' do let(:api_user) { maintainer } - it 'schedules removal of repository' do - expect(DeleteContainerRepositoryWorker).to receive(:perform_async) - .with(maintainer.id, root_repository.id) - - subject + it 'marks the repository as delete_scheduled' do + expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async) + expect { subject }.to change { root_repository.reload.status }.from(nil).to('delete_scheduled') expect(response).to have_gitlab_http_status(:accepted) end + + context 'with container_registry_delete_repository_with_cron_worker disabled' do + before do + stub_feature_flags(container_registry_delete_repository_with_cron_worker: false) + end + + it 'schedules removal of repository' do + expect(DeleteContainerRepositoryWorker).to receive(:perform_async) + .with(maintainer.id, root_repository.id) + expect { subject }.to change { root_repository.reload.status }.from(nil).to('delete_scheduled') + + expect(response).to have_gitlab_http_status(:accepted) + end + end end end end diff --git a/spec/requests/projects/ml/experiments_controller_spec.rb b/spec/requests/projects/ml/experiments_controller_spec.rb index 7be5899fcbe..67a2fe47dc8 100644 --- a/spec/requests/projects/ml/experiments_controller_spec.rb +++ b/spec/requests/projects/ml/experiments_controller_spec.rb @@ -3,11 +3,14 @@ require 'spec_helper' RSpec.describe Projects::Ml::ExperimentsController do - let_it_be(:project) { create(:project, :repository) } - let_it_be(:user) { project.first_owner } - let_it_be(:basic_params) { { namespace_id: project.namespace.to_param, project_id: project } } + let_it_be(:project_with_feature) { create(:project, :repository) } + let_it_be(:user) { project_with_feature.first_owner } + let_it_be(:project_without_feature) do + create(:project, :repository).tap { |p| p.add_developer(user) } + end + let_it_be(:experiment) do - create(:ml_experiments, project: project, user: user).tap do |e| + create(:ml_experiments, project: project_with_feature, user: user).tap do |e| create(:ml_candidates, experiment: e, user: user) end end @@ -15,9 +18,12 @@ RSpec.describe Projects::Ml::ExperimentsController do let(:params) { basic_params } let(:ff_value) { true } let(:threshold) { 4 } + let(:project) { project_with_feature } + let(:basic_params) { { namespace_id: project.namespace.to_param, project_id: project } } before do - stub_feature_flags(ml_experiment_tracking: ff_value) + stub_feature_flags(ml_experiment_tracking: false) + stub_feature_flags(ml_experiment_tracking: project_with_feature) if ff_value sign_in(user) end @@ -49,6 +55,14 @@ RSpec.describe Projects::Ml::ExperimentsController do expect { list_experiments }.not_to exceed_all_query_limit(control_count).with_threshold(threshold) end + context 'when :ml_experiment_tracking is disabled for the project' do + let(:project) { project_without_feature } + + it 'responds with a 404' do + expect(response).to have_gitlab_http_status(:not_found) + end + end + it_behaves_like '404 if feature flag disabled' end diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 360cf1bbf63..67c13649c6f 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -135,7 +135,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes execute_service expect(histogram).to have_received(:observe) - .with({ source: 'push' }, 5) + .with({ source: 'push', plan: project.actual_plan_name }, 5) end it 'tracks included template usage' do diff --git a/spec/support/shared_examples/requests/api/members_shared_examples.rb b/spec/support/shared_examples/requests/api/members_shared_examples.rb index fce75c29971..9136f60eb93 100644 --- a/spec/support/shared_examples/requests/api/members_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/members_shared_examples.rb @@ -11,3 +11,11 @@ RSpec.shared_examples 'a 404 response when source is private' do expect(response).to have_gitlab_http_status(:not_found) end end + +RSpec.shared_examples 'a 403 response when user does not have rights to manage members of a specific access level' do + it 'returns 403' do + route + + expect(response).to have_gitlab_http_status(:forbidden) + end +end diff --git a/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb index 544a0ed8fdd..bdff2c65691 100644 --- a/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb @@ -63,9 +63,9 @@ RSpec.shared_examples 'redirects to version download' do |user_type, status, add it 'returns a valid response' do subject - expect(request.url).to include 'module-1/system/download' + expect(request.url).to include "#{package.name}/download" expect(response.headers).to include 'Location' - expect(response.headers['Location']).to include 'module-1/system/1.0.1/download' + expect(response.headers['Location']).to include "#{package.name}/1.0.1/download" end end end diff --git a/spec/tooling/danger/project_helper_spec.rb b/spec/tooling/danger/project_helper_spec.rb index 1edf4bccf92..f9ad9ed13c2 100644 --- a/spec/tooling/danger/project_helper_spec.rb +++ b/spec/tooling/danger/project_helper_spec.rb @@ -33,6 +33,12 @@ RSpec.describe Tooling::Danger::ProjectHelper do where(:path, :expected_categories) do 'glfm_specification/example_snapshots/prosemirror_json.yml' | [:frontend] 'glfm_specification/input/glfm_anything.yml' | [:frontend, :backend] + + 'doc/api/graphql/reference/index.md' | [:docs, :backend] + 'doc/api/graphql/reference/some_other_file.txt' | [:docs, :backend] + 'doc/api/openapi/openapi.yaml' | [:docs, :backend] + 'doc/api/openapi/any_other_file.yaml' | [:docs, :backend] + 'usage_data.rb' | [:database, :backend, :product_intelligence] 'doc/foo.md' | [:docs] 'CONTRIBUTING.md' | [:docs] @@ -249,11 +255,6 @@ RSpec.describe Tooling::Danger::ProjectHelper do 'app/views/layouts/header/_default.html.haml' | [:frontend, :backend] 'app/views/layouts/header/_default.html.erb' | [:frontend, :backend] - - 'doc/api/graphql/reference/index.md' | [:docs] - 'doc/api/graphql/reference/some_other_file.txt' | [:backend] - 'doc/api/openapi/openapi.yaml' | [:backend] - 'doc/api/openapi/any_other_file.yaml' | [:backend] end with_them do diff --git a/spec/workers/container_registry/cleanup_worker_spec.rb b/spec/workers/container_registry/cleanup_worker_spec.rb new file mode 100644 index 00000000000..ffcb421ce1e --- /dev/null +++ b/spec/workers/container_registry/cleanup_worker_spec.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ContainerRegistry::CleanupWorker, :aggregate_failures do + let(:worker) { described_class.new } + + describe '#perform' do + let_it_be_with_reload(:container_repository) { create(:container_repository) } + + subject(:perform) { worker.perform } + + context 'with no delete scheduled container repositories' do + it "doesn't enqueue delete container repository jobs" do + expect(ContainerRegistry::DeleteContainerRepositoryWorker).not_to receive(:perform_with_capacity) + + perform + end + end + + context 'with delete scheduled container repositories' do + before do + container_repository.delete_scheduled! + end + + it 'enqueues delete container repository jobs' do + expect(ContainerRegistry::DeleteContainerRepositoryWorker).to receive(:perform_with_capacity) + + perform + end + end + + context 'with stale delete ongoing container repositories' do + let(:delete_started_at) { (described_class::STALE_DELETE_THRESHOLD + 5.minutes).ago } + + before do + container_repository.update!(status: :delete_ongoing, delete_started_at: delete_started_at) + end + + it 'resets them and enqueue delete container repository jobs' do + expect(ContainerRegistry::DeleteContainerRepositoryWorker).to receive(:perform_with_capacity) + + expect { perform } + .to change { container_repository.reload.status }.from('delete_ongoing').to('delete_scheduled') + .and change { container_repository.reload.delete_started_at }.to(nil) + end + end + + context 'for counts logging' do + let_it_be(:delete_started_at) { (described_class::STALE_DELETE_THRESHOLD + 5.minutes).ago } + let_it_be(:stale_delete_container_repository) do + create(:container_repository, :status_delete_ongoing, delete_started_at: delete_started_at) + end + + before do + container_repository.delete_scheduled! + end + + it 'logs the counts' do + expect(worker).to receive(:log_extra_metadata_on_done).with(:delete_scheduled_container_repositories_count, 1) + expect(worker).to receive(:log_extra_metadata_on_done).with(:stale_delete_container_repositories_count, 1) + + perform + end + end + + context 'with container_registry_delete_repository_with_cron_worker disabled' do + before do + stub_feature_flags(container_registry_delete_repository_with_cron_worker: false) + end + + it 'does not run' do + expect(worker).not_to receive(:reset_stale_deletes) + expect(worker).not_to receive(:enqueue_delete_container_repository_jobs) + expect(worker).not_to receive(:log_counts) + + subject + end + end + end +end diff --git a/spec/workers/pages_worker_spec.rb b/spec/workers/pages_worker_spec.rb index ad714d8d11e..f0d29037fa4 100644 --- a/spec/workers/pages_worker_spec.rb +++ b/spec/workers/pages_worker_spec.rb @@ -3,14 +3,26 @@ require 'spec_helper' RSpec.describe PagesWorker, :sidekiq_inline do - let(:project) { create(:project) } - let(:ci_build) { create(:ci_build, project: project) } + let_it_be(:ci_build) { create(:ci_build) } - it 'calls UpdatePagesService' do - expect_next_instance_of(Projects::UpdatePagesService, project, ci_build) do |service| - expect(service).to receive(:execute) + context 'when called with the deploy action' do + it 'calls UpdatePagesService' do + expect_next_instance_of(Projects::UpdatePagesService, ci_build.project, ci_build) do |service| + expect(service).to receive(:execute) + end + + described_class.perform_async(:deploy, ci_build.id) end + end - described_class.perform_async(:deploy, ci_build.id) + context 'when called with any other action' do + it 'does nothing' do + expect_next_instance_of(described_class) do |job_class| + expect(job_class).not_to receive(:foo) + expect(job_class).not_to receive(:deploy) + end + + described_class.perform_async(:foo) + end end end diff --git a/tooling/danger/project_helper.rb b/tooling/danger/project_helper.rb index 5b8d46f8328..a69d9049035 100644 --- a/tooling/danger/project_helper.rb +++ b/tooling/danger/project_helper.rb @@ -21,6 +21,10 @@ module Tooling %r{\Aglfm_specification/.+prosemirror_json\.yml} => [:frontend], %r{\Aglfm_specification/.+\.yml} => [:frontend, :backend], + # API auto generated doc files and schema (must come before generic docs regex) + %r{\Adoc/api/graphql/reference/} => [:docs, :backend], + %r{\Adoc/api/openapi/.*\.yaml\z} => [:docs, :backend], + [%r{usage_data\.rb}, %r{^(\+|-).*\s+(count|distinct_count|estimate_batch_distinct_count)\(.*\)(.*)$}] => [:database, :backend, :product_intelligence], %r{\A((ee|jh)/)?config/feature_flags/} => :feature_flag, @@ -166,10 +170,6 @@ module Tooling %r{\A((ee|jh)/)?changelogs/} => :none, %r{\Alocale/gitlab\.pot\z} => :none, - # API auto generated doc files and schema - %r{\Adoc/api/graphql/reference/} => :backend, - %r{\Adoc/api/openapi/.*\.yaml\z} => :backend, - # Fallbacks in case the above patterns miss anything %r{\.rb\z} => :backend, %r{( diff --git a/workhorse/go.mod b/workhorse/go.mod index dd68d7c7497..51adab831c2 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -7,7 +7,7 @@ require ( github.com/BurntSushi/toml v1.2.1 github.com/FZambia/sentinel v1.1.1 github.com/alecthomas/chroma/v2 v2.3.0 - github.com/aws/aws-sdk-go v1.44.133 + github.com/aws/aws-sdk-go v1.44.136 github.com/disintegration/imaging v1.6.2 github.com/getsentry/raven-go v0.2.0 github.com/golang-jwt/jwt/v4 v4.4.2 diff --git a/workhorse/go.sum b/workhorse/go.sum index a4d6d914e33..bf0c7df390d 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -227,8 +227,8 @@ github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4 github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.45/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.68/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.44.133 h1:+pWxt9nyKc0jf33rORBaQ93KPjYpmIIy3ozVXdJ82Oo= -github.com/aws/aws-sdk-go v1.44.133/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.136 h1:J1KJJssa8pjU8jETYUxwRS37KTcxjACfKd9GK8t+5ZU= +github.com/aws/aws-sdk-go v1.44.136/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.16.8 h1:gOe9UPR98XSf7oEJCcojYg+N2/jCRm4DdeIsP85pIyQ= github.com/aws/aws-sdk-go-v2 v1.16.8/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= |