summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-11-11 12:08:16 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-11-11 12:08:16 +0000
commit2761b4465bb13e170f0b8b2941d83f356a47eee6 (patch)
tree9a34e185671e7a9afe8720c8eafc6a3319a63c97
parent43e40e8daaceafb9b78fde9ac5ce97584a210a90 (diff)
downloadgitlab-ce-2761b4465bb13e170f0b8b2941d83f356a47eee6.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/performance/method_object_as_block.yml1
-rw-r--r--.rubocop_todo/rspec/expect_change.yml1
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum4
-rw-r--r--Gemfile.lock8
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue2
-rw-r--r--app/assets/javascripts/clusters_list/components/delete_agent_button.vue7
-rw-r--r--app/assets/javascripts/editor/schema/ci.json66
-rw-r--r--app/assets/javascripts/work_items/router/index.js2
-rw-r--r--app/assets/javascripts/work_items/router/routes.js35
-rw-r--r--app/assets/stylesheets/page_bundles/clusters.scss4
-rw-r--r--app/controllers/concerns/integrations/params.rb4
-rw-r--r--app/models/concerns/repository_storage_movable.rb4
-rw-r--r--app/models/integration.rb4
-rw-r--r--app/models/integrations/base_chat_notification.rb6
-rw-r--r--app/models/integrations/discord.rb3
-rw-r--r--app/models/integrations/hangouts_chat.rb6
-rw-r--r--app/models/integrations/mattermost.rb2
-rw-r--r--app/models/integrations/microsoft_teams.rb6
-rw-r--r--app/models/integrations/pumble.rb2
-rw-r--r--app/models/integrations/slack.rb4
-rw-r--r--app/models/integrations/unify_circuit.rb2
-rw-r--r--app/models/integrations/webex_teams.rb2
-rw-r--r--app/policies/project_policy.rb4
-rw-r--r--app/serializers/detailed_status_entity.rb37
-rw-r--r--app/serializers/integrations/field_entity.rb2
-rw-r--r--app/serializers/test_case_entity.rb23
-rw-r--r--app/serializers/test_report_entity.rb14
-rw-r--r--app/serializers/test_report_summary_entity.rb2
-rw-r--r--app/serializers/test_suite_entity.rb19
-rw-r--r--app/serializers/test_suite_summary_entity.rb5
-rw-r--r--app/services/merge_requests/mergeability/run_checks_service.rb1
-rw-r--r--app/views/shared/_md_preview.html.haml2
-rw-r--r--config/feature_flags/development/mergeability_caching.yml8
-rw-r--r--config/metrics/counts_all/20210216181016_projects_with_expiration_policy_enabled.yml3
-rw-r--r--config/metrics/counts_all/20210216181018_projects_with_expiration_policy_enabled_with_keep_n_set_to_1.yml4
-rw-r--r--config/metrics/counts_all/20210216181020_projects_with_expiration_policy_enabled_with_keep_n_set_to_5.yml4
-rw-r--r--config/metrics/counts_all/20210216181022_projects_with_expiration_policy_enabled_with_keep_n_set_to_10.yml4
-rw-r--r--config/metrics/counts_all/20210216181024_projects_with_expiration_policy_enabled_with_keep_n_set_to_25.yml4
-rw-r--r--config/metrics/counts_all/20210216181025_projects_with_expiration_policy_enabled_with_keep_n_set_to_50.yml4
-rw-r--r--config/metrics/counts_all/20210216181027_projects_with_expiration_policy_enabled_with_keep_n_set_to_100.yml4
-rw-r--r--config/metrics/counts_all/20210216181046_projects_with_expiration_policy_enabled_with_keep_n_unset.yml4
-rw-r--r--doc/administration/audit_events.md37
-rw-r--r--doc/ci/yaml/index.md32
-rw-r--r--doc/development/documentation/topic_types/tutorial.md5
-rw-r--r--doc/development/sec/security_report_ingestion_overview.md2
-rw-r--r--doc/tutorials/agile_sprint.md2
-rw-r--r--doc/tutorials/index.md2
-rw-r--r--doc/tutorials/make_your_first_git_commit.md2
-rw-r--r--generator_templates/usage_metric_definition/metric_definition.yml2
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/ci/pipelines.rb149
-rw-r--r--lib/api/entities/ci/pipeline.rb18
-rw-r--r--lib/api/project_repository_storage_moves.rb2
-rw-r--r--lib/api/snippet_repository_storage_moves.rb2
-rw-r--r--lib/gitlab/ci/config/external/mapper.rb16
-rw-r--r--lib/gitlab/ci/pipeline/chain/limit/active_jobs.rb5
-rw-r--r--lib/gitlab/usage_data.rb4
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/controllers/projects/settings/integrations_controller_spec.rb17
-rw-r--r--spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric.yml2
-rw-r--r--spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_ee.yml2
-rw-r--r--spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_name_suggestions.yml2
-rw-r--r--spec/frontend/editor/schema/ci/ci_schema_spec.js6
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_syntax_desc.yml (renamed from spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables.yml)1
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml10
-rw-r--r--spec/frontend/work_items/router_spec.js15
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb4
-rw-r--r--spec/lib/gitlab/usage/metric_definition_spec.rb5
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb16
-rw-r--r--spec/models/integration_spec.rb10
-rw-r--r--spec/models/integrations/base_chat_notification_spec.rb6
-rw-r--r--spec/models/merge_request_spec.rb8
-rw-r--r--spec/serializers/integrations/field_entity_spec.rb34
-rw-r--r--spec/services/issues/close_service_spec.rb21
-rw-r--r--spec/services/issues/create_service_spec.rb61
-rw-r--r--spec/services/issues/move_service_spec.rb48
-rw-r--r--spec/services/issues/reopen_service_spec.rb20
-rw-r--r--spec/services/issues/update_service_spec.rb4
-rw-r--r--spec/services/merge_requests/mergeability/run_checks_service_spec.rb12
-rw-r--r--spec/services/merge_requests/update_service_spec.rb2
-rw-r--r--spec/support/helpers/usage_data_helpers.rb7
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/services/issuable_update_service_shared_examples.rb40
-rw-r--r--spec/views/projects/commit/show.html.haml_spec.rb8
89 files changed, 707 insertions, 296 deletions
diff --git a/.rubocop_todo/performance/method_object_as_block.yml b/.rubocop_todo/performance/method_object_as_block.yml
index 1bc82ff05ec..acb1e2d621b 100644
--- a/.rubocop_todo/performance/method_object_as_block.yml
+++ b/.rubocop_todo/performance/method_object_as_block.yml
@@ -29,7 +29,6 @@ Performance/MethodObjectAsBlock:
- 'ee/app/services/security/findings/cleanup_service.rb'
- 'ee/app/services/security/ingestion/ingest_reports_service.rb'
- 'ee/app/services/security/ingestion/tasks/ingest_vulnerability_statistics.rb'
- - 'ee/app/services/security/store_findings_metadata_service.rb'
- 'ee/app/services/security/store_grouped_scans_service.rb'
- 'ee/lib/ee/container_registry/client.rb'
- 'ee/lib/ee/gitlab/ci/config_ee.rb'
diff --git a/.rubocop_todo/rspec/expect_change.yml b/.rubocop_todo/rspec/expect_change.yml
index 626ed1390ce..26981055462 100644
--- a/.rubocop_todo/rspec/expect_change.yml
+++ b/.rubocop_todo/rspec/expect_change.yml
@@ -293,7 +293,6 @@ RSpec/ExpectChange:
- 'ee/spec/services/security/orchestration/assign_service_spec.rb'
- 'ee/spec/services/security/override_uuids_service_spec.rb'
- 'ee/spec/services/security/security_orchestration_policies/sync_opened_merge_requests_service_spec.rb'
- - 'ee/spec/services/security/store_findings_metadata_service_spec.rb'
- 'ee/spec/services/security/store_scan_service_spec.rb'
- 'ee/spec/services/start_pull_mirroring_service_spec.rb'
- 'ee/spec/services/status_page/mark_for_publication_service_spec.rb'
diff --git a/Gemfile b/Gemfile
index 8288559a3c8..9b42789ea30 100644
--- a/Gemfile
+++ b/Gemfile
@@ -166,7 +166,7 @@ gem 'seed-fu', '~> 2.3.7'
gem 'elasticsearch-model', '~> 7.2'
gem 'elasticsearch-rails', '~> 7.2', require: 'elasticsearch/rails/instrumentation'
gem 'elasticsearch-api', '7.13.3'
-gem 'aws-sdk-core', '~> 3.166.0'
+gem 'aws-sdk-core', '~> 3.167.0'
gem 'aws-sdk-cloudformation', '~> 1'
gem 'aws-sdk-s3', '~> 1.117.1'
gem 'faraday_middleware-aws-sigv4', '~>0.3.0'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 9dca9488e95..06379a80710 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -33,9 +33,9 @@
{"name":"awesome_print","version":"1.9.2","platform":"ruby","checksum":"e99b32b704acff16d768b3468680793ced40bfdc4537eb07e06a4be11133786e"},
{"name":"awrence","version":"1.1.1","platform":"ruby","checksum":"9be584c97408ed92d5e1ca11740853646fe270de675f2f8dd44e8233226dfc97"},
{"name":"aws-eventstream","version":"1.2.0","platform":"ruby","checksum":"ffa53482c92880b001ff2fb06919b9bb82fd847cbb0fa244985d2ebb6dd0d1df"},
-{"name":"aws-partitions","version":"1.651.0","platform":"ruby","checksum":"61f354049eb2c10bf0aa96b115f7443d181d79ec5508f7a34b8724c4cfa95dda"},
+{"name":"aws-partitions","version":"1.658.0","platform":"ruby","checksum":"bba2e21fc87c4e68c7ba5c09e3cd2b81d59ca86111ab236eaf9c5a8ae207b3fc"},
{"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"},
-{"name":"aws-sdk-core","version":"3.166.0","platform":"ruby","checksum":"827b82a31f13007fbd3ce78801949019ad3b6fa0c658270d5caa6095cb4945fa"},
+{"name":"aws-sdk-core","version":"3.167.0","platform":"ruby","checksum":"d371856ad86f8bff08928059ee09b7cb9bca8ebf36bf5081f12424e4f491b624"},
{"name":"aws-sdk-kms","version":"1.59.0","platform":"ruby","checksum":"6c002ebf8e404625c8338ca12ae69b1329399f9dc1b0ebca474e00ff06700153"},
{"name":"aws-sdk-s3","version":"1.117.1","platform":"ruby","checksum":"76f6dac5baeb2b78616eb34c6af650c1b7a15c1078b169d1b27e8421904c509d"},
{"name":"aws-sigv4","version":"1.5.1","platform":"ruby","checksum":"d68c87fff4ee843b4b92b23c7f31f957f254ec6eb064181f7119124aab8b8bb4"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 36f5c4f00db..1b41829b4e6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -185,11 +185,11 @@ GEM
awesome_print (1.9.2)
awrence (1.1.1)
aws-eventstream (1.2.0)
- aws-partitions (1.651.0)
+ aws-partitions (1.658.0)
aws-sdk-cloudformation (1.41.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1)
- aws-sdk-core (3.166.0)
+ aws-sdk-core (3.167.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
@@ -1590,7 +1590,7 @@ DEPENDENCIES
autoprefixer-rails (= 10.2.5.1)
awesome_print
aws-sdk-cloudformation (~> 1)
- aws-sdk-core (~> 3.166.0)
+ aws-sdk-core (~> 3.167.0)
aws-sdk-s3 (~> 1.117.1)
babosa (~> 1.0.4)
base32 (~> 0.3.0)
@@ -1869,4 +1869,4 @@ DEPENDENCIES
yajl-ruby (~> 1.4.3)
BUNDLED WITH
- 2.3.24
+ 2.3.25
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
index 6b5aac57f1c..03bc4b825ae 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -612,7 +612,7 @@ export default {
>
<alert-settings-form-help-block
:message="viewCredentialsHelpMsg"
- link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
+ link="https://docs.gitlab.com/ee/operations/incident_management/integrations.html#configuration"
/>
<gl-form-group id="integration-webhook">
diff --git a/app/assets/javascripts/clusters_list/components/delete_agent_button.vue b/app/assets/javascripts/clusters_list/components/delete_agent_button.vue
index 6f2c353a67b..7a028858d10 100644
--- a/app/assets/javascripts/clusters_list/components/delete_agent_button.vue
+++ b/app/assets/javascripts/clusters_list/components/delete_agent_button.vue
@@ -92,6 +92,9 @@ export default {
disableModalSubmit() {
return this.deleteConfirmText !== this.agent.name;
},
+ containerTabIndex() {
+ return this.canAdminCluster ? -1 : 0;
+ },
},
methods: {
async deleteAgent() {
@@ -156,8 +159,8 @@ export default {
<div>
<div
v-gl-tooltip="deleteButtonTooltip"
- class="gl-display-inline-block"
- tabindex="-1"
+ :tabindex="containerTabIndex"
+ class="cluster-button-container gl-rounded-base gl-display-inline-block"
data-testid="delete-agent-button-tooltip"
>
<gl-button
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index 4c2d6ac30bb..45f063a2048 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -132,7 +132,7 @@
"$ref": "#/definitions/exists"
},
"variables": {
- "$ref": "#/definitions/variables"
+ "$ref": "#/definitions/rulesVariables"
},
"when": {
"type": "string",
@@ -690,7 +690,7 @@
"$ref": "#/definitions/exists"
},
"variables": {
- "$ref": "#/definitions/variables"
+ "$ref": "#/definitions/rulesVariables"
},
"when": {
"$ref": "#/definitions/when"
@@ -744,6 +744,10 @@
"description": {
"type": "string",
"markdownDescription": "Explains what the variable is used for, what the acceptable values are. Variables with `description` are prefilled when running a pipeline manually. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#variablesdescription)."
+ },
+ "expand": {
+ "type": "boolean",
+ "markdownDescription": "If the variable is expandable or not. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#variablesexpand)."
}
},
"additionalProperties": false
@@ -753,6 +757,49 @@
"additionalProperties": false
}
},
+ "jobVariables": {
+ "markdownDescription": "Defines variables for a job. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#variables).",
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "number"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "value": {
+ "type": "string"
+ },
+ "expand": {
+ "type": "boolean",
+ "markdownDescription": "Defines if the variable is expandable or not. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#variablesexpand)."
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "additionalProperties": false
+ }
+ },
+ "rulesVariables": {
+ "markdownDescription": "Defines variables for a rule result. [Learn More](https://docs.gitlab.com/ee/ci/yaml/index.html#rulesvariables).",
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "type": [
+ "string",
+ "number"
+ ]
+ },
+ "additionalProperties": false
+ }
+ },
"if": {
"type": "string",
"markdownDescription": "Expression to evaluate whether additional attributes should be provided to the job. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#rulesif)."
@@ -795,19 +842,6 @@
"type": "string"
}
},
- "variables": {
- "markdownDescription": "Defines environment variables. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#variables).",
- "type": "object",
- "patternProperties": {
- ".*": {
- "type": [
- "string",
- "number"
- ]
- },
- "additionalProperties": false
- }
- },
"timeout": {
"type": "string",
"markdownDescription": "Allows you to configure a timeout for a specific job (e.g. `1 minute`, `1h 30m 12s`). [Learn More](https://docs.gitlab.com/ee/ci/yaml/index.html#timeout).",
@@ -1170,7 +1204,7 @@
"$ref": "#/definitions/rules"
},
"variables": {
- "$ref": "#/definitions/variables"
+ "$ref": "#/definitions/jobVariables"
},
"cache": {
"$ref": "#/definitions/cache"
diff --git a/app/assets/javascripts/work_items/router/index.js b/app/assets/javascripts/work_items/router/index.js
index 2b39a298720..777badeb5be 100644
--- a/app/assets/javascripts/work_items/router/index.js
+++ b/app/assets/javascripts/work_items/router/index.js
@@ -9,7 +9,7 @@ Vue.use(VueRouter);
export function createRouter(fullPath) {
return new VueRouter({
- routes,
+ routes: routes(),
mode: 'history',
base: joinPaths(fullPath, '-', 'work_items'),
});
diff --git a/app/assets/javascripts/work_items/router/routes.js b/app/assets/javascripts/work_items/router/routes.js
index 95772bbd026..1e3a7e184bb 100644
--- a/app/assets/javascripts/work_items/router/routes.js
+++ b/app/assets/javascripts/work_items/router/routes.js
@@ -1,13 +1,22 @@
-export const routes = [
- {
- path: '/new',
- name: 'createWorkItem',
- component: () => import('../pages/create_work_item.vue'),
- },
- {
- path: '/:id',
- name: 'workItem',
- component: () => import('../pages/work_item_root.vue'),
- props: true,
- },
-];
+function getRoutes() {
+ const routes = [
+ {
+ path: '/:id',
+ name: 'workItem',
+ component: () => import('../pages/work_item_root.vue'),
+ props: true,
+ },
+ ];
+
+ if (gon.features?.workItemsMvc2) {
+ routes.unshift({
+ path: '/new',
+ name: 'createWorkItem',
+ component: () => import('../pages/create_work_item.vue'),
+ });
+ }
+
+ return routes;
+}
+
+export const routes = getRoutes;
diff --git a/app/assets/stylesheets/page_bundles/clusters.scss b/app/assets/stylesheets/page_bundles/clusters.scss
index a877ae72e31..4f29ff4b1ad 100644
--- a/app/assets/stylesheets/page_bundles/clusters.scss
+++ b/app/assets/stylesheets/page_bundles/clusters.scss
@@ -20,3 +20,7 @@
min-height: 372px;
}
}
+
+.cluster-button-container:focus-within {
+ @include gl-focus;
+}
diff --git a/app/controllers/concerns/integrations/params.rb b/app/controllers/concerns/integrations/params.rb
index c3ad9d3dff3..30de4a86bec 100644
--- a/app/controllers/concerns/integrations/params.rb
+++ b/app/controllers/concerns/integrations/params.rb
@@ -88,6 +88,10 @@ module Integrations
param_values = return_value[:integration]
if param_values.is_a?(ActionController::Parameters)
+ if action_name == 'update' && integration.chat? && param_values['webhook'] == BaseChatNotification::SECRET_MASK
+ param_values.delete('webhook')
+ end
+
integration.secret_fields.each do |param|
param_values.delete(param) if param_values[param].blank?
end
diff --git a/app/models/concerns/repository_storage_movable.rb b/app/models/concerns/repository_storage_movable.rb
index b7fd52ab305..87ff413f2c1 100644
--- a/app/models/concerns/repository_storage_movable.rb
+++ b/app/models/concerns/repository_storage_movable.rb
@@ -19,9 +19,7 @@ module RepositoryStorageMovable
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
validate :container_repository_writable, on: :create
- default_value_for(:destination_storage_name, allows_nil: false) do
- Repository.pick_storage_shard
- end
+ attribute :destination_storage_name, default: -> { Repository.pick_storage_shard }
state_machine initial: :initial do
event :schedule do
diff --git a/app/models/integration.rb b/app/models/integration.rb
index 90dc6b486ab..41278dce22d 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -589,6 +589,10 @@ class Integration < ApplicationRecord
false
end
+ def chat?
+ category == :chat
+ end
+
private
# Ancestors sorted by hierarchy depth in bottom-top order.
diff --git a/app/models/integrations/base_chat_notification.rb b/app/models/integrations/base_chat_notification.rb
index c7992e4083c..06d18b7260f 100644
--- a/app/models/integrations/base_chat_notification.rb
+++ b/app/models/integrations/base_chat_notification.rb
@@ -22,6 +22,8 @@ module Integrations
MATCH_ALL_LABELS = 'match_all'
].freeze
+ SECRET_MASK = '************'
+
default_value_for :category, 'chat'
prop_accessor :webhook, :username, :channel, :branches_to_be_notified, :labels_to_be_notified, :labels_to_be_notified_behavior
@@ -71,7 +73,7 @@ module Integrations
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}", required: true }.freeze,
+ { type: 'text', name: 'webhook', help: "#{webhook_help}", required: true }.freeze,
{ type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze,
{ type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'Do not send notifications for successful pipelines.' }.freeze,
{
@@ -147,7 +149,7 @@ module Integrations
raise NotImplementedError
end
- def webhook_placeholder
+ def webhook_help
raise NotImplementedError
end
diff --git a/app/models/integrations/discord.rb b/app/models/integrations/discord.rb
index d0389b82410..061c491034d 100644
--- a/app/models/integrations/discord.rb
+++ b/app/models/integrations/discord.rb
@@ -10,8 +10,7 @@ module Integrations
field :webhook,
section: SECTION_TYPE_CONNECTION,
- placeholder: 'https://discordapp.com/api/webhooks/…',
- help: 'URL to the webhook for the Discord channel.',
+ help: 'e.g. https://discordapp.com/api/webhooks/…',
required: true
field :notify_only_broken_pipelines,
diff --git a/app/models/integrations/hangouts_chat.rb b/app/models/integrations/hangouts_chat.rb
index 6e7f31aa030..c903e8d9eb8 100644
--- a/app/models/integrations/hangouts_chat.rb
+++ b/app/models/integrations/hangouts_chat.rb
@@ -22,10 +22,6 @@ module Integrations
def default_channel_placeholder
end
- def webhook_placeholder
- 'https://chat.googleapis.com/v1/spaces…'
- end
-
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
@@ -33,7 +29,7 @@ module Integrations
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
+ { type: 'text', name: 'webhook', help: 'https://chat.googleapis.com/v1/spaces…' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{
type: 'select',
diff --git a/app/models/integrations/mattermost.rb b/app/models/integrations/mattermost.rb
index 60f3b10b80c..dd1c98ee06b 100644
--- a/app/models/integrations/mattermost.rb
+++ b/app/models/integrations/mattermost.rb
@@ -25,7 +25,7 @@ module Integrations
'my-channel'
end
- def webhook_placeholder
+ def webhook_help
'http://mattermost.example.com/hooks/'
end
diff --git a/app/models/integrations/microsoft_teams.rb b/app/models/integrations/microsoft_teams.rb
index 69863f164cd..d6cbe5760e8 100644
--- a/app/models/integrations/microsoft_teams.rb
+++ b/app/models/integrations/microsoft_teams.rb
@@ -18,10 +18,6 @@ module Integrations
'<p>Use this service to send notifications about events in GitLab projects to your Microsoft Teams channels. <a href="https://docs.gitlab.com/ee/user/project/integrations/microsoft_teams.html" target="_blank" rel="noopener noreferrer">How do I configure this integration?</a></p>'
end
- def webhook_placeholder
- 'https://outlook.office.com/webhook/…'
- end
-
def default_channel_placeholder
end
@@ -32,7 +28,7 @@ module Integrations
def default_fields
[
- { type: 'text', section: SECTION_TYPE_CONNECTION, name: 'webhook', required: true, placeholder: "#{webhook_placeholder}" },
+ { type: 'text', section: SECTION_TYPE_CONNECTION, name: 'webhook', help: 'https://outlook.office.com/webhook/…', required: true },
{
type: 'checkbox',
section: SECTION_TYPE_CONFIGURATION,
diff --git a/app/models/integrations/pumble.rb b/app/models/integrations/pumble.rb
index 6c5493c4b65..e08dc6d0f51 100644
--- a/app/models/integrations/pumble.rb
+++ b/app/models/integrations/pumble.rb
@@ -36,7 +36,7 @@ module Integrations
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "https://api.pumble.com/workspaces/x/...", required: true },
+ { type: 'text', name: 'webhook', help: 'https://api.pumble.com/workspaces/x/...', required: true },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{
type: 'select',
diff --git a/app/models/integrations/slack.rb b/app/models/integrations/slack.rb
index ffca0d40f69..89326b8174f 100644
--- a/app/models/integrations/slack.rb
+++ b/app/models/integrations/slack.rb
@@ -16,8 +16,8 @@ module Integrations
'slack'
end
- override :webhook_placeholder
- def webhook_placeholder
+ override :webhook_help
+ def webhook_help
'https://hooks.slack.com/services/…'
end
end
diff --git a/app/models/integrations/unify_circuit.rb b/app/models/integrations/unify_circuit.rb
index 2e7b9b7dc60..aa19133b8c2 100644
--- a/app/models/integrations/unify_circuit.rb
+++ b/app/models/integrations/unify_circuit.rb
@@ -29,7 +29,7 @@ module Integrations
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "https://yourcircuit.com/rest/v2/webhooks/incoming/…", required: true },
+ { type: 'text', name: 'webhook', help: 'https://yourcircuit.com/rest/v2/webhooks/incoming/…', required: true },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{
type: 'select',
diff --git a/app/models/integrations/webex_teams.rb b/app/models/integrations/webex_teams.rb
index 5a7a296f550..8e6f5ca6d17 100644
--- a/app/models/integrations/webex_teams.rb
+++ b/app/models/integrations/webex_teams.rb
@@ -29,7 +29,7 @@ module Integrations
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "https://api.ciscospark.com/v1/webhooks/incoming/...", required: true },
+ { type: 'text', name: 'webhook', help: 'https://api.ciscospark.com/v1/webhooks/incoming/...', required: true },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{
type: 'select',
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index bb7c19d67eb..bfeb1a602ab 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -736,6 +736,10 @@ class ProjectPolicy < BasePolicy
enable :read_work_item
end
+ rule { can?(:read_merge_request) }.policy do
+ enable :read_vulnerability_merge_request_link
+ end
+
rule { can?(:developer_access) }.policy do
enable :read_security_configuration
end
diff --git a/app/serializers/detailed_status_entity.rb b/app/serializers/detailed_status_entity.rb
index 4f23ef0ed82..ed8ac9f40f7 100644
--- a/app/serializers/detailed_status_entity.rb
+++ b/app/serializers/detailed_status_entity.rb
@@ -3,12 +3,25 @@
class DetailedStatusEntity < Grape::Entity
include RequestAwareEntity
- expose :icon, :text, :label, :group
- expose :status_tooltip, as: :tooltip
- expose :has_details?, as: :has_details
- expose :details_path
+ expose :icon, documentation: { type: 'string', example: 'status_success' }
+ expose :text, documentation: { type: 'string', example: 'passed' }
+ expose :label, documentation: { type: 'string', example: 'passed' }
+ expose :group, documentation: { type: 'string', example: 'success' }
+ expose :status_tooltip, as: :tooltip, documentation: { type: 'string', example: 'passed' }
+ expose :has_details?, as: :has_details, documentation: { type: 'boolean', example: true }
+ expose :details_path, documentation: { type: 'string', example: '/test-group/test-project/-/pipelines/287' }
- expose :illustration do |status|
+ expose :illustration, documentation: {
+ type: 'object',
+ example: <<~JSON
+ {
+ "image": "illustrations/job_not_triggered.svg",
+ "size": "svg-306",
+ "title": "This job has not been triggered yet",
+ "content": "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+ }
+ JSON
+ } do |status|
illustration = {
image: ActionController::Base.helpers.image_path(status.illustration[:image])
}
@@ -19,15 +32,17 @@ class DetailedStatusEntity < Grape::Entity
# ignored
end
- expose :favicon do |status|
+ expose :favicon,
+ documentation: { type: 'string',
+ example: '/assets/ci_favicons/favicon_status_success.png' } do |status|
Gitlab::Favicon.status_overlay(status.favicon)
end
expose :action, if: -> (status, _) { status.has_action? } do
- expose :action_icon, as: :icon
- expose :action_title, as: :title
- expose :action_path, as: :path
- expose :action_method, as: :method
- expose :action_button_title, as: :button_title
+ expose :action_icon, as: :icon, documentation: { type: 'string', example: 'cancel' }
+ expose :action_title, as: :title, documentation: { type: 'string', example: 'Cancel' }
+ expose :action_path, as: :path, documentation: { type: 'string', example: '/namespace1/project1/-/jobs/2/cancel' }
+ expose :action_method, as: :method, documentation: { type: 'string', example: 'post' }
+ expose :action_button_title, as: :button_title, documentation: { type: 'string', example: 'Cancel this job' }
end
end
diff --git a/app/serializers/integrations/field_entity.rb b/app/serializers/integrations/field_entity.rb
index 697b53a737e..1c548cfab78 100644
--- a/app/serializers/integrations/field_entity.rb
+++ b/app/serializers/integrations/field_entity.rb
@@ -22,6 +22,8 @@ module Integrations
'true'
elsif field[:type] == 'checkbox'
ActiveRecord::Type::Boolean.new.deserialize(value).to_s
+ elsif field[:name] == 'webhook' && integration.chat?
+ BaseChatNotification::SECRET_MASK if value.present?
else
value
end
diff --git a/app/serializers/test_case_entity.rb b/app/serializers/test_case_entity.rb
index 8a5fadf53a6..1a872274cbf 100644
--- a/app/serializers/test_case_entity.rb
+++ b/app/serializers/test_case_entity.rb
@@ -3,15 +3,20 @@
class TestCaseEntity < Grape::Entity
include API::Helpers::RelatedResourcesHelpers
- expose :status
- expose :name, default: "(No name)"
- expose :classname
- expose :file
- expose :execution_time
- expose :system_output
- expose :stack_trace
- expose :recent_failures
- expose :attachment_url, if: -> (*) { can_read_screenshots? } do |test_case|
+ expose :status, documentation: { type: 'string', example: 'success' }
+ expose :name, default: "(No name)",
+ documentation: { type: 'string', example: 'Security Reports can create an auto-remediation MR' }
+ expose :classname, documentation: { type: 'string', example: 'vulnerability_management_spec' }
+ expose :file, documentation: { type: 'string', example: './spec/test_spec.rb' }
+ expose :execution_time, documentation: { type: 'integer', example: 180 }
+ expose :system_output, documentation: { type: 'string', example: 'Failure/Error: is_expected.to eq(3)' }
+ expose :stack_trace, documentation: { type: 'string', example: 'Failure/Error: is_expected.to eq(3)' }
+ expose :recent_failures, documentation: { example: { count: 3, base_branch: 'develop' } }
+ expose(
+ :attachment_url,
+ if: -> (*) { can_read_screenshots? },
+ documentation: { type: 'string', example: 'http://localhost/namespace1/project1/-/jobs/1/artifacts/file/some/path.png' }
+ ) do |test_case|
expose_url(test_case.attachment_url)
end
diff --git a/app/serializers/test_report_entity.rb b/app/serializers/test_report_entity.rb
index 9eb487da60a..0ff1c671f53 100644
--- a/app/serializers/test_report_entity.rb
+++ b/app/serializers/test_report_entity.rb
@@ -1,15 +1,15 @@
# frozen_string_literal: true
class TestReportEntity < Grape::Entity
- expose :total_time
- expose :total_count
+ expose :total_time, documentation: { type: 'integer', example: 180 }
+ expose :total_count, documentation: { type: 'integer', example: 1 }
- expose :success_count
- expose :failed_count
- expose :skipped_count
- expose :error_count
+ expose :success_count, documentation: { type: 'integer', example: 1 }
+ expose :failed_count, documentation: { type: 'integer', example: 0 }
+ expose :skipped_count, documentation: { type: 'integer', example: 0 }
+ expose :error_count, documentation: { type: 'integer', example: 0 }
- expose :test_suites, using: TestSuiteEntity do |report|
+ expose :test_suites, using: TestSuiteEntity, documentation: { is_array: true } do |report|
report.test_suites.values
end
end
diff --git a/app/serializers/test_report_summary_entity.rb b/app/serializers/test_report_summary_entity.rb
index bc73c49092f..f712b9f5500 100644
--- a/app/serializers/test_report_summary_entity.rb
+++ b/app/serializers/test_report_summary_entity.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class TestReportSummaryEntity < Grape::Entity
- expose :total
+ expose :total, documentation: { type: 'integer', example: 3363 }
expose :test_suites, using: TestSuiteSummaryEntity do |summary|
summary.test_suites.values
diff --git a/app/serializers/test_suite_entity.rb b/app/serializers/test_suite_entity.rb
index 15eb2891b22..a31b1f3ab9b 100644
--- a/app/serializers/test_suite_entity.rb
+++ b/app/serializers/test_suite_entity.rb
@@ -1,18 +1,19 @@
# frozen_string_literal: true
class TestSuiteEntity < Grape::Entity
- expose :name
- expose :total_time
- expose :total_count
+ expose :name, documentation: { type: 'string', example: 'test' }
+ expose :total_time, documentation: { type: 'integer', example: 1904 }
+ expose :total_count, documentation: { type: 'integer', example: 3363 }
- expose :success_count
- expose :failed_count
- expose :skipped_count
- expose :error_count
+ expose :success_count, documentation: { type: 'integer', example: 3351 }
+ expose :failed_count, documentation: { type: 'integer', example: 0 }
+ expose :skipped_count, documentation: { type: 'integer', example: 12 }
+ expose :error_count, documentation: { type: 'integer', example: 0 }
with_options if: -> (_, opts) { opts[:details] } do |test_suite|
- expose :suite_error
- expose :test_cases, using: TestCaseEntity do |test_suite|
+ expose :suite_error,
+ documentation: { type: 'string', example: 'JUnit XML parsing failed: 1:1: FATAL: Document is empty' }
+ expose :test_cases, using: TestCaseEntity, documentation: { is_array: true } do |test_suite|
test_suite.suite_error ? [] : test_suite.sorted.test_cases.values.flat_map(&:values)
end
end
diff --git a/app/serializers/test_suite_summary_entity.rb b/app/serializers/test_suite_summary_entity.rb
index 228c6e499fe..3a9ccb22713 100644
--- a/app/serializers/test_suite_summary_entity.rb
+++ b/app/serializers/test_suite_summary_entity.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
class TestSuiteSummaryEntity < TestSuiteEntity
- expose :build_ids do |summary|
+ expose :build_ids, documentation: { type: 'integer', is_array: true, example: [66004] } do |summary|
summary.build_ids
end
- expose :suite_error
+ expose :suite_error,
+ documentation: { type: 'string', example: 'JUnit XML parsing failed: 1:1: FATAL: Document is empty' }
end
diff --git a/app/services/merge_requests/mergeability/run_checks_service.rb b/app/services/merge_requests/mergeability/run_checks_service.rb
index 0af589b7987..740a6feac2c 100644
--- a/app/services/merge_requests/mergeability/run_checks_service.rb
+++ b/app/services/merge_requests/mergeability/run_checks_service.rb
@@ -46,7 +46,6 @@ module MergeRequests
attr_reader :merge_request, :params, :results
def run_check(check)
- return check.execute unless Feature.enabled?(:mergeability_caching, merge_request.project)
return check.execute unless check.cacheable?
cached_result = cached_results.read(merge_check: check)
diff --git a/app/views/shared/_md_preview.html.haml b/app/views/shared/_md_preview.html.haml
index 7314a7ddadc..2fff70cdc74 100644
--- a/app/views/shared/_md_preview.html.haml
+++ b/app/views/shared/_md_preview.html.haml
@@ -1,6 +1,6 @@
- referenced_users = local_assigns.fetch(:referenced_users, nil)
-- if defined?(@merge_request) && @merge_request.discussion_locked?
+- if @merge_request&.discussion_locked?
.issuable-note-warning
= sprite_icon('lock', css_class: 'icon')
%span
diff --git a/config/feature_flags/development/mergeability_caching.yml b/config/feature_flags/development/mergeability_caching.yml
deleted file mode 100644
index b9063299926..00000000000
--- a/config/feature_flags/development/mergeability_caching.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: mergeability_caching
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68312
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340810
-milestone: '14.4'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/metrics/counts_all/20210216181016_projects_with_expiration_policy_enabled.yml b/config/metrics/counts_all/20210216181016_projects_with_expiration_policy_enabled.yml
index c5e5e7fffd1..3c49f82fa6b 100644
--- a/config/metrics/counts_all/20210216181016_projects_with_expiration_policy_enabled.yml
+++ b/config/metrics/counts_all/20210216181016_projects_with_expiration_policy_enabled.yml
@@ -10,6 +10,9 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181018_projects_with_expiration_policy_enabled_with_keep_n_set_to_1.yml b/config/metrics/counts_all/20210216181018_projects_with_expiration_policy_enabled_with_keep_n_set_to_1.yml
index 2a531707621..e9eaec20453 100644
--- a/config/metrics/counts_all/20210216181018_projects_with_expiration_policy_enabled_with_keep_n_set_to_1.yml
+++ b/config/metrics/counts_all/20210216181018_projects_with_expiration_policy_enabled_with_keep_n_set_to_1.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ keep_n: 1
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181020_projects_with_expiration_policy_enabled_with_keep_n_set_to_5.yml b/config/metrics/counts_all/20210216181020_projects_with_expiration_policy_enabled_with_keep_n_set_to_5.yml
index 4e9df93f171..b6bc610b97a 100644
--- a/config/metrics/counts_all/20210216181020_projects_with_expiration_policy_enabled_with_keep_n_set_to_5.yml
+++ b/config/metrics/counts_all/20210216181020_projects_with_expiration_policy_enabled_with_keep_n_set_to_5.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ keep_n: 5
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181022_projects_with_expiration_policy_enabled_with_keep_n_set_to_10.yml b/config/metrics/counts_all/20210216181022_projects_with_expiration_policy_enabled_with_keep_n_set_to_10.yml
index da0e5006521..906382f2e52 100644
--- a/config/metrics/counts_all/20210216181022_projects_with_expiration_policy_enabled_with_keep_n_set_to_10.yml
+++ b/config/metrics/counts_all/20210216181022_projects_with_expiration_policy_enabled_with_keep_n_set_to_10.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ keep_n: 10
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181024_projects_with_expiration_policy_enabled_with_keep_n_set_to_25.yml b/config/metrics/counts_all/20210216181024_projects_with_expiration_policy_enabled_with_keep_n_set_to_25.yml
index 84bce062d63..2ba5e503c81 100644
--- a/config/metrics/counts_all/20210216181024_projects_with_expiration_policy_enabled_with_keep_n_set_to_25.yml
+++ b/config/metrics/counts_all/20210216181024_projects_with_expiration_policy_enabled_with_keep_n_set_to_25.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ keep_n: 25
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181025_projects_with_expiration_policy_enabled_with_keep_n_set_to_50.yml b/config/metrics/counts_all/20210216181025_projects_with_expiration_policy_enabled_with_keep_n_set_to_50.yml
index 38579a1bdc2..faa4bb4ba8f 100644
--- a/config/metrics/counts_all/20210216181025_projects_with_expiration_policy_enabled_with_keep_n_set_to_50.yml
+++ b/config/metrics/counts_all/20210216181025_projects_with_expiration_policy_enabled_with_keep_n_set_to_50.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ keep_n: 50
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181027_projects_with_expiration_policy_enabled_with_keep_n_set_to_100.yml b/config/metrics/counts_all/20210216181027_projects_with_expiration_policy_enabled_with_keep_n_set_to_100.yml
index 69c59dd2262..bc2963db71f 100644
--- a/config/metrics/counts_all/20210216181027_projects_with_expiration_policy_enabled_with_keep_n_set_to_100.yml
+++ b/config/metrics/counts_all/20210216181027_projects_with_expiration_policy_enabled_with_keep_n_set_to_100.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ keep_n: 100
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216181046_projects_with_expiration_policy_enabled_with_keep_n_unset.yml b/config/metrics/counts_all/20210216181046_projects_with_expiration_policy_enabled_with_keep_n_unset.yml
index 6a98e4ecfd8..912e349a206 100644
--- a/config/metrics/counts_all/20210216181046_projects_with_expiration_policy_enabled_with_keep_n_unset.yml
+++ b/config/metrics/counts_all/20210216181046_projects_with_expiration_policy_enabled_with_keep_n_unset.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: database
+instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
+options:
+ enabled: true
+ keep_n: null
distribution:
- ee
- ce
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index e7da59e5845..0aa0d163972 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -4,37 +4,22 @@ group: Compliance
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Audit Events **(PREMIUM)**
+# Audit events **(PREMIUM)**
-GitLab offers a way to view the changes made within the GitLab server for owners and administrators
-on a [paid plan](https://about.gitlab.com/pricing/).
+Use audit events to track important events, including who performed the related action and when.
+You can use audit events to track, for example:
-GitLab system administrators can also view all audit events by accessing the [`audit_json.log` file](logs/index.md#audit_jsonlog).
-The JSON audit log does not include events that are [only streamed](../development/audit_event_guide/index.md#event-streaming).
+- Who changed the permission level of a particular user for a GitLab project, and when.
+- Who added a new user or removed a user, and when.
-You can:
+The GitLab API, database, and `audit_json.log` record many audit events. Some audit events are only available through
+[streaming audit events](audit_event_streaming.md).
-- Generate an [audit report](audit_reports.md) of audit events.
-- [Stream audit events](audit_event_streaming.md) to an external endpoint.
+You can also generate an [audit report](audit_reports.md) of audit events.
-## Overview
-
-**Audit Events** is a tool for GitLab owners and administrators
-to track important events such as who performed certain actions and the
-time they happened. For example, these actions could be a change to a user
-permission level, who added a new user, or who removed a user.
-
-## Use cases
-
-- Check who changed the permission level of a particular
- user for a GitLab project.
-- Track which users have access to a certain group of projects
- in GitLab, and who gave them that permission level.
-
-## Retention policy
-
-There is no retention policy in place for audit events.
-See the [Specify a retention period for audit events](https://gitlab.com/groups/gitlab-org/-/epics/7917) for more information.
+NOTE:
+You can't configure a retention policy for audit events, but epic
+[7917](https://gitlab.com/groups/gitlab-org/-/epics/7917) proposes to change this.
## List of events
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 9701451ae31..e07aa0e42c1 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -4255,6 +4255,38 @@ variables:
- A global variable defined with `value` but no `description` behaves the same as
[`variables`](#variables).
+### `variables:expand`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353991) in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_raw_variables_in_yaml_config`. Disabled by default.
+
+Use the `expand` keyword to configure a variable to be expandable or not.
+
+**Keyword type**: Global and job keyword. You can use it at the global level, and also at the job level.
+
+**Possible inputs**:
+
+- `true` (default): The variable is expandable.
+- `false`: The variable is not expandable.
+
+**Example of `variables:expand`**:
+
+```yaml
+variables:
+ VAR1: value1
+ VAR2: value2 $VAR1
+ VAR3:
+ value: value3 $VAR1
+ expand: false
+```
+
+- The result of `VAR2` is `value2 value1`.
+- The result of `VAR3` is `value3 $VAR1`.
+
+**Additional details**:
+
+- The `expand` keyword can only be used with the global and job-level `variables` keywords.
+ You can't use it with [`rules:variables`](#rulesvariables) or [`workflow:rules:variables`](#workflowrulesvariables).
+
### `when`
Use `when` to configure the conditions for when jobs run. If not defined in a job,
diff --git a/doc/development/documentation/topic_types/tutorial.md b/doc/development/documentation/topic_types/tutorial.md
index 80fdadcc6b3..1b1426a0465 100644
--- a/doc/development/documentation/topic_types/tutorial.md
+++ b/doc/development/documentation/topic_types/tutorial.md
@@ -17,6 +17,8 @@ Tutorials are learning aids that complement our core documentation.
They do not introduce new features.
Always use the primary [topic types](index.md) to document new features.
+## Tutorial format
+
Tutorials should be in this format:
```markdown
@@ -54,6 +56,9 @@ To do step 2:
1. Another step.
```
+An example of a tutorial that follows this format is
+[Tutorial: Make your first Git commit](../../../tutorials/make_your_first_git_commit.md).
+
## Tutorial page title
Start the page title with `Tutorial:` followed by an active verb, like `Tutorial: Create a website`.
diff --git a/doc/development/sec/security_report_ingestion_overview.md b/doc/development/sec/security_report_ingestion_overview.md
index 11389f388b3..c1d977c2a17 100644
--- a/doc/development/sec/security_report_ingestion_overview.md
+++ b/doc/development/sec/security_report_ingestion_overview.md
@@ -30,7 +30,7 @@ Assumptions:
1. `Security::StoreScansWorker` is called and it schedules `Security::StoreScansService`.
1. `Security::StoreScansService` calls `Security::StoreGroupedScansService`.
1. `Security::StoreGroupedScansService` calls `Security::StoreScanService`.
-1. `Security::StoreScanService` calls `Security::StoreFindingsMetadataService`.
+1. `Security::StoreScanService` calls `Security::StoreFindingsService`.
1. At this point we have `Security::Finding` objects **only**.
At this point, the following things can happen to the `Security::Finding`:
diff --git a/doc/tutorials/agile_sprint.md b/doc/tutorials/agile_sprint.md
index eb6aed92467..8e1886cf464 100644
--- a/doc/tutorials/agile_sprint.md
+++ b/doc/tutorials/agile_sprint.md
@@ -4,7 +4,7 @@ group: Tutorials
info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
---
-# Use GitLab to run an Agile iteration
+# Tutorial: Use GitLab to run an Agile iteration
To run an Agile development iteration in GitLab, you use multiple GitLab features
that work together.
diff --git a/doc/tutorials/index.md b/doc/tutorials/index.md
index f9be21b3887..9b264a8c636 100644
--- a/doc/tutorials/index.md
+++ b/doc/tutorials/index.md
@@ -53,7 +53,7 @@ CI/CD pipelines are used to automatically build, test, and deploy your code.
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
-| [Tutorial: Create and run your first GitLab CI/CD pipeline](../ci/quick_start/index.md) | Create a `.gitlab-ci.yml` file and start a pipeline. | **{star}** |
+| [Create and run your first GitLab CI/CD pipeline](../ci/quick_start/index.md) | Create a `.gitlab-ci.yml` file and start a pipeline. | **{star}** |
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Get started: Learn about CI/CD](https://www.youtube.com/watch?v=sIegJaLy2ug) (9m 02s) | Learn about the `.gitlab-ci.yml` file and how it's used. | **{star}** |
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [CI deep dive](https://www.youtube.com/watch?v=ZVUbmVac-m8&list=PL05JrBw4t0KorkxIFgZGnzzxjZRCGROt_&index=27) (22m 51s) | Take a closer look at pipelines and continuous integration concepts. | |
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [CD deep dive](https://www.youtube.com/watch?v=Cn0rzND-Yjw&list=PL05JrBw4t0KorkxIFgZGnzzxjZRCGROt_&index=10) (47m 54s) | Learn about deploying in GitLab. | |
diff --git a/doc/tutorials/make_your_first_git_commit.md b/doc/tutorials/make_your_first_git_commit.md
index e4fe5486b6d..3df65389c76 100644
--- a/doc/tutorials/make_your_first_git_commit.md
+++ b/doc/tutorials/make_your_first_git_commit.md
@@ -4,7 +4,7 @@ group: Tutorials
info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
---
-# Make your first Git commit
+# Tutorial: Make your first Git commit
This tutorial is going to teach you a little bit about how Git works. It walks
you through the steps of creating your own project, editing a file, and
diff --git a/generator_templates/usage_metric_definition/metric_definition.yml b/generator_templates/usage_metric_definition/metric_definition.yml
index f8920b8d963..ca4f00866ab 100644
--- a/generator_templates/usage_metric_definition/metric_definition.yml
+++ b/generator_templates/usage_metric_definition/metric_definition.yml
@@ -13,7 +13,7 @@ time_frame: <%= time_frame %>
data_source:
data_category: optional
instrumentation_class: <%= class_name %>
-performance_indicator_type:
+performance_indicator_type: []
distribution:
<%= distribution %>
tier:
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 3d739c28624..4d25108cfe5 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -182,6 +182,7 @@ module API
mount ::API::Ci::ResourceGroups
mount ::API::Ci::Runner
mount ::API::Ci::Runners
+ mount ::API::Ci::Pipelines
mount ::API::Ci::Variables
mount ::API::Clusters::AgentTokens
mount ::API::Clusters::Agents
@@ -251,7 +252,6 @@ module API
mount ::API::Branches
mount ::API::Ci::JobArtifacts
mount ::API::Ci::PipelineSchedules
- mount ::API::Ci::Pipelines
mount ::API::Ci::SecureFiles
mount ::API::Ci::Triggers
mount ::API::CommitStatuses
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index e7c54b9cc61..c055512e54e 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -10,12 +10,17 @@ module API
before { authenticate_non_get! }
params do
- requires :id, type: String, desc: 'The project ID'
+ requires :id, type: String, desc: 'The project ID or URL-encoded path', documentation: { example: 11 }
end
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get all Pipelines of the project' do
detail 'This feature was introduced in GitLab 8.11.'
- success Entities::Ci::PipelineBasic
+ success status: 200, model: Entities::Ci::PipelineBasic
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' }
+ ]
+ is_array true
end
helpers do
@@ -31,27 +36,39 @@ module API
else
['unknown']
end
- }
+ },
+ documentation: { example: %w[pending running] }
end
end
params do
use :pagination
optional :scope, type: String, values: %w[running pending finished branches tags],
- desc: 'The scope of pipelines'
+ desc: 'The scope of pipelines',
+ documentation: { example: 'pending' }
optional :status, type: String, values: ::Ci::HasStatus::AVAILABLE_STATUSES,
- desc: 'The status of pipelines'
- optional :ref, type: String, desc: 'The ref of pipelines'
- optional :sha, type: String, desc: 'The sha of pipelines'
- optional :yaml_errors, type: Boolean, desc: 'Returns pipelines with invalid configurations'
- optional :username, type: String, desc: 'The username of the user who triggered pipelines'
- optional :updated_before, type: DateTime, desc: 'Return pipelines updated before the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
- optional :updated_after, type: DateTime, desc: 'Return pipelines updated after the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
+ desc: 'The status of pipelines',
+ documentation: { example: 'pending' }
+ optional :ref, type: String, desc: 'The ref of pipelines',
+ documentation: { example: 'develop' }
+ optional :sha, type: String, desc: 'The sha of pipelines',
+ documentation: { example: 'a91957a858320c0e17f3a0eca7cfacbff50ea29a' }
+ optional :yaml_errors, type: Boolean, desc: 'Returns pipelines with invalid configurations',
+ documentation: { example: false }
+ optional :username, type: String, desc: 'The username of the user who triggered pipelines',
+ documentation: { example: 'root' }
+ optional :updated_before, type: DateTime, desc: 'Return pipelines updated before the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ',
+ documentation: { example: '2015-12-24T15:51:21.880Z' }
+ optional :updated_after, type: DateTime, desc: 'Return pipelines updated after the specified datetime. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ',
+ documentation: { example: '2015-12-24T15:51:21.880Z' }
optional :order_by, type: String, values: ::Ci::PipelinesFinder::ALLOWED_INDEXED_COLUMNS, default: 'id',
- desc: 'Order pipelines'
+ desc: 'Order pipelines',
+ documentation: { example: 'status' }
optional :sort, type: String, values: %w[asc desc], default: 'desc',
- desc: 'Sort pipelines'
- optional :source, type: String, values: ::Ci::Pipeline.sources.keys
+ desc: 'Sort pipelines',
+ documentation: { example: 'asc' }
+ optional :source, type: String, values: ::Ci::Pipeline.sources.keys,
+ documentation: { example: 'push' }
end
get ':id/pipelines', urgency: :low, feature_category: :continuous_integration do
authorize! :read_pipeline, user_project
@@ -63,13 +80,20 @@ module API
desc 'Create a new pipeline' do
detail 'This feature was introduced in GitLab 8.14'
- success Entities::Ci::Pipeline
+ success status: 201, model: Entities::Ci::Pipeline
+ failure [
+ { code: 400, message: 'Bad request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :ref, type: String, desc: 'Reference'
+ requires :ref, type: String, desc: 'Reference',
+ documentation: { example: 'develop' }
optional :variables, type: Array, desc: 'Array of variables available in the pipeline' do
- optional :key, type: String, desc: 'The key of the variable'
- optional :value, type: String, desc: 'The value of the variable'
+ optional :key, type: String, desc: 'The key of the variable', documentation: { example: 'UPLOAD_TO_S3' }
+ optional :value, type: String, desc: 'The value of the variable', documentation: { example: 'true' }
optional :variable_type, type: String, values: ::Ci::PipelineVariable.variable_types.keys, default: 'env_var', desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
end
end
@@ -93,12 +117,18 @@ module API
end
end
- desc 'Gets a the latest pipeline for the project branch' do
+ desc 'Gets the latest pipeline for the project branch' do
detail 'This feature was introduced in GitLab 12.3'
- success Entities::Ci::Pipeline
+ success status: 200, model: Entities::Ci::Pipeline
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- optional :ref, type: String, desc: 'branch ref of pipeline'
+ optional :ref, type: String, desc: 'Branch ref of pipeline. Uses project default branch if not specified.',
+ documentation: { example: 'develop' }
end
get ':id/pipelines/latest', urgency: :low, feature_category: :continuous_integration do
authorize! :read_pipeline, latest_pipeline
@@ -108,10 +138,15 @@ module API
desc 'Gets a specific pipeline for the project' do
detail 'This feature was introduced in GitLab 8.11'
- success Entities::Ci::Pipeline
+ success status: 200, model: Entities::Ci::Pipeline
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
end
get ':id/pipelines/:pipeline_id', urgency: :low, feature_category: :continuous_integration do
authorize! :read_pipeline, pipeline
@@ -120,10 +155,16 @@ module API
end
desc 'Get pipeline jobs' do
- success Entities::Ci::Job
+ success status: 200, model: Entities::Ci::Job
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
optional :include_retried, type: Boolean, default: false, desc: 'Includes retried jobs'
use :optional_scope
use :pagination
@@ -144,10 +185,16 @@ module API
end
desc 'Get pipeline bridge jobs' do
- success Entities::Ci::Bridge
+ success status: 200, model: Entities::Ci::Bridge
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
use :optional_scope
use :pagination
end
@@ -167,10 +214,16 @@ module API
desc 'Gets the variables for a given pipeline' do
detail 'This feature was introduced in GitLab 11.11'
- success Entities::Ci::Variable
+ success status: 200, model: Entities::Ci::Variable
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
+ is_array true
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
end
get ':id/pipelines/:pipeline_id/variables', feature_category: :pipeline_authoring, urgency: :low do
authorize! :read_pipeline_variable, pipeline
@@ -180,10 +233,15 @@ module API
desc 'Gets the test report for a given pipeline' do
detail 'This feature was introduced in GitLab 13.0.'
- success TestReportEntity
+ success status: 200, model: TestReportEntity
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
end
get ':id/pipelines/:pipeline_id/test_report', feature_category: :code_testing, urgency: :low do
authorize! :read_build, pipeline
@@ -193,10 +251,15 @@ module API
desc 'Gets the test report summary for a given pipeline' do
detail 'This feature was introduced in GitLab 14.2'
- success TestReportSummaryEntity
+ success status: 200, model: TestReportSummaryEntity
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
end
get ':id/pipelines/:pipeline_id/test_report_summary', feature_category: :code_testing do
authorize! :read_build, pipeline
@@ -209,7 +272,7 @@ module API
http_codes [[204, 'Pipeline was deleted'], [403, 'Forbidden']]
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
end
delete ':id/pipelines/:pipeline_id', urgency: :low, feature_category: :continuous_integration do
authorize! :destroy_pipeline, pipeline
@@ -223,10 +286,15 @@ module API
desc 'Retry builds in the pipeline' do
detail 'This feature was introduced in GitLab 8.11.'
- success Entities::Ci::Pipeline
+ success status: 201, model: Entities::Ci::Pipeline
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
end
post ':id/pipelines/:pipeline_id/retry', urgency: :low, feature_category: :continuous_integration do
authorize! :update_pipeline, pipeline
@@ -242,10 +310,15 @@ module API
desc 'Cancel all builds in the pipeline' do
detail 'This feature was introduced in GitLab 8.11.'
- success Entities::Ci::Pipeline
+ success status: 200, model: Entities::Ci::Pipeline
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not found' }
+ ]
end
params do
- requires :pipeline_id, type: Integer, desc: 'The pipeline ID'
+ requires :pipeline_id, type: Integer, desc: 'The pipeline ID', documentation: { example: 18 }
end
post ':id/pipelines/:pipeline_id/cancel', urgency: :low, feature_category: :continuous_integration do
authorize! :update_pipeline, pipeline
diff --git a/lib/api/entities/ci/pipeline.rb b/lib/api/entities/ci/pipeline.rb
index a8033a21044..7631cf60dbd 100644
--- a/lib/api/entities/ci/pipeline.rb
+++ b/lib/api/entities/ci/pipeline.rb
@@ -4,13 +4,21 @@ module API
module Entities
module Ci
class Pipeline < PipelineBasic
- expose :before_sha, :tag, :yaml_errors
+ expose :before_sha, documentation: { type: 'string', example: 'a91957a858320c0e17f3a0eca7cfacbff50ea29a' }
+ expose :tag, documentation: { type: 'boolean', example: false }
+ expose :yaml_errors, documentation: { type: 'string', example: "widgets:build: needs 'widgets:test'" }
expose :user, with: Entities::UserBasic
- expose :created_at, :updated_at, :started_at, :finished_at, :committed_at
- expose :duration
- expose :queued_duration
- expose :coverage do |pipeline|
+ expose :created_at, documentation: { type: 'dateTime', example: '2015-12-24T15:51:21.880Z' }
+ expose :updated_at, documentation: { type: 'dateTime', example: '2015-12-24T17:54:31.198Z' }
+ expose :started_at, documentation: { type: 'dateTime', example: '2015-12-24T17:54:30.733Z' }
+ expose :finished_at, documentation: { type: 'dateTime', example: '2015-12-24T17:54:31.198Z' }
+ expose :committed_at, documentation: { type: 'dateTime', example: '2015-12-24T15:51:21.880Z' }
+ expose :duration,
+ documentation: { type: 'integer', desc: 'Time spent running in seconds', example: 127 }
+ expose :queued_duration,
+ documentation: { type: 'integer', desc: 'Time spent enqueued in seconds', example: 63 }
+ expose :coverage, documentation: { type: 'number', format: 'float', example: 98.29 } do |pipeline|
pipeline.present.coverage
end
expose :detailed_status, using: DetailedStatusEntity do |pipeline, options|
diff --git a/lib/api/project_repository_storage_moves.rb b/lib/api/project_repository_storage_moves.rb
index 817dc49c431..5777b8754e7 100644
--- a/lib/api/project_repository_storage_moves.rb
+++ b/lib/api/project_repository_storage_moves.rb
@@ -94,7 +94,7 @@ module API
end
post ':id/repository_storage_moves' do
storage_move = user_project.repository_storage_moves.build(
- declared_params.merge(source_storage_name: user_project.repository_storage)
+ declared_params.compact.merge(source_storage_name: user_project.repository_storage)
)
if storage_move.schedule
diff --git a/lib/api/snippet_repository_storage_moves.rb b/lib/api/snippet_repository_storage_moves.rb
index 7e9d07d8e28..92eb10b3bb8 100644
--- a/lib/api/snippet_repository_storage_moves.rb
+++ b/lib/api/snippet_repository_storage_moves.rb
@@ -104,7 +104,7 @@ module API
end
post ':id/repository_storage_moves' do
storage_move = user_snippet.repository_storage_moves.build(
- declared_params.merge(source_storage_name: user_snippet.repository_storage)
+ declared_params.compact.merge(source_storage_name: user_snippet.repository_storage)
)
if storage_move.schedule
diff --git a/lib/gitlab/ci/config/external/mapper.rb b/lib/gitlab/ci/config/external/mapper.rb
index 2a1060a6059..fc03ac125fd 100644
--- a/lib/gitlab/ci/config/external/mapper.rb
+++ b/lib/gitlab/ci/config/external/mapper.rb
@@ -8,13 +8,15 @@ module Gitlab
include Gitlab::Utils::StrongMemoize
FILE_CLASSES = [
- External::File::Remote,
- External::File::Template,
External::File::Local,
External::File::Project,
+ External::File::Remote,
+ External::File::Template,
External::File::Artifact
].freeze
+ FILE_SUBKEYS = FILE_CLASSES.map { |f| f.name.demodulize.downcase }.freeze
+
Error = Class.new(StandardError)
AmbigiousSpecificationError = Class.new(Error)
TooManyIncludesError = Class.new(Error)
@@ -120,9 +122,13 @@ module Gitlab
file_class.new(location, context)
end.select(&:matching?)
- raise AmbigiousSpecificationError, "Include `#{masked_location(location.to_json)}` needs to match exactly one accessor!" unless matching.one?
-
- matching.first
+ if matching.one?
+ matching.first
+ elsif matching.empty?
+ raise AmbigiousSpecificationError, "`#{masked_location(location.to_json)}` does not have a valid subkey for include. Valid subkeys are: `#{FILE_SUBKEYS.join('`, `')}`"
+ else
+ raise AmbigiousSpecificationError, "Each include must use only one of: `#{FILE_SUBKEYS.join('`, `')}`"
+ end
end
def verify!(location_object)
diff --git a/lib/gitlab/ci/pipeline/chain/limit/active_jobs.rb b/lib/gitlab/ci/pipeline/chain/limit/active_jobs.rb
index 8b26416edf7..2bb32a316be 100644
--- a/lib/gitlab/ci/pipeline/chain/limit/active_jobs.rb
+++ b/lib/gitlab/ci/pipeline/chain/limit/active_jobs.rb
@@ -21,7 +21,10 @@ module Gitlab
class: self.class.name,
message: MESSAGE,
project_id: project.id,
- plan: project.actual_plan_name)
+ plan: project.actual_plan_name,
+ project_path: project.path,
+ jobs_in_alive_pipelines_count: count_jobs_in_alive_pipelines
+ )
end
def break?
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index fba5b183ce5..544211666bb 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -337,17 +337,15 @@ module Gitlab
# rubocop: disable UsageData/LargeTable
base = ::ContainerExpirationPolicy.active
# rubocop: enable UsageData/LargeTable
- results[:projects_with_expiration_policy_enabled] = distinct_count(base, :project_id, start: start, finish: finish)
# rubocop: disable UsageData/LargeTable
- %i[keep_n cadence older_than].each do |option|
+ %i[cadence older_than].each do |option|
::ContainerExpirationPolicy.public_send("#{option}_options").keys.each do |value| # rubocop: disable GitlabSecurity/PublicSend
results["projects_with_expiration_policy_enabled_with_#{option}_set_to_#{value}".to_sym] = distinct_count(base.where(option => value), :project_id, start: start, finish: finish)
end
end
# rubocop: enable UsageData/LargeTable
- results[:projects_with_expiration_policy_enabled_with_keep_n_unset] = distinct_count(base.where(keep_n: nil), :project_id, start: start, finish: finish)
results[:projects_with_expiration_policy_enabled_with_older_than_unset] = distinct_count(base.where(older_than: nil), :project_id, start: start, finish: finish)
results
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 7326c9f81df..3189ad63035 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -11114,9 +11114,6 @@ msgstr ""
msgid "Could not remove %{user} from %{group}. Cannot remove last group owner."
msgstr ""
-msgid "Could not remove %{user} from %{group}. User is not a group member."
-msgstr ""
-
msgid "Could not remove the trigger."
msgstr ""
@@ -48393,6 +48390,9 @@ msgstr ""
msgid "is already associated to a GitLab Issue. New issue will not be associated."
msgstr ""
+msgid "is already linked to this vulnerability"
+msgstr ""
+
msgid "is an invalid IP address range"
msgstr ""
diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb
index b76269f6f93..2b23f177a9d 100644
--- a/spec/controllers/projects/settings/integrations_controller_spec.rb
+++ b/spec/controllers/projects/settings/integrations_controller_spec.rb
@@ -334,6 +334,23 @@ RSpec.describe Projects::Settings::IntegrationsController do
)
end
end
+
+ context 'with chat notification integration' do
+ let_it_be(:integration) { project.create_microsoft_teams_integration(webhook: 'http://webhook.com') }
+ let(:message) { 'Microsoft Teams notifications settings saved and active.' }
+
+ it_behaves_like 'integration update'
+
+ context 'with masked token' do
+ let(:integration_params) { { active: true, webhook: '************' } }
+
+ it_behaves_like 'integration update'
+
+ it 'does not update the webhook' do
+ expect(integration.reload.webhook).to eq('http://webhook.com')
+ end
+ end
+ end
end
describe 'as JSON' do
diff --git a/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric.yml b/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric.yml
index a5bdd378f53..520328f1041 100644
--- a/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric.yml
+++ b/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric.yml
@@ -14,7 +14,7 @@ time_frame: 7d
data_source:
data_category: operational
instrumentation_class: Count
-performance_indicator_type:
+performance_indicator_type: []
distribution:
- ce
# Add here corresponding tiers
diff --git a/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_ee.yml b/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_ee.yml
index 4931285f6cf..1942f33e043 100644
--- a/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_ee.yml
+++ b/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_ee.yml
@@ -14,7 +14,7 @@ time_frame: 7d
data_source:
data_category: optional
instrumentation_class: Count
-performance_indicator_type:
+performance_indicator_type: []
distribution:
- ee
tier:
diff --git a/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_name_suggestions.yml b/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_name_suggestions.yml
index 39472af686d..a72ba5109cc 100644
--- a/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_name_suggestions.yml
+++ b/spec/fixtures/lib/generators/gitlab/usage_metric_definition_generator/sample_metric_with_name_suggestions.yml
@@ -15,7 +15,7 @@ time_frame: 7d
data_source:
data_category: optional
instrumentation_class: Count
-performance_indicator_type:
+performance_indicator_type: []
distribution:
- ce
- ee
diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js
index b2c9118ddcf..203a00577f1 100644
--- a/spec/frontend/editor/schema/ci/ci_schema_spec.js
+++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js
@@ -37,7 +37,8 @@ import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml';
import IncludeNegativeYaml from './yaml_tests/negative_tests/include.yml';
import RulesNegativeYaml from './yaml_tests/negative_tests/rules.yml';
-import VariablesNegativeYaml from './yaml_tests/negative_tests/variables.yml';
+import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
+import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
import JobWhenNegativeYaml from './yaml_tests/negative_tests/job_when.yml';
import ProjectPathIncludeEmptyYaml from './yaml_tests/negative_tests/project_path/include/empty.yml';
@@ -137,7 +138,8 @@ describe('negative tests', () => {
IncludeNegativeYaml,
JobWhenNegativeYaml,
RulesNegativeYaml,
- VariablesNegativeYaml,
+ VariablesInvalidSyntaxDescYaml,
+ VariablesWrongSyntaxUsageExpand,
ProjectPathIncludeEmptyYaml,
ProjectPathIncludeInvalidVariableYaml,
ProjectPathIncludeLeadSlashYaml,
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_syntax_desc.yml
index a7f23cf0d73..4916a6b354e 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_syntax_desc.yml
@@ -1,4 +1,3 @@
-# invalid variable (unknown keyword is used)
variables:
FOO:
value: BAR
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml
new file mode 100644
index 00000000000..62bebfa57e7
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml
@@ -0,0 +1,4 @@
+variables:
+ RAW_VAR:
+ value: Hello $FOO
+ expand: okay
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
index ee71087a72e..53d020c432f 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
@@ -6,3 +6,13 @@ variables:
description: "A single value variable"
DEPLOY_ENVIRONMENT:
description: "A multi-value variable"
+ RAW_VAR:
+ value: "Hello $FOO"
+ expand: false
+
+rspec:
+ script: rspec
+ variables:
+ RAW_VAR2:
+ value: "Hello $DEPLOY_ENVIRONMENT"
+ expand: false \ No newline at end of file
diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js
index 66a917d8052..eb81c38f889 100644
--- a/spec/frontend/work_items/router_spec.js
+++ b/spec/frontend/work_items/router_spec.js
@@ -63,6 +63,14 @@ describe('Work items router', () => {
});
};
+ beforeEach(() => {
+ window.gon = {
+ features: {
+ workItemsMvc2: false,
+ },
+ };
+ });
+
afterEach(() => {
wrapper.destroy();
window.location.hash = '';
@@ -74,7 +82,14 @@ describe('Work items router', () => {
expect(wrapper.findComponent(WorkItemsRoot).exists()).toBe(true);
});
+ it('does not render create work item page on `/new` route if `workItemsMvc2` feature flag is off', async () => {
+ await createComponent('/new');
+
+ expect(wrapper.findComponent(CreateWorkItem).exists()).toBe(false);
+ });
+
it('renders create work item page on `/new` route', async () => {
+ window.gon.features.workItemsMvc2 = true;
await createComponent('/new');
expect(wrapper.findComponent(CreateWorkItem).exists()).toBe(true);
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index e12f5dcee0a..d905568f01e 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -113,7 +113,19 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
it_behaves_like 'logging config file fetch', 'config_file_fetch_template_content_duration_s', 1
end
- context 'when the key is a hash of file and remote' do
+ context 'when the key is not valid' do
+ let(:local_file) { 'secret-file.yml' }
+ let(:values) do
+ { include: { invalid: local_file },
+ image: 'image:1.0' }
+ end
+
+ it 'returns ambigious specification error' do
+ expect { subject }.to raise_error(described_class::AmbigiousSpecificationError, '`{"invalid":"secret-file.yml"}` does not have a valid subkey for include. Valid subkeys are: `local`, `project`, `remote`, `template`, `artifact`')
+ end
+ end
+
+ context 'when the key is a hash of local and remote' do
let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file', 'masked' => true }]) }
let(:local_file) { 'secret-file.yml' }
let(:remote_url) { 'https://gitlab.com/secret-file.yml' }
@@ -123,7 +135,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
end
it 'returns ambigious specification error' do
- expect { subject }.to raise_error(described_class::AmbigiousSpecificationError, 'Include `{"local":"xxxxxxxxxxx.yml","remote":"https://gitlab.com/xxxxxxxxxxx.yml"}` needs to match exactly one accessor!')
+ expect { subject }.to raise_error(described_class::AmbigiousSpecificationError, 'Each include must use only one of: `local`, `project`, `remote`, `template`, `artifact`')
end
end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 475503de7da..c4a6641ff6b 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -484,7 +484,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'raises ConfigError' do
expect { config }.to raise_error(
described_class::ConfigError,
- 'Include `{"remote":"http://url","local":"/local/file.yml"}` needs to match exactly one accessor!'
+ /Each include must use only one of/
)
end
end
@@ -714,7 +714,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'raises an error' do
expect { config }.to raise_error(
described_class::ConfigError,
- /needs to match exactly one accessor!/
+ /does not have a valid subkey for include/
)
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb
index bc453f1502b..c5a5e905d17 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/limit/active_jobs_spec.rb
@@ -69,7 +69,9 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::ActiveJobs do
class: described_class.name,
message: described_class::MESSAGE,
project_id: project.id,
- plan: default_plan.name
+ plan: default_plan.name,
+ project_path: project.path,
+ jobs_in_alive_pipelines_count: step.send(:count_jobs_in_alive_pipelines)
)
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 28318a776f9..5de813f7739 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1407,7 +1407,7 @@ module Gitlab
context "when an array of wrong keyed object is provided" do
let(:include_content) { [{ yolo: "/local.gitlab-ci.yml" }] }
- it_behaves_like 'returns errors', /needs to match exactly one accessor/
+ it_behaves_like 'returns errors', /does not have a valid subkey for include/
end
context "when an array of mixed typed objects is provided" do
@@ -1432,7 +1432,7 @@ module Gitlab
context "when the include type is incorrect" do
let(:include_content) { { name: "/local.gitlab-ci.yml" } }
- it_behaves_like 'returns errors', /needs to match exactly one accessor/
+ it_behaves_like 'returns errors', /does not have a valid subkey for include/
end
end
diff --git a/spec/lib/gitlab/usage/metric_definition_spec.rb b/spec/lib/gitlab/usage/metric_definition_spec.rb
index 90aa524d6bf..931340947a2 100644
--- a/spec/lib/gitlab/usage/metric_definition_spec.rb
+++ b/spec/lib/gitlab/usage/metric_definition_spec.rb
@@ -134,8 +134,9 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
:repair_issue_url | nil
:removed_by_url | 1
- :instrumentation_class | 'Metric_Class'
- :instrumentation_class | 'metricClass'
+ :performance_indicator_type | nil
+ :instrumentation_class | 'Metric_Class'
+ :instrumentation_class | 'metricClass'
end
with_them do
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 04e91547c8e..5d01b4afbcb 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -606,13 +606,12 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
let_it_be(:disabled) { create(:container_expiration_policy, enabled: false) }
let_it_be(:enabled) { create(:container_expiration_policy, enabled: true) }
- %i[keep_n cadence older_than].each do |attribute|
+ %i[cadence older_than].each do |attribute|
ContainerExpirationPolicy.send("#{attribute}_options").keys.each do |value|
let_it_be("container_expiration_policy_with_#{attribute}_set_to_#{value}") { create(:container_expiration_policy, attribute => value) }
end
end
- let_it_be('container_expiration_policy_with_keep_n_set_to_null') { create(:container_expiration_policy, keep_n: nil) }
let_it_be('container_expiration_policy_with_older_than_set_to_null') { create(:container_expiration_policy, older_than: nil) }
let(:inactive_policies) { ::ContainerExpirationPolicy.where(enabled: false) }
@@ -621,23 +620,14 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
subject { described_class.data[:counts] }
it 'gathers usage data' do
- expect(subject[:projects_with_expiration_policy_enabled]).to eq 19
-
- expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_unset]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_1]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_5]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_10]).to eq 13
- expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_25]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_50]).to eq 1
-
expect(subject[:projects_with_expiration_policy_enabled_with_older_than_unset]).to eq 1
expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_7d]).to eq 1
expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_14d]).to eq 1
expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_30d]).to eq 1
expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_60d]).to eq 1
- expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_90d]).to eq 14
+ expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_90d]).to eq 7
- expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1d]).to eq 15
+ expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1d]).to eq 8
expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_7d]).to eq 1
expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_14d]).to eq 1
expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1month]).to eq 1
diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb
index 3cd08479977..4938e1797af 100644
--- a/spec/models/integration_spec.rb
+++ b/spec/models/integration_spec.rb
@@ -234,6 +234,16 @@ RSpec.describe Integration do
end
end
+ describe '#chat?' do
+ it 'is true when integration is chat integration' do
+ expect(build(:mattermost_integration).chat?).to eq(true)
+ end
+
+ it 'is false when integration is not chat integration' do
+ expect(build(:integration).chat?).to eq(false)
+ end
+ end
+
describe '.find_or_initialize_non_project_specific_integration' do
let!(:integration_1) { create(:jira_integration, project_id: nil, group_id: group.id) }
let!(:integration_2) { create(:jira_integration, project: project) }
diff --git a/spec/models/integrations/base_chat_notification_spec.rb b/spec/models/integrations/base_chat_notification_spec.rb
index e9d931bb564..3875f0edec5 100644
--- a/spec/models/integrations/base_chat_notification_spec.rb
+++ b/spec/models/integrations/base_chat_notification_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Integrations::BaseChatNotification do
before do
allow(subject).to receive(:activated?).and_return(true)
allow(subject).to receive(:default_channel_placeholder).and_return('placeholder')
- allow(subject).to receive(:webhook_placeholder).and_return('placeholder')
+ allow(subject).to receive(:webhook_help).and_return('help')
end
it { is_expected.to validate_presence_of :webhook }
@@ -280,9 +280,9 @@ RSpec.describe Integrations::BaseChatNotification do
end
end
- describe '#webhook_placeholder' do
+ describe '#webhook_help' do
it 'raises an error' do
- expect { subject.webhook_placeholder }.to raise_error(NotImplementedError)
+ expect { subject.webhook_help }.to raise_error(NotImplementedError)
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 7e9f933fa22..922f4ac8804 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -3252,14 +3252,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
describe '#mergeable_state?' do
it_behaves_like 'for mergeable_state'
-
- context 'when merge state caching is off' do
- before do
- stub_feature_flags(mergeability_caching: false)
- end
-
- it_behaves_like 'for mergeable_state'
- end
end
describe "#public_merge_status" do
diff --git a/spec/serializers/integrations/field_entity_spec.rb b/spec/serializers/integrations/field_entity_spec.rb
index 7af17cf6df6..4212a1ee6a2 100644
--- a/spec/serializers/integrations/field_entity_spec.rb
+++ b/spec/serializers/integrations/field_entity_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Integrations::FieldEntity do
describe '#as_json' do
context 'with Jira integration' do
- let(:integration) { create(:jira_integration) }
+ let(:integration) { build(:jira_integration) }
context 'with field with type text' do
let(:field) { integration_field('username') }
@@ -59,7 +59,7 @@ RSpec.describe Integrations::FieldEntity do
end
context 'with EmailsOnPush integration' do
- let(:integration) { create(:emails_on_push_integration, send_from_committer_email: '1') }
+ let(:integration) { build(:emails_on_push_integration, send_from_committer_email: '1') }
context 'with field with type checkbox' do
let(:field) { integration_field('send_from_committer_email') }
@@ -111,6 +111,36 @@ RSpec.describe Integrations::FieldEntity do
end
end
end
+
+ context 'with chat integration' do
+ let(:integration) { build(:mattermost_integration) }
+ let(:field) { integration_field('webhook') }
+
+ it 'exposes correct attributes but masks webhook' do
+ expected_hash = {
+ section: nil,
+ type: 'text',
+ name: 'webhook',
+ title: nil,
+ placeholder: nil,
+ help: 'http://mattermost.example.com/hooks/',
+ required: true,
+ choices: nil,
+ value: '************',
+ checkbox_label: nil
+ }
+
+ is_expected.to eq(expected_hash)
+ end
+
+ context 'when webhook was not set' do
+ let(:integration) { build(:mattermost_integration, webhook: nil) }
+
+ it 'does not show the masked webhook' do
+ expect(subject[:value]).to be_nil
+ end
+ end
+ end
end
def integration_field(name)
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index e88fe1b42f0..ef92b6984d5 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -397,9 +397,26 @@ RSpec.describe Issues::CloseService do
end
context 'when issue is not confidential' do
+ let(:expected_payload) do
+ include(
+ event_type: 'issue',
+ object_kind: 'issue',
+ changes: {
+ closed_at: { current: kind_of(Time), previous: nil },
+ state_id: { current: 2, previous: 1 },
+ updated_at: { current: kind_of(Time), previous: kind_of(Time) }
+ },
+ object_attributes: include(
+ closed_at: kind_of(Time),
+ state: 'closed',
+ action: 'close'
+ )
+ )
+ end
+
it 'executes issue hooks' do
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks)
+ expect(project).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
+ expect(project).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
described_class.new(project: project, current_user: user).close_issue(issue)
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 5fe4c693451..5ddf91e167e 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -391,22 +391,61 @@ RSpec.describe Issues::CreateService do
end
end
- it 'executes issue hooks when issue is not confidential' do
- opts = { title: 'Title', description: 'Description', confidential: false }
+ describe 'executing hooks' do
+ let(:opts) { { title: 'Title', description: 'Description' } }
+ let(:expected_payload) do
+ include(
+ event_type: 'issue',
+ object_kind: 'issue',
+ changes: {
+ author_id: { current: user.id, previous: nil },
+ created_at: { current: kind_of(Time), previous: nil },
+ description: { current: opts[:description], previous: nil },
+ id: { current: kind_of(Integer), previous: nil },
+ iid: { current: kind_of(Integer), previous: nil },
+ project_id: { current: project.id, previous: nil },
+ title: { current: opts[:title], previous: nil },
+ updated_at: { current: kind_of(Time), previous: nil }
+ },
+ object_attributes: include(
+ opts.merge(
+ author_id: user.id,
+ project_id: project.id
+ )
+ )
+ )
+ end
+
+ it 'executes issue hooks' do
+ expect(project).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
+ expect(project).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks)
+ described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute
+ end
- described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute
- end
+ context 'when issue is confidential' do
+ let(:expected_payload) do
+ include(
+ event_type: 'confidential_issue',
+ object_kind: 'issue',
+ changes: include(
+ confidential: { current: true, previous: false }
+ ),
+ object_attributes: include(confidential: true)
+ )
+ end
- it 'executes confidential issue hooks when issue is confidential' do
- opts = { title: 'Title', description: 'Description', confidential: true }
+ before do
+ opts[:confidential] = true
+ end
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :confidential_issue_hooks)
+ it 'executes confidential issue hooks' do
+ expect(project).to receive(:execute_hooks).with(expected_payload, :confidential_issue_hooks)
+ expect(project).to receive(:execute_integrations).with(expected_payload, :confidential_issue_hooks)
- described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute
+ end
+ end
end
context 'after_save callback to store_mentions' do
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index 23180f75eb3..655c5085fdc 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -228,18 +228,48 @@ RSpec.describe Issues::MoveService do
end
context 'project issue hooks' do
- let!(:hook) { create(:project_hook, project: old_project, issues_events: true) }
+ let_it_be(:old_project_hook) { create(:project_hook, project: old_project, issues_events: true) }
+ let_it_be(:new_project_hook) { create(:project_hook, project: new_project, issues_events: true) }
+
+ let(:expected_new_project_hook_payload) do
+ hash_including(
+ event_type: 'issue',
+ object_kind: 'issue',
+ object_attributes: include(
+ project_id: new_project.id,
+ state: 'opened',
+ action: 'open'
+ )
+ )
+ end
+
+ let(:expected_old_project_hook_payload) do
+ hash_including(
+ event_type: 'issue',
+ object_kind: 'issue',
+ changes: {
+ state_id: { current: 2, previous: 1 },
+ closed_at: { current: kind_of(Time), previous: nil },
+ updated_at: { current: kind_of(Time), previous: kind_of(Time) }
+ },
+ object_attributes: include(
+ id: old_issue.id,
+ closed_at: kind_of(Time),
+ state: 'closed',
+ action: 'close'
+ )
+ )
+ end
- it 'executes project issue hooks' do
- allow_next_instance_of(WebHookService) do |instance|
- allow(instance).to receive(:execute)
+ it 'executes project issue hooks for both projects' do
+ expect_next_instance_of(WebHookService, new_project_hook, expected_new_project_hook_payload, 'issue_hooks') do |service|
+ expect(service).to receive(:async_execute).once
+ end
+ expect_next_instance_of(WebHookService, old_project_hook, expected_old_project_hook_payload, 'issue_hooks') do |service|
+ expect(service).to receive(:async_execute).once
end
- # Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
- # but since the entire spec run takes place in a transaction, we never
- # actually get to the `after_commit` hook that queues these jobs.
- expect { move_service.execute(old_issue, new_project) }
- .not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
+ move_service.execute(old_issue, new_project)
end
end
diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb
index 477b44f4c2c..6013826f9b1 100644
--- a/spec/services/issues/reopen_service_spec.rb
+++ b/spec/services/issues/reopen_service_spec.rb
@@ -85,9 +85,25 @@ RSpec.describe Issues::ReopenService do
end
context 'when issue is not confidential' do
+ let(:expected_payload) do
+ include(
+ event_type: 'issue',
+ object_kind: 'issue',
+ changes: {
+ closed_at: { current: nil, previous: kind_of(Time) },
+ state_id: { current: 1, previous: 2 },
+ updated_at: { current: kind_of(Time), previous: kind_of(Time) }
+ },
+ object_attributes: include(
+ state: 'opened',
+ action: 'reopen'
+ )
+ )
+ end
+
it 'executes issue hooks' do
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks)
+ expect(project).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
+ expect(project).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
execute
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index ebc870406e8..f1ee62fd589 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -543,7 +543,7 @@ RSpec.describe Issues::UpdateService, :mailer do
end
end
- context 'when decription is not changed' do
+ context 'when description is not changed' do
it 'does not trigger GraphQL description updated subscription' do
expect(GraphqlTriggers).not_to receive(:issuable_description_updated)
@@ -1402,7 +1402,7 @@ RSpec.describe Issues::UpdateService, :mailer do
end
end
- include_examples 'issuable update service' do
+ it_behaves_like 'issuable update service' do
let(:open_issuable) { issue }
let(:closed_issuable) { create(:closed_issue, project: project) }
end
diff --git a/spec/services/merge_requests/mergeability/run_checks_service_spec.rb b/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
index 2d3fd554a1b..c56b38bccc1 100644
--- a/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
@@ -104,18 +104,6 @@ RSpec.describe MergeRequests::Mergeability::RunChecksService, :clean_gitlab_redi
expect(execute.success?).to eq(true)
end
end
-
- context 'when mergeability_caching is turned off' do
- before do
- stub_feature_flags(mergeability_caching: false)
- end
-
- it 'does not call the results store' do
- expect(Gitlab::MergeRequests::Mergeability::ResultsStore).not_to receive(:new)
-
- expect(execute.success?).to eq(true)
- end
- end
end
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index cc520568244..da78f86c7c8 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -1148,7 +1148,7 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
end
end
- include_examples 'issuable update service' do
+ it_behaves_like 'issuable update service' do
let(:open_issuable) { merge_request }
let(:closed_issuable) { create(:closed_merge_request, source_project: project) }
end
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index b4f0cbd8527..fa221e64edc 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -67,13 +67,6 @@ module UsageDataHelpers
projects_with_repositories_enabled
projects_with_error_tracking_enabled
projects_with_enabled_alert_integrations
- projects_with_expiration_policy_enabled
- projects_with_expiration_policy_enabled_with_keep_n_unset
- projects_with_expiration_policy_enabled_with_keep_n_set_to_1
- projects_with_expiration_policy_enabled_with_keep_n_set_to_5
- projects_with_expiration_policy_enabled_with_keep_n_set_to_10
- projects_with_expiration_policy_enabled_with_keep_n_set_to_25
- projects_with_expiration_policy_enabled_with_keep_n_set_to_50
projects_with_expiration_policy_enabled_with_older_than_unset
projects_with_expiration_policy_enabled_with_older_than_set_to_7d
projects_with_expiration_policy_enabled_with_older_than_set_to_14d
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 3ca7a37806b..fbee77307d0 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -3223,7 +3223,6 @@
- './ee/spec/services/security/security_orchestration_policies/sync_opened_merge_requests_service_spec.rb'
- './ee/spec/services/security/security_orchestration_policies/sync_open_merge_requests_head_pipeline_service_spec.rb'
- './ee/spec/services/security/security_orchestration_policies/validate_policy_service_spec.rb'
-- './ee/spec/services/security/store_findings_metadata_service_spec.rb'
- './ee/spec/services/security/store_grouped_scans_service_spec.rb'
- './ee/spec/services/security/store_scan_service_spec.rb'
- './ee/spec/services/security/store_scans_service_spec.rb'
diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb
index 94061b140f4..b85c3904127 100644
--- a/spec/support/services/issuable_update_service_shared_examples.rb
+++ b/spec/support/services/issuable_update_service_shared_examples.rb
@@ -6,18 +6,48 @@ RSpec.shared_examples 'issuable update service' do
end
context 'changing state' do
- before do
- expect(project).to receive(:execute_hooks).once
- end
+ let(:hook_event) { :"#{closed_issuable.class.name.underscore.to_sym}_hooks" }
context 'to reopened' do
- it 'executes hooks only once' do
+ let(:expected_payload) do
+ include(
+ changes: include(
+ state_id: { current: 1, previous: 2 },
+ updated_at: { current: kind_of(Time), previous: kind_of(Time) }
+ ),
+ object_attributes: include(
+ state: 'opened',
+ action: 'reopen'
+ )
+ )
+ end
+
+ it 'executes hooks' do
+ expect(project).to receive(:execute_hooks).with(expected_payload, hook_event)
+ expect(project).to receive(:execute_integrations).with(expected_payload, hook_event)
+
described_class.new(project: project, current_user: user, params: { state_event: 'reopen' }).execute(closed_issuable)
end
end
context 'to closed' do
- it 'executes hooks only once' do
+ let(:expected_payload) do
+ include(
+ changes: include(
+ state_id: { current: 2, previous: 1 },
+ updated_at: { current: kind_of(Time), previous: kind_of(Time) }
+ ),
+ object_attributes: include(
+ state: 'closed',
+ action: 'close'
+ )
+ )
+ end
+
+ it 'executes hooks' do
+ expect(project).to receive(:execute_hooks).with(expected_payload, hook_event)
+ expect(project).to receive(:execute_integrations).with(expected_payload, hook_event)
+
described_class.new(project: project, current_user: user, params: { state_event: 'close' }).execute(open_issuable)
end
end
diff --git a/spec/views/projects/commit/show.html.haml_spec.rb b/spec/views/projects/commit/show.html.haml_spec.rb
index 59182f6e757..4d5c987ce37 100644
--- a/spec/views/projects/commit/show.html.haml_spec.rb
+++ b/spec/views/projects/commit/show.html.haml_spec.rb
@@ -67,5 +67,13 @@ RSpec.describe 'projects/commit/show.html.haml' do
expect(rendered).to have_content("This commit is part of merge request")
expect(rendered).to have_link(merge_request.to_reference, href: merge_request_url)
end
+
+ context 'when merge request is nil' do
+ let(:merge_request) { nil }
+
+ it 'renders the page' do
+ expect { rendered }.not_to raise_error
+ end
+ end
end
end