Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml7
-rw-r--r--.rubocop.yml4
-rw-r--r--.rubocop_todo/rails/create_table_with_timestamps.yml69
-rw-r--r--.secretsignore66
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_METRICS_EXPORTER_VERSION2
-rw-r--r--app/assets/javascripts/content_editor/extensions/suggestions.js2
-rw-r--r--app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue22
-rw-r--r--app/assets/javascripts/jobs/components/table/cells/duration_cell.vue27
-rw-r--r--app/assets/javascripts/webhooks/components/form_url_app.vue21
-rw-r--r--app/controllers/projects/ml/experiments_controller.rb2
-rw-r--r--app/controllers/projects/packages/infrastructure_registry_controller.rb2
-rw-r--r--app/controllers/projects/registry/repositories_controller.rb6
-rw-r--r--app/controllers/terraform/services_controller.rb2
-rw-r--r--app/graphql/mutations/container_repositories/destroy.rb8
-rw-r--r--app/helpers/blob_helper.rb26
-rw-r--r--app/models/container_repository.rb1
-rw-r--r--app/workers/all_queues.yml9
-rw-r--r--app/workers/container_registry/cleanup_worker.rb59
-rw-r--r--app/workers/pages_worker.rb2
-rw-r--r--config/feature_flags/development/container_registry_delete_repository_with_cron_worker.yml (renamed from config/feature_flags/development/ci_variable_expansion_in_rules_exists.yml)8
-rw-r--r--config/feature_flags/development/ml_experiment_tracking.yml3
-rw-r--r--config/initializers/1_settings.rb3
-rw-r--r--config/open_api.yml4
-rw-r--r--data/deprecations/15-6-deprecate-post-api-v4-runner.yml10
-rw-r--r--data/deprecations/15-6-deprecate-runner-reg-token-helm.yml9
-rw-r--r--db/post_migrate/20221107094359_recount_epic_cache_counts.rb29
-rw-r--r--db/schema_migrations/202211070943591
-rw-r--r--doc/administration/auth/oidc.md2
-rw-r--r--doc/architecture/blueprints/runner_scaling/index.md24
-rw-r--r--doc/ci/yaml/index.md2
-rw-r--r--doc/development/code_review.md7
-rw-r--r--doc/development/database/database_migration_pipeline.md25
-rw-r--r--doc/development/testing_guide/best_practices.md5
-rw-r--r--doc/integration/bitbucket.md2
-rw-r--r--doc/update/deprecations.md19
-rw-r--r--doc/update/zero_downtime.md2
-rw-r--r--doc/user/project/integrations/webex_teams.md2
-rw-r--r--doc/user/project/issue_board.md8
-rw-r--r--lefthook.yml4
-rw-r--r--lib/api/api.rb6
-rw-r--r--lib/api/ci/pipeline_schedules.rb144
-rw-r--r--lib/api/entities/ci/pipeline_schedule.rb12
-rw-r--r--lib/api/entities/metrics/user_starred_dashboard.rb5
-rw-r--r--lib/api/group_import.rb11
-rw-r--r--lib/api/metrics/user_starred_dashboards.rb22
-rw-r--r--lib/api/project_container_repositories.rb6
-rw-r--r--lib/api/terraform/modules/v1/packages.rb2
-rw-r--r--lib/gitlab/background_migration/recount_epic_cache_counts.rb18
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/exists.rb8
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb2
-rw-r--r--lib/tasks/gitlab/assets.rake2
-rw-r--r--locale/gitlab.pot9
-rwxr-xr-xscripts/review_apps/review-apps.sh13
-rw-r--r--spec/controllers/projects/registry/repositories_controller_spec.rb23
-rw-r--r--spec/features/projects/container_registry_spec.rb5
-rw-r--r--spec/frontend/editor/schema/ci/ci_schema_spec.js26
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml3
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/empty.yml5
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/invalid_variable.yml5
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/leading_slash.yml5
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/no_slash.yml5
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/include/tailing_slash.yml5
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/trigger_include.yml29
-rw-r--r--spec/frontend/webhooks/components/form_url_app_spec.js13
-rw-r--r--spec/graphql/mutations/container_repositories/destroy_spec.rb22
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb2
-rw-r--r--spec/migrations/recount_epic_cache_counts_spec.rb32
-rw-r--r--spec/models/container_repository_spec.rb10
-rw-r--r--spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb23
-rw-r--r--spec/requests/api/invitations_spec.rb125
-rw-r--r--spec/requests/api/members_spec.rb125
-rw-r--r--spec/requests/api/project_container_repositories_spec.rb22
-rw-r--r--spec/requests/projects/ml/experiments_controller_spec.rb24
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb2
-rw-r--r--spec/support/shared_examples/requests/api/members_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb4
-rw-r--r--spec/tooling/danger/project_helper_spec.rb11
-rw-r--r--spec/workers/container_registry/cleanup_worker_spec.rb81
-rw-r--r--spec/workers/pages_worker_spec.rb24
-rw-r--r--tooling/danger/project_helper.rb8
-rw-r--r--workhorse/go.mod2
-rw-r--r--workhorse/go.sum4
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=