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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_todo/layout/line_length.yml2
-rw-r--r--.rubocop_todo/layout/space_in_lambda_literal.yml1
-rw-r--r--.rubocop_todo/rails/pluck.yml2
-rw-r--r--.rubocop_todo/rspec/context_wording.yml2
-rw-r--r--.rubocop_todo/rspec/verified_doubles.yml2
-rw-r--r--.rubocop_todo/style/accessor_grouping.yml1
-rw-r--r--app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue2
-rw-r--r--app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue2
-rw-r--r--app/assets/javascripts/releases/components/app_index.vue1
-rw-r--r--app/assets/javascripts/releases/components/release_block.vue8
-rw-r--r--app/assets/javascripts/releases/components/release_block_footer.vue34
-rw-r--r--app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql1
-rw-r--r--app/assets/javascripts/releases/util.js3
-rw-r--r--app/controllers/admin/groups_controller.rb5
-rw-r--r--app/controllers/groups/usage_quotas_controller.rb34
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/namespace.rb6
-rw-r--r--app/models/namespace_setting.rb10
-rw-r--r--app/policies/group_policy.rb4
-rw-r--r--app/services/incident_management/pager_duty/process_webhook_service.rb22
-rw-r--r--app/services/merge_requests/build_service.rb10
-rw-r--r--app/views/admin/application_settings/_runner_registrars_form.html.haml2
-rw-r--r--app/views/groups/_group_admin_settings.html.haml10
-rw-r--r--app/views/groups/milestones/index.html.haml10
-rw-r--r--app/views/groups/usage_quotas/index.html.haml7
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml2
-rw-r--r--app/views/shared/empty_states/_milestones.html.haml2
-rw-r--r--app/views/shared/empty_states/_milestones_tab.html.haml2
-rw-r--r--config/feature_flags/ops/dynamic_image_resizing.yml2
-rw-r--r--config/feature_flags/ops/enforce_memory_watchdog.yml2
-rw-r--r--config/feature_flags/ops/gitlab_memory_watchdog.yml2
-rw-r--r--config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml4
-rw-r--r--config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml4
-rw-r--r--config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml4
-rw-r--r--config/feature_flags/ops/report_jemalloc_stats.yml2
-rw-r--r--config/routes/group.rb2
-rw-r--r--db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb9
-rw-r--r--db/schema_migrations/202209081500541
-rw-r--r--db/structure.sql1
-rw-r--r--doc/development/feature_flags/index.md4
-rw-r--r--doc/development/image_scaling.md4
-rw-r--r--doc/operations/incident_management/incidents.md4
-rw-r--r--doc/security/reset_user_password.md14
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md20
-rw-r--r--doc/user/project/description_templates.md16
-rw-r--r--doc/user/usage_quotas.md3
-rw-r--r--lib/gitlab/incident_management/pager_duty/incident_issue_description.rb9
-rw-r--r--lib/gitlab/merge_requests/message_generator.rb (renamed from lib/gitlab/merge_requests/commit_message_generator.rb)74
-rw-r--r--lib/pager_duty/validator/schemas/message.json101
-rw-r--r--lib/pager_duty/webhook_payload_parser.rb36
-rw-r--r--locale/gitlab.pot18
-rw-r--r--package.json6
-rw-r--r--spec/bin/feature_flag_spec.rb10
-rw-r--r--spec/controllers/admin/groups_controller_spec.rb44
-rw-r--r--spec/fixtures/pager_duty/webhook_incident_trigger.json284
-rw-r--r--spec/frontend/releases/__snapshots__/util_spec.js.snap4
-rw-r--r--spec/frontend/releases/components/release_block_footer_spec.js231
-rw-r--r--spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb21
-rw-r--r--spec/lib/gitlab/merge_requests/message_generator_spec.rb (renamed from spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb)112
-rw-r--r--spec/lib/pager_duty/webhook_payload_parser_spec.rb90
-rw-r--r--spec/models/namespace_setting_spec.rb57
-rw-r--r--spec/policies/group_policy_spec.rb24
-rw-r--r--spec/requests/groups/usage_quotas_controller_spec.rb48
-rw-r--r--spec/routing/group_routing_spec.rb4
-rw-r--r--spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb14
-rw-r--r--spec/services/incident_management/pager_duty/process_webhook_service_spec.rb2
-rw-r--r--spec/services/merge_requests/build_service_spec.rb99
-rw-r--r--spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb4
-rw-r--r--yarn.lock16
69 files changed, 1014 insertions, 583 deletions
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index b465a1b8f79..5ed9cb2a534 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -3269,7 +3269,6 @@ Layout/LineLength:
- 'lib/gitlab/lograge/custom_options.rb'
- 'lib/gitlab/mail_room/authenticator.rb'
- 'lib/gitlab/markdown_cache/active_record/extension.rb'
- - 'lib/gitlab/merge_requests/commit_message_generator.rb'
- 'lib/gitlab/metrics/dashboard/importer.rb'
- 'lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb'
- 'lib/gitlab/metrics/dashboard/stages/cluster_endpoint_inserter.rb'
@@ -4605,7 +4604,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb'
- 'spec/lib/gitlab/lfs/client_spec.rb'
- 'spec/lib/gitlab/mail_room/authenticator_spec.rb'
- - 'spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb'
- 'spec/lib/gitlab/metrics/background_transaction_spec.rb'
- 'spec/lib/gitlab/metrics/boot_time_tracker_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/finder_spec.rb'
diff --git a/.rubocop_todo/layout/space_in_lambda_literal.yml b/.rubocop_todo/layout/space_in_lambda_literal.yml
index 0bd511b1790..1305fc2a203 100644
--- a/.rubocop_todo/layout/space_in_lambda_literal.yml
+++ b/.rubocop_todo/layout/space_in_lambda_literal.yml
@@ -394,7 +394,6 @@ Layout/SpaceInLambdaLiteral:
- 'lib/gitlab/gl_repository.rb'
- 'lib/gitlab/health_checks/server.rb'
- 'lib/gitlab/import_export/import_failure_service.rb'
- - 'lib/gitlab/merge_requests/commit_message_generator.rb'
- 'lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb'
- 'lib/gitlab/metrics/exporter/base_exporter.rb'
- 'lib/gitlab/visibility_level.rb'
diff --git a/.rubocop_todo/rails/pluck.yml b/.rubocop_todo/rails/pluck.yml
index 6aeca1b7ea8..e094a3397a6 100644
--- a/.rubocop_todo/rails/pluck.yml
+++ b/.rubocop_todo/rails/pluck.yml
@@ -126,7 +126,7 @@ Rails/Pluck:
- 'lib/gitlab/git_access.rb'
- 'lib/gitlab/github_import/representation/issue.rb'
- 'lib/gitlab/jira_import/metadata_collector.rb'
- - 'lib/gitlab/merge_requests/commit_message_generator.rb'
+ - 'lib/gitlab/merge_requests/message_generator.rb'
- 'lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb'
- 'lib/gitlab/metrics/dashboard/stages/custom_metrics_details_inserter.rb'
- 'lib/gitlab/sidekiq_config/cli_methods.rb'
diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml
index 523cc1bfad9..7131626d15b 100644
--- a/.rubocop_todo/rspec/context_wording.yml
+++ b/.rubocop_todo/rspec/context_wording.yml
@@ -2023,7 +2023,7 @@ RSpec/ContextWording:
- 'spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb'
- 'spec/lib/gitlab/memory/reports_daemon_spec.rb'
- 'spec/lib/gitlab/memory/watchdog_spec.rb'
- - 'spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb'
+ - 'spec/lib/gitlab/merge_requests/message_generator_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/cache_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importer_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml
index 1d4b6d23e0d..8fa03c775be 100644
--- a/.rubocop_todo/rspec/verified_doubles.yml
+++ b/.rubocop_todo/rspec/verified_doubles.yml
@@ -633,7 +633,7 @@ RSpec/VerifiedDoubles:
- 'spec/lib/gitlab/mail_room/mail_room_spec.rb'
- 'spec/lib/gitlab/manifest_import/metadata_spec.rb'
- 'spec/lib/gitlab/markdown_cache/field_data_spec.rb'
- - 'spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb'
+ - 'spec/lib/gitlab/merge_requests/message_generator_spec.rb'
- 'spec/lib/gitlab/merge_requests/mergeability/redis_interface_spec.rb'
- 'spec/lib/gitlab/metrics/boot_time_tracker_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
diff --git a/.rubocop_todo/style/accessor_grouping.yml b/.rubocop_todo/style/accessor_grouping.yml
index 27b14007afd..a5586813885 100644
--- a/.rubocop_todo/style/accessor_grouping.yml
+++ b/.rubocop_todo/style/accessor_grouping.yml
@@ -57,7 +57,6 @@ Style/AccessorGrouping:
- 'lib/gitlab/http_io.rb'
- 'lib/gitlab/import_export/group/legacy_tree_restorer.rb'
- 'lib/gitlab/import_export/project/tree_restorer.rb'
- - 'lib/gitlab/merge_requests/commit_message_generator.rb'
- 'lib/gitlab/sidekiq_daemon/monitor.rb'
- 'lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb'
- 'lib/gitlab/suggestions/file_suggestion.rb'
diff --git a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue
index 1a470d74b59..0fde87dd0ba 100644
--- a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue
+++ b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue
@@ -90,10 +90,10 @@ export default {
:id="inputId"
:value="percentage"
:state="isValid"
- class="rollout-percentage gl-text-right gl-w-9"
type="number"
min="0"
max="100"
+ size="xs"
@input="onPercentageChange"
/>
<span class="ml-1">%</span>
diff --git a/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue
index 91e1b85d66e..0acb0d4366c 100644
--- a/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue
+++ b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue
@@ -56,10 +56,10 @@ export default {
:id="inputId"
:value="percentage"
:state="isValid"
- class="rollout-percentage gl-text-right gl-w-9"
type="number"
min="0"
max="100"
+ size="xs"
@input="onPercentageChange"
/>
<span class="gl-ml-2">%</span>
diff --git a/app/assets/javascripts/releases/components/app_index.vue b/app/assets/javascripts/releases/components/app_index.vue
index 6dc8240e680..1b360b79b0c 100644
--- a/app/assets/javascripts/releases/components/app_index.vue
+++ b/app/assets/javascripts/releases/components/app_index.vue
@@ -263,6 +263,7 @@ export default {
v-for="(release, index) in releases"
:key="getReleaseKey(release, index)"
:release="release"
+ :sort="sort"
:class="{ 'linked-card': releases.length > 1 && index !== releases.length - 1 }"
/>
diff --git a/app/assets/javascripts/releases/components/release_block.vue b/app/assets/javascripts/releases/components/release_block.vue
index 9c404d74acb..4e9e047dbd5 100644
--- a/app/assets/javascripts/releases/components/release_block.vue
+++ b/app/assets/javascripts/releases/components/release_block.vue
@@ -5,6 +5,7 @@ import SafeHtml from '~/vue_shared/directives/safe_html';
import { scrollToElement } from '~/lib/utils/common_utils';
import { slugify } from '~/lib/utils/text_utility';
import { getLocationHash } from '~/lib/utils/url_utility';
+import { CREATED_ASC } from '~/releases/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import '~/behaviors/markdown/render_gfm';
import EvidenceBlock from './evidence_block.vue';
@@ -32,6 +33,11 @@ export default {
required: true,
default: () => ({}),
},
+ sort: {
+ type: String,
+ required: false,
+ default: CREATED_ASC,
+ },
},
data() {
return {
@@ -119,6 +125,8 @@ export default {
:tag-path="release.tagPath"
:author="release.author"
:released-at="release.releasedAt"
+ :created-at="release.createdAt"
+ :sort="sort"
/>
</div>
</template>
diff --git a/app/assets/javascripts/releases/components/release_block_footer.vue b/app/assets/javascripts/releases/components/release_block_footer.vue
index 3881c83b5c2..85fb7d02a37 100644
--- a/app/assets/javascripts/releases/components/release_block_footer.vue
+++ b/app/assets/javascripts/releases/components/release_block_footer.vue
@@ -3,6 +3,7 @@ import { GlTooltipDirective, GlLink, GlIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { RELEASED_AT_ASC, RELEASED_AT_DESC } from '~/releases/constants';
export default {
name: 'ReleaseBlockFooter',
@@ -46,10 +47,26 @@ export default {
required: false,
default: null,
},
+ createdAt: {
+ type: Date,
+ required: false,
+ default: null,
+ },
+ sort: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
- releasedAtTimeAgo() {
- return this.timeFormatted(this.releasedAt);
+ isSortedByReleaseDate() {
+ return this.sort === RELEASED_AT_ASC || this.sort === RELEASED_AT_DESC;
+ },
+ timeAt() {
+ return this.isSortedByReleaseDate ? this.releasedAt : this.createdAt;
+ },
+ atTimeAgo() {
+ return this.timeFormatted(this.timeAt);
},
userImageAltDescription() {
return this.author && this.author.username
@@ -58,7 +75,10 @@ export default {
},
createdTime() {
const now = new Date();
- const isFuture = now < new Date(this.releasedAt);
+ const isFuture = now < new Date(this.timeAt);
+ if (this.isSortedByReleaseDate) {
+ return isFuture ? __('Will be released') : __('Released');
+ }
return isFuture ? __('Will be created') : __('Created');
},
},
@@ -93,17 +113,17 @@ export default {
</div>
<div
- v-if="releasedAt || author"
+ v-if="timeAt || author"
class="gl-float-left gl-display-flex gl-align-items-center js-author-date-info"
>
<span class="gl-text-secondary">{{ createdTime }}&nbsp;</span>
- <template v-if="releasedAt">
+ <template v-if="timeAt">
<span
v-gl-tooltip.bottom
- :title="tooltipTitle(releasedAt)"
+ :title="tooltipTitle(timeAt)"
class="gl-text-secondary gl-flex-shrink-0"
>
- {{ releasedAtTimeAgo }}&nbsp;
+ {{ atTimeAgo }}&nbsp;
</span>
</template>
diff --git a/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql b/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql
index 3ad66afa259..177dff1823e 100644
--- a/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql
+++ b/app/assets/javascripts/releases/graphql/fragments/release_for_editing.fragment.graphql
@@ -4,6 +4,7 @@ fragment ReleaseForEditing on Release {
tagName
description
releasedAt
+ createdAt
tagPath
assets {
links {
diff --git a/app/assets/javascripts/releases/util.js b/app/assets/javascripts/releases/util.js
index a1027ef08d7..10d7887c0b1 100644
--- a/app/assets/javascripts/releases/util.js
+++ b/app/assets/javascripts/releases/util.js
@@ -15,7 +15,8 @@ const convertScalarProperties = (graphQLRelease) =>
'historicalRelease',
]);
-const convertDateProperties = ({ releasedAt }) => ({
+const convertDateProperties = ({ createdAt, releasedAt }) => ({
+ createdAt: new Date(createdAt),
releasedAt: new Date(releasedAt),
});
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 1395d4bb3b7..8005babe19e 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -51,6 +51,10 @@ class Admin::GroupsController < Admin::ApplicationController
@group.build_admin_note unless @group.admin_note
if @group.update(group_params)
+ unless Gitlab::Utils.to_boolean(group_params['runner_registration_enabled'])
+ Ci::Runners::ResetRegistrationTokenService.new(@group, current_user).execute
+ end
+
redirect_to [:admin, @group], notice: _('Group was successfully updated.')
else
render "edit"
@@ -91,6 +95,7 @@ class Admin::GroupsController < Admin::ApplicationController
:name,
:path,
:request_access_enabled,
+ :runner_registration_enabled,
:visibility_level,
:require_two_factor_authentication,
:two_factor_grace_period,
diff --git a/app/controllers/groups/usage_quotas_controller.rb b/app/controllers/groups/usage_quotas_controller.rb
new file mode 100644
index 00000000000..062d59554d6
--- /dev/null
+++ b/app/controllers/groups/usage_quotas_controller.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Groups
+ class UsageQuotasController < Groups::ApplicationController
+ before_action :authorize_read_usage_quotas!
+ before_action :verify_usage_quotas_enabled!
+
+ feature_category :subscription_usage_reports
+ urgency :low
+
+ def index
+ # To be used in ee/app/controllers/ee/groups/usage_quotas_controller.rb
+ @seat_count_data = seat_count_data
+ @current_namespace_usage = current_namespace_usage
+ @projects_usage = projects_usage
+ end
+
+ private
+
+ def verify_usage_quotas_enabled!
+ render_404 unless Feature.enabled?(:usage_quotas_for_all_editions, group)
+ render_404 if group.has_parent?
+ end
+
+ # To be overriden in ee/app/controllers/ee/groups/usage_quotas_controller.rb
+ def seat_count_data; end
+
+ def current_namespace_usage; end
+
+ def projects_usage; end
+ end
+end
+
+Groups::UsageQuotasController.prepend_mod
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 6987bccadcb..03993506d80 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1383,7 +1383,7 @@ class MergeRequest < ApplicationRecord
def default_merge_commit_message(include_description: false, user: nil)
if self.target_project.merge_commit_template.present? && !include_description
- return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self, current_user: user).merge_message
+ return ::Gitlab::MergeRequests::MessageGenerator.new(merge_request: self, current_user: user).merge_commit_message
end
closes_issues_references = visible_closing_issues_for.map do |issue|
@@ -1407,7 +1407,7 @@ class MergeRequest < ApplicationRecord
def default_squash_commit_message(user: nil)
if self.target_project.squash_commit_template.present?
- return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self, current_user: user).squash_message
+ return ::Gitlab::MergeRequests::MessageGenerator.new(merge_request: self, current_user: user).squash_commit_message
end
title
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index d6df73f1c0e..5ee4fbedd5f 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -131,6 +131,8 @@ class Namespace < ApplicationRecord
to: :namespace_settings, allow_nil: true
delegate :show_diff_preview_in_email, :show_diff_preview_in_email?, :show_diff_preview_in_email=,
to: :namespace_settings
+ delegate :runner_registration_enabled, :runner_registration_enabled?, :runner_registration_enabled=,
+ to: :namespace_settings
delegate :maven_package_requests_forwarding,
:pypi_package_requests_forwarding,
:npm_package_requests_forwarding,
@@ -606,6 +608,10 @@ class Namespace < ApplicationRecord
namespace_settings&.enabled_git_access_protocol
end
+ def all_ancestors_have_runner_registration_enabled?
+ namespace_settings&.all_ancestors_have_runner_registration_enabled?
+ end
+
private
def cluster_enabled_granted?
diff --git a/app/models/namespace_setting.rb b/app/models/namespace_setting.rb
index 3e6371b0c4d..5081d5cdafe 100644
--- a/app/models/namespace_setting.rb
+++ b/app/models/namespace_setting.rb
@@ -59,6 +59,16 @@ class NamespaceSetting < ApplicationRecord
all_ancestors_allow_diff_preview_in_email?
end
+ def runner_registration_enabled?
+ runner_registration_enabled && all_ancestors_have_runner_registration_enabled?
+ end
+
+ def all_ancestors_have_runner_registration_enabled?
+ return true unless namespace.has_parent?
+
+ !self.class.where(namespace_id: namespace.ancestors, runner_registration_enabled: false).exists?
+ end
+
private
def all_ancestors_allow_diff_preview_in_email?
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index fd1c642ad50..858c145de3f 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -83,8 +83,8 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
with_scope :subject
condition(:crm_enabled, score: 0, scope: :subject) { @subject.crm_enabled? }
- condition(:group_runner_registration_allowed, scope: :global) do
- Gitlab::CurrentSettings.valid_runner_registrars.include?('group')
+ condition(:group_runner_registration_allowed, scope: :subject) do
+ Gitlab::CurrentSettings.valid_runner_registrars.include?('group') && @subject.runner_registration_enabled?
end
rule { can?(:read_group) & design_management_enabled }.policy do
diff --git a/app/services/incident_management/pager_duty/process_webhook_service.rb b/app/services/incident_management/pager_duty/process_webhook_service.rb
index a49e639ea62..3ce2674616e 100644
--- a/app/services/incident_management/pager_duty/process_webhook_service.rb
+++ b/app/services/incident_management/pager_duty/process_webhook_service.rb
@@ -9,8 +9,8 @@ module IncidentManagement
# https://developer.pagerduty.com/docs/webhooks/webhook-behavior/#size-limit
PAGER_DUTY_PAYLOAD_SIZE_LIMIT = 55.kilobytes
- # https://developer.pagerduty.com/docs/webhooks/v2-overview/#webhook-types
- PAGER_DUTY_PROCESSABLE_EVENT_TYPES = %w(incident.trigger).freeze
+ # https://developer.pagerduty.com/docs/db0fa8c8984fc-overview#event-types
+ PAGER_DUTY_PROCESSABLE_EVENT_TYPES = %w(incident.triggered).freeze
def initialize(project, payload)
super(project: project)
@@ -33,16 +33,18 @@ module IncidentManagement
attr_reader :payload
def process_incidents
- pager_duty_processable_events.each do |event|
- ::IncidentManagement::PagerDuty::ProcessIncidentWorker.perform_async(project.id, event['incident'])
- end
+ event = pager_duty_processable_event
+ return unless event
+
+ ::IncidentManagement::PagerDuty::ProcessIncidentWorker
+ .perform_async(project.id, event['incident'])
end
- def pager_duty_processable_events
- strong_memoize(:pager_duty_processable_events) do
- ::PagerDuty::WebhookPayloadParser
- .call(payload.to_h)
- .filter { |msg| msg['event'].to_s.in?(PAGER_DUTY_PROCESSABLE_EVENT_TYPES) }
+ def pager_duty_processable_event
+ strong_memoize(:pager_duty_processable_event) do
+ event = ::PagerDuty::WebhookPayloadParser.call(payload.to_h)
+
+ event if event['event'].to_s.in?(PAGER_DUTY_PROCESSABLE_EVENT_TYPES)
end
end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index cc786ac02bd..b9a681f29db 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -224,6 +224,7 @@ module MergeRequests
#
def assign_title_and_description
assign_description_from_repository_template
+ replace_variables_in_description
assign_title_and_description_from_commits
merge_request.title ||= title_from_issue if target_project.issues_enabled? || target_project.external_issue_tracker
merge_request.title ||= source_branch.titleize.humanize
@@ -318,6 +319,15 @@ module MergeRequests
merge_request.description = repository_template.content
end
+ def replace_variables_in_description
+ return unless merge_request.description.present?
+
+ merge_request.description = ::Gitlab::MergeRequests::MessageGenerator.new(
+ merge_request: merge_request,
+ current_user: current_user
+ ).new_mr_description
+ end
+
def issue_iid
strong_memoize(:issue_iid) do
@params_issue_iid || begin
diff --git a/app/views/admin/application_settings/_runner_registrars_form.html.haml b/app/views/admin/application_settings/_runner_registrars_form.html.haml
index 1d6051a06ea..e7c72fb1dac 100644
--- a/app/views/admin/application_settings/_runner_registrars_form.html.haml
+++ b/app/views/admin/application_settings/_runner_registrars_form.html.haml
@@ -5,7 +5,7 @@
.gl-form-group
%span.form-text.gl-mb-3.gl-mt-0
= _('If no options are selected, only administrators can register runners.')
- = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'prevent-users-from-registering-runners'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'restrict-runner-registration-by-all-users-in-an-instance'), target: '_blank', rel: 'noopener noreferrer'
= hidden_field_tag "application_setting[valid_runner_registrars][]", nil
- ApplicationSetting::VALID_RUNNER_REGISTRAR_TYPES.each do |type|
= f.gitlab_ui_checkbox_component :valid_runner_registrars, s_("Runners|Members of the %{type} can register runners") % { type: type },
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index 687a1fb32bf..0b26db64ffa 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -27,3 +27,13 @@
= f.text_field :two_factor_grace_period, class: 'form-control gl-form-input gl-form-input-sm'
%small.form-text.text-gl-muted
= _("Time (in hours) that users are allowed to skip forced configuration of two-factor authentication.")
+
+- if @group.namespace_settings.present?
+ .form-group.gl-form-group
+ %legend.col-form-label.col-form-label
+ = s_('Runners|Runner Registration')
+ - parent_disabled = Gitlab::CurrentSettings.valid_runner_registrars.exclude?('group') || !@group.all_ancestors_have_runner_registration_enabled?
+ = f.gitlab_ui_checkbox_component :runner_registration_enabled,
+ s_('Runners|New group runners can be registered'),
+ checkbox_options: { checked: @group.runner_registration_enabled && !parent_disabled, disabled: parent_disabled },
+ help_text: s_('Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD.').html_safe
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 50a1b474504..f49b69f821d 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -9,13 +9,14 @@
= render 'shared/milestones/search_form'
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @group)
- = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm gl-ml-3", data: { qa_selector: "new_group_milestone_link" }
-
+ = render Pajamas::ButtonComponent.new(href: new_group_milestone_path(@group), variant: :confirm, button_options: { data: { qa_selector: "new_group_milestone_link" }, class: "gl-ml-3" }) do
+ = _('New milestone')
- if @milestones.blank?
= render 'shared/empty_states/milestones_tab', learn_more_path: help_page_path('user/project/milestones/index') do
- if can?(current_user, :admin_milestone, @group)
.text-center
- = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm", data: { qa_selector: "new_group_milestone_link" }
+ = render Pajamas::ButtonComponent.new(href: new_group_milestone_path(@group), variant: :confirm, button_options: { data: { qa_selector: "new_group_milestone_link" }}) do
+ = _('New milestone')
- else
.milestones
%ul.content-list
@@ -29,4 +30,5 @@
= render 'shared/empty_states/milestones', learn_more_path: help_page_path('user/project/milestones/index') do
- if can?(current_user, :admin_milestone, @group)
.text-center
- = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm", data: { qa_selector: "new_group_milestone_link" }
+ = render Pajamas::ButtonComponent.new(href: new_group_milestone_path(@group), variant: :confirm, button_options: { data: { qa_selector: "new_group_milestone_link" }}) do
+ = _('New milestone')
diff --git a/app/views/groups/usage_quotas/index.html.haml b/app/views/groups/usage_quotas/index.html.haml
new file mode 100644
index 00000000000..a8c1071b876
--- /dev/null
+++ b/app/views/groups/usage_quotas/index.html.haml
@@ -0,0 +1,7 @@
+- page_title s_("UsageQuota|Usage")
+
+.gl-alert.gl-alert-no-icon.gl-alert-info.gl-mt-6
+ %h2.gl-alert-title
+ Development
+ .gl-alert-content
+ Placeholder for usage quotas Vue app
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 3634bacb6ec..f3a7037bdab 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -17,7 +17,7 @@
group_path: '' }
- else
= _('Please contact an admin to register runners.')
- = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'prevent-users-from-registering-runners'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'restrict-runner-registration-by-all-users-in-an-instance'), target: '_blank', rel: 'noopener noreferrer'
%hr
diff --git a/app/views/shared/empty_states/_milestones.html.haml b/app/views/shared/empty_states/_milestones.html.haml
index fb69e75370e..0d7dbd1415b 100644
--- a/app/views/shared/empty_states/_milestones.html.haml
+++ b/app/views/shared/empty_states/_milestones.html.haml
@@ -6,7 +6,7 @@
.svg-content
= image_tag 'illustrations/milestone_burndown_chart.svg'
.col-12
- .text-content
+ .text-content.text-center
%h4= s_('Milestones|Use milestones to track issues and merge requests over a fixed period of time')
%p.state-description
= s_('Milestones|Organize issues and merge requests into a cohesive group, and set optional start and due dates. %{learn_more_link}').html_safe % { learn_more_link: learn_more_link }
diff --git a/app/views/shared/empty_states/_milestones_tab.html.haml b/app/views/shared/empty_states/_milestones_tab.html.haml
index f6760b0a3f4..52df30434b4 100644
--- a/app/views/shared/empty_states/_milestones_tab.html.haml
+++ b/app/views/shared/empty_states/_milestones_tab.html.haml
@@ -12,6 +12,6 @@
%h4.text-center= s_('Milestones|There are no closed milestones')
- else
%h4.text-center= s_('Milestones|There are no open milestones')
- %p.state-description
+ %p.state-description.text-center
= s_('Milestones|Create a milestone to better track your issues and merge requests. %{learn_more_link}').html_safe % { learn_more_link: learn_more_link }
= yield
diff --git a/config/feature_flags/ops/dynamic_image_resizing.yml b/config/feature_flags/ops/dynamic_image_resizing.yml
index f456fa8bf1e..2b83b043a22 100644
--- a/config/feature_flags/ops/dynamic_image_resizing.yml
+++ b/config/feature_flags/ops/dynamic_image_resizing.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45050
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/266986
milestone: '13.6'
type: ops
-group: group::memory
+group: group::workspace
default_enabled: true
diff --git a/config/feature_flags/ops/enforce_memory_watchdog.yml b/config/feature_flags/ops/enforce_memory_watchdog.yml
index 1d1f9a4eef0..fae127564c9 100644
--- a/config/feature_flags/ops/enforce_memory_watchdog.yml
+++ b/config/feature_flags/ops/enforce_memory_watchdog.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91910
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367534
milestone: '15.2'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_memory_watchdog.yml b/config/feature_flags/ops/gitlab_memory_watchdog.yml
index 9b995ea607e..f4c9a489864 100644
--- a/config/feature_flags/ops/gitlab_memory_watchdog.yml
+++ b/config/feature_flags/ops/gitlab_memory_watchdog.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91910
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367534
milestone: '15.2'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml b/config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml
index 78e60987a7f..e3ed761f97b 100644
--- a/config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml
+++ b/config/feature_flags/ops/gitlab_service_measuring_projects_create_service.yml
@@ -1,8 +1,8 @@
---
name: gitlab_service_measuring_projects_create_service
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30977
-rollout_issue_url:
+rollout_issue_url:
milestone: '13.0'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml b/config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml
index 309492f8be9..0ce25441f25 100644
--- a/config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml
+++ b/config/feature_flags/ops/gitlab_service_measuring_projects_import_export_export_service.yml
@@ -1,8 +1,8 @@
---
name: gitlab_service_measuring_projects_import_export_export_service
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30977
-rollout_issue_url:
+rollout_issue_url:
milestone: '13.0'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml b/config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml
index 03a8eca99d9..aa01e15aef5 100644
--- a/config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml
+++ b/config/feature_flags/ops/gitlab_service_measuring_projects_import_service.yml
@@ -1,8 +1,8 @@
---
name: gitlab_service_measuring_projects_import_service
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30977
-rollout_issue_url:
+rollout_issue_url:
milestone: '13.0'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/feature_flags/ops/report_jemalloc_stats.yml b/config/feature_flags/ops/report_jemalloc_stats.yml
index 2bbf63d2d78..61fbfa26206 100644
--- a/config/feature_flags/ops/report_jemalloc_stats.yml
+++ b/config/feature_flags/ops/report_jemalloc_stats.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91283
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367845
milestone: '15.2'
type: ops
-group: group::memory
+group: group::application performance
default_enabled: false
diff --git a/config/routes/group.rb b/config/routes/group.rb
index a715596580d..819db0bb6b1 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -61,6 +61,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :packages_and_registries, only: [:show]
end
+ resources :usage_quotas, only: [:index]
+
resource :variables, only: [:show, :update]
resources :children, only: [:index]
diff --git a/db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb b/db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb
new file mode 100644
index 00000000000..7721d0d592c
--- /dev/null
+++ b/db/migrate/20220908150054_add_runner_registration_enabled_to_namespace_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddRunnerRegistrationEnabledToNamespaceSettings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :namespace_settings, :runner_registration_enabled, :boolean, default: true
+ end
+end
diff --git a/db/schema_migrations/20220908150054 b/db/schema_migrations/20220908150054
new file mode 100644
index 00000000000..125498c3e63
--- /dev/null
+++ b/db/schema_migrations/20220908150054
@@ -0,0 +1 @@
+820ab42535cf8291960e41f26395d4f820c0a464b6b1bbf51955d79a16e900ab \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b1f6118344e..33a9f13eddf 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -18147,6 +18147,7 @@ CREATE TABLE namespace_settings (
allow_merge_on_skipped_pipeline boolean DEFAULT false NOT NULL,
only_allow_merge_if_all_discussions_are_resolved boolean DEFAULT false NOT NULL,
default_compliance_framework_id bigint,
+ runner_registration_enabled boolean DEFAULT true,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))
);
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index 500afa8ba1d..21841a31261 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -187,7 +187,7 @@ Only feature flags that have a YAML definition file can be used when running the
```shell
$ bin/feature-flag my_feature_flag
>> Specify the group introducing the feature flag, like `group::apm`:
-?> group::memory
+?> group::application performance
>> URL of the MR introducing the feature flag (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
@@ -202,7 +202,7 @@ create config/feature_flags/development/my_feature_flag.yml
name: my_feature_flag
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38602
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232533
-group: group::memory
+group: group::application performance
type: development
default_enabled: false
```
diff --git a/doc/development/image_scaling.md b/doc/development/image_scaling.md
index 4b19c21a457..a1a5e9122c6 100644
--- a/doc/development/image_scaling.md
+++ b/doc/development/image_scaling.md
@@ -1,6 +1,6 @@
---
-stage: Data Stores
-group: Application Performance
+stage: Manage
+group: Workspace
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
---
diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md
index 3fe57c9d8cf..67a0dba1e1b 100644
--- a/doc/operations/incident_management/incidents.md
+++ b/doc/operations/incident_management/incidents.md
@@ -60,7 +60,8 @@ GitLab to create incident automatically whenever an alert is triggered:
### Create incidents via the PagerDuty webhook
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3.
+> - [PagerDuty V3 Webhook](https://support.pagerduty.com/docs/webhooks) support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/383029) in GitLab 15.7.
You can set up a webhook with PagerDuty to automatically create a GitLab incident
for each PagerDuty incident. This configuration requires you to make changes
@@ -69,7 +70,6 @@ in both PagerDuty and GitLab:
Prerequisites:
- You must have at least the Maintainer role for the project.
-- You must use a PagerDuty [Generic V2 Webhook](https://support.pagerduty.com/docs/webhooks).
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Monitor**
diff --git a/doc/security/reset_user_password.md b/doc/security/reset_user_password.md
index 248737fc908..38c52912d5c 100644
--- a/doc/security/reset_user_password.md
+++ b/doc/security/reset_user_password.md
@@ -7,7 +7,7 @@ type: howto
# Reset a user's password **(FREE SELF)**
-You can reset user passwords by using a Rake task, a Rails console, or the
+You can reset user passwords by using the UI, a Rake task, a Rails console, or the
[Users API](../api/users.md#user-modification).
## Prerequisites
@@ -16,6 +16,18 @@ To reset a user password, you must be an administrator of a self-managed GitLab
The user's new password must meet all [password requirements](../user/profile/user_passwords.md#password-requirements).
+## Use the UI
+
+To reset a user's password in the UI:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Overview > Users**.
+1. For the user whose password you want to update, select **Edit** (**{pencil-square}**).
+1. In the **Password** area, type a password and password confirmation.
+1. Select **Save changes**.
+
+A confirmation is displayed.
+
## Use a Rake task
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52347) in GitLab 13.9.
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index adca9c85af1..52798db870b 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -326,7 +326,7 @@ To set the maximum file size:
1. Enter the maximum file size, in bytes.
1. Select **Save size limits**.
-## Prevent users from registering runners
+## Restrict runner registration by all users in an instance
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22225) in GitLab 14.1.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/368008) in GitLab 15.5.
@@ -335,7 +335,7 @@ GitLab administrators can adjust who is allowed to register runners, by showing
By default, all members of a project and group are able to register runners.
-To change this:
+To restrict all users in an instance from registering runners:
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
@@ -347,6 +347,22 @@ To change this:
WARNING:
When the registration sections are hidden in the UI, members of the project or group that need to register runners must contact the administrators. If you plan to prevent registration, ensure users have access to the runners they need to run jobs.
+## Restrict runner registration by all members in a group
+
+Prerequisites:
+
+- Runner registration must be enabled for [all users in the instance](#restrict-runner-registration-by-all-users-in-an-instance).
+
+GitLab administrators can adjust group permissions to restrict runner registration by group members.
+
+To restrict runner registration by members in a specific group:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Overview > Groups** and find your group.
+1. Select **Edit**.
+1. Clear the **New group runners can be registered** checkbox if you want to disable runner registration by all members in the group. If the setting is read-only, you must enable runner registration for the [instance](#restrict-runner-registration-by-all-users-in-an-instance).
+1. Select **Save changes**.
+
## Troubleshooting
### 413 Request Entity Too Large
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 40c36236932..7774b567e8b 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -83,6 +83,22 @@ NOTE:
You can create shortcut links to create an issue using a designated template.
For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal`. Read more about [creating issues using a URL with prefilled values](issues/managing_issues.md#using-a-url-with-prefilled-values).
+### Supported variables in merge request templates
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89810) in GitLab 15.7.
+
+When you save a merge request for the first time, GitLab replaces these variables in
+your merge request template with their values:
+
+| Variable | Description | Output example |
+|----------|-------------|----------------|
+| `%{all_commits}` | Messages from all commits in the merge request. Limited to 100 most recent commits. Skips commit bodies exceeding 100 KiB and merge commit messages. | `* Feature introduced` <br><br> `This commit implements feature` <br> `Changelog:added` <br><br> `* Bug fixed` <br><br> `* Documentation improved` <br><br>`This commit introduced better docs.` |
+| `%{co_authored_by}` | Names and emails of commit authors in a `Co-authored-by` Git commit trailer format. Limited to authors of 100 most recent commits in merge request. | `Co-authored-by: Zane Doe <zdoe@example.com>` <br> `Co-authored-by: Blake Smith <bsmith@example.com>` |
+| `%{first_commit}` | Full message of the first commit in merge request diff. | `Update README.md` |
+| `%{first_multiline_commit}` | Full message of the first commit that's not a merge commit and has more than one line in message body. Merge request title if all commits aren't multiline. | `Update README.md`<br><br>`Improved project description in readme file.` |
+| `%{source_branch}` | The name of the branch being merged. | `my-feature-branch` |
+| `%{target_branch}` | The name of the branch that the changes are applied to. | `main` |
+
### Set instance-level description templates **(PREMIUM SELF)**
You can set a description template at the **instance level** for issues
diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md
index 7df5ade215d..0e5703caffc 100644
--- a/doc/user/usage_quotas.md
+++ b/doc/user/usage_quotas.md
@@ -126,8 +126,7 @@ Storage types that add to the total namespace storage are:
- Wiki
- Snippets
-If your total namespace storage exceeds the available namespace storage quota, all projects under the namespace are locked.
-A locked project cannot push to the repository, run pipelines and jobs, or build and push packages.
+If your total namespace storage exceeds the available namespace storage quota, all projects under the namespace become read-only. Your ability to write new data is restricted until the read-only state is removed. For more information, see [Restricted actions](../user/read_only_namespaces.md#restricted-actions).
To prevent exceeding the namespace storage quota, you can:
diff --git a/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb b/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
index 6aeeb1d31aa..cbc4f126293 100644
--- a/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
+++ b/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
@@ -19,7 +19,7 @@ module Gitlab
"**Incident key:** #{incident_payload['incident_key']}",
"**Created at:** #{markdown_incident_created_at}",
"**Assignees:** #{markdown_assignees.join(', ')}",
- "**Impacted services:** #{markdown_impacted_services.join(', ')}"
+ "**Impacted service:** #{markdown_impacted_service}"
].join(markdown_line_break)
end
@@ -47,10 +47,9 @@ module Gitlab
end
end
- def markdown_impacted_services
- Array(incident_payload['impacted_services']).map do |is|
- markdown_link(is['summary'], is['url'])
- end
+ def markdown_impacted_service
+ service = incident_payload['impacted_service']
+ markdown_link(service['summary'], service['url']) unless service.nil?
end
def markdown_link(label, url)
diff --git a/lib/gitlab/merge_requests/commit_message_generator.rb b/lib/gitlab/merge_requests/message_generator.rb
index b09e2150012..5113fbdcd7b 100644
--- a/lib/gitlab/merge_requests/commit_message_generator.rb
+++ b/lib/gitlab/merge_requests/message_generator.rb
@@ -1,28 +1,41 @@
# frozen_string_literal: true
module Gitlab
module MergeRequests
- class CommitMessageGenerator
+ class MessageGenerator
def initialize(merge_request:, current_user:)
@merge_request = merge_request
@current_user = @merge_request.metrics&.merged_by || @merge_request.merge_user || current_user
end
- def merge_message
+ def merge_commit_message
return unless @merge_request.target_project.merge_commit_template.present?
- replace_placeholders(@merge_request.target_project.merge_commit_template)
+ replace_placeholders(@merge_request.target_project.merge_commit_template, allowed_placeholders: PLACEHOLDERS)
end
- def squash_message
+ def squash_commit_message
return unless @merge_request.target_project.squash_commit_template.present?
- replace_placeholders(@merge_request.target_project.squash_commit_template, squash: true)
+ replace_placeholders(
+ @merge_request.target_project.squash_commit_template,
+ allowed_placeholders: PLACEHOLDERS,
+ squash: true
+ )
+ end
+
+ def new_mr_description
+ return unless @merge_request.description.present?
+
+ replace_placeholders(
+ @merge_request.description,
+ allowed_placeholders: ALLOWED_NEW_MR_PLACEHOLDERS,
+ keep_carriage_return: true
+ )
end
private
- attr_reader :merge_request
- attr_reader :current_user
+ attr_reader :merge_request, :current_user
PLACEHOLDERS = {
'source_branch' => ->(merge_request, _, _) { merge_request.source_branch.to_s },
@@ -38,11 +51,25 @@ module Gitlab
end,
'description' => ->(merge_request, _, _) { merge_request.description },
'reference' => ->(merge_request, _, _) { merge_request.to_reference(full: true) },
- 'first_commit' => -> (merge_request, _, _) { merge_request.first_commit&.safe_message&.strip },
- 'first_multiline_commit' => -> (merge_request, _, _) { merge_request.first_multiline_commit&.safe_message&.strip.presence || merge_request.title },
+ 'first_commit' => -> (merge_request, _, _) {
+ return unless merge_request.persisted? || merge_request.compare_commits.present?
+
+ merge_request.first_commit&.safe_message&.strip
+ },
+ 'first_multiline_commit' => -> (merge_request, _, _) {
+ merge_request.first_multiline_commit&.safe_message&.strip.presence || merge_request.title
+ },
'url' => ->(merge_request, _, _) { Gitlab::UrlBuilder.build(merge_request) },
- 'reviewed_by' => ->(merge_request, _, _) { merge_request.reviewed_by_users.map { |user| "Reviewed-by: #{user.name} <#{user.commit_email_or_default}>" }.join("\n") },
- 'approved_by' => ->(merge_request, _, _) { merge_request.approved_by_users.map { |user| "Approved-by: #{user.name} <#{user.commit_email_or_default}>" }.join("\n") },
+ 'reviewed_by' => ->(merge_request, _, _) {
+ merge_request.reviewed_by_users
+ .map { |user| "Reviewed-by: #{user.name} <#{user.commit_email_or_default}>" }
+ .join("\n")
+ },
+ 'approved_by' => ->(merge_request, _, _) {
+ merge_request.approved_by_users
+ .map { |user| "Approved-by: #{user.name} <#{user.commit_email_or_default}>" }
+ .join("\n")
+ },
'merged_by' => ->(_, user, _) { "#{user&.name} <#{user&.commit_email_or_default}>" },
'co_authored_by' => ->(merge_request, merged_by, squash) do
commit_author = squash ? merge_request.author : merged_by
@@ -67,15 +94,34 @@ module Gitlab
end
}.freeze
+ # A new merge request that is in the process of being created and hasn't
+ # been persisted to the database.
+ #
+ # Limit the placeholders to a subset of the available ones where the
+ # placeholders wouldn't make sense in context. Disallowed placeholders
+ # will be replaced with an empty string.
+ ALLOWED_NEW_MR_PLACEHOLDERS = %w[
+ source_branch
+ target_branch
+ first_commit
+ first_multiline_commit
+ co_authored_by
+ all_commits
+ ].freeze
+
PLACEHOLDERS_COMBINED_REGEX = /%{(#{Regexp.union(PLACEHOLDERS.keys)})}/.freeze
- def replace_placeholders(message, squash: false)
+ def replace_placeholders(message, allowed_placeholders: [], squash: false, keep_carriage_return: false)
# Convert CRLF to LF.
- message = message.delete("\r")
+ message = message.delete("\r") unless keep_carriage_return
used_variables = message.scan(PLACEHOLDERS_COMBINED_REGEX).map { |value| value[0] }.uniq
values = used_variables.to_h do |variable_name|
- ["%{#{variable_name}}", PLACEHOLDERS[variable_name].call(merge_request, current_user, squash)]
+ replacement = if allowed_placeholders.include?(variable_name)
+ PLACEHOLDERS[variable_name].call(merge_request, current_user, squash)
+ end
+
+ ["%{#{variable_name}}", replacement]
end
names_of_empty_variables = values.filter_map { |name, value| name if value.blank? }
diff --git a/lib/pager_duty/validator/schemas/message.json b/lib/pager_duty/validator/schemas/message.json
index b1a3185cd1a..eeec6657587 100644
--- a/lib/pager_duty/validator/schemas/message.json
+++ b/lib/pager_duty/validator/schemas/message.json
@@ -1,44 +1,77 @@
{
"type": "object",
- "required": ["event", "incident"],
+ "required": [
+ "event"
+ ],
"properties": {
- "event": { "type": "string" },
- "incident": {
+ "event": {
"type": "object",
"required": [
- "html_url",
- "incident_number",
- "title",
- "status",
- "created_at",
- "urgency",
- "incident_key"
- ],
- "properties": {
- "html_url": { "type": "string" },
- "incindent_number": { "type": "integer" },
- "title": { "type": "string" },
- "status": { "type": "string" },
- "created_at": { "type": "string" },
- "urgency": { "type": "string", "enum": ["high", "low"] },
- "incident_key": { "type": ["string", "null"] },
- "assignments": {
- "type": "array",
- "items": {
- "assignee": {
- "type": "array",
- "items": {
- "summary": { "type": "string" },
- "html_url": { "type": "string" }
+ "data"
+ ]
+ },
+ "properties": {
+ "data": {
+ "type": "object",
+ "required": [
+ "html_url",
+ "number",
+ "title",
+ "status",
+ "created_at",
+ "urgency",
+ "incident_key"
+ ],
+ "properties": {
+ "html_url": {
+ "type": "string"
+ },
+ "number": {
+ "type": "integer"
+ },
+ "title": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string"
+ },
+ "urgency": {
+ "type": "string",
+ "enum": [
+ "high",
+ "low"
+ ]
+ },
+ "incident_key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "assignee": {
+ "type": "array",
+ "items": {
+ "summary": {
+ "type": "string"
+ },
+ "html_url": {
+ "type": "string"
+ }
+ }
+ },
+ "service": {
+ "type": "object",
+ "items": {
+ "summary": {
+ "type": "string"
+ },
+ "html_url": {
+ "type": "string"
}
}
- }
- },
- "impacted_services": {
- "type": "array",
- "items": {
- "summary": { "type": "string" },
- "html_url": { "type": "string" }
}
}
}
diff --git a/lib/pager_duty/webhook_payload_parser.rb b/lib/pager_duty/webhook_payload_parser.rb
index c17e3df1a72..e65341232f5 100644
--- a/lib/pager_duty/webhook_payload_parser.rb
+++ b/lib/pager_duty/webhook_payload_parser.rb
@@ -13,7 +13,7 @@ module PagerDuty
end
def call
- Array(payload['messages']).map { |msg| parse_message(msg) }.reject(&:empty?)
+ parse_message(payload)
end
private
@@ -24,41 +24,47 @@ module PagerDuty
return {} unless valid_message?(message)
{
- 'event' => message['event'],
- 'incident' => parse_incident(message['incident'])
+ 'event' => message.dig('event', 'event_type'),
+ 'incident' => parse_incident(message.dig('event', 'data'))
}
end
def parse_incident(incident)
+ return {} unless incident
+
{
'url' => incident['html_url'],
- 'incident_number' => incident['incident_number'],
+ 'incident_number' => incident['number'],
'title' => incident['title'],
'status' => incident['status'],
'created_at' => incident['created_at'],
'urgency' => incident['urgency'],
'incident_key' => incident['incident_key'],
'assignees' => reject_empty(parse_assignees(incident)),
- 'impacted_services' => reject_empty(parse_impacted_services(incident))
+ 'impacted_service' => parse_impacted_service(incident)
}
end
def parse_assignees(incident)
- Array(incident['assignments']).map do |a|
+ return [] unless incident
+
+ Array(incident['assignees']).map do |a|
{
- 'summary' => a.dig('assignee', 'summary'),
- 'url' => a.dig('assignee', 'html_url')
+ 'summary' => a['summary'],
+ 'url' => a['html_url']
}
end
end
- def parse_impacted_services(incident)
- Array(incident['impacted_services']).map do |is|
- {
- 'summary' => is['summary'],
- 'url' => is['html_url']
- }
- end
+ def parse_impacted_service(incident)
+ return {} unless incident
+
+ return {} if incident.dig('service', 'summary').blank? && incident.dig('service', 'html_url').blank?
+
+ {
+ 'summary' => incident.dig('service', 'summary'),
+ 'url' => incident.dig('service', 'html_url')
+ }
end
def reject_empty(entities)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 7debdd79964..2aae66ccfce 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -23894,9 +23894,6 @@ msgstr ""
msgid "Kubernetes deployment not found"
msgstr ""
-msgid "Kubernetes error: %{error_code}"
-msgstr ""
-
msgid "LDAP"
msgstr ""
@@ -34121,6 +34118,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released"
+msgstr ""
+
msgid "Released date"
msgstr ""
@@ -35481,6 +35481,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings &gt CI/CD."
+msgstr ""
+
msgid "Runners|Filter projects"
msgstr ""
@@ -35538,6 +35541,9 @@ msgstr ""
msgid "Runners|Never expires"
msgstr ""
+msgid "Runners|New group runners can be registered"
+msgstr ""
+
msgid "Runners|New registration token generated!"
msgstr ""
@@ -35633,6 +35639,9 @@ msgstr ""
msgid "Runners|Runner %{name} was deleted"
msgstr ""
+msgid "Runners|Runner Registration"
+msgstr ""
+
msgid "Runners|Runner assigned to project."
msgstr ""
@@ -46564,6 +46573,9 @@ msgstr ""
msgid "Will be mapped to"
msgstr ""
+msgid "Will be released"
+msgstr ""
+
msgid "Will deploy to"
msgstr ""
diff --git a/package.json b/package.json
index 322adf7025d..cf9436e33ee 100644
--- a/package.json
+++ b/package.json
@@ -223,12 +223,12 @@
"cheerio": "^1.0.0-rc.9",
"commander": "^2.20.3",
"custom-jquery-matchers": "^2.1.0",
- "eslint": "8.26.0",
+ "eslint": "8.28.0",
"eslint-import-resolver-jest": "3.0.2",
"eslint-import-resolver-webpack": "0.13.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-no-jquery": "2.7.0",
- "eslint-plugin-no-unsanitized": "^4.0.1",
+ "eslint-plugin-no-unsanitized": "^4.0.2",
"gettext-extractor": "^3.5.3",
"gettext-extractor-vue": "^5.0.0",
"glob": "^7.1.6",
@@ -273,4 +273,4 @@
"node": ">=12.22.1",
"yarn": "^1.10.0"
}
-} \ No newline at end of file
+}
diff --git a/spec/bin/feature_flag_spec.rb b/spec/bin/feature_flag_spec.rb
index 03f5ac135f7..cce103965d3 100644
--- a/spec/bin/feature_flag_spec.rb
+++ b/spec/bin/feature_flag_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'bin/feature-flag' do
using RSpec::Parameterized::TableSyntax
describe FeatureFlagCreator do
- let(:argv) { %w[feature-flag-name -t development -g group::memory -i https://url -m http://url] }
+ let(:argv) { %w[feature-flag-name -t development -g group::geo -i https://url -m http://url] }
let(:options) { FeatureFlagOptionParser.parse(argv) }
let(:creator) { described_class.new(options) }
let(:existing_flags) do
@@ -81,8 +81,8 @@ RSpec.describe 'bin/feature-flag' do
:type | %w[foo --type development] | :development
:type | %w[foo -t invalid] | nil
:type | %w[foo --type invalid] | nil
- :group | %w[foo -g group::memory] | 'group::memory'
- :group | %w[foo --group group::memory] | 'group::memory'
+ :group | %w[foo -g group::geo] | 'group::geo'
+ :group | %w[foo --group group::geo] | 'group::geo'
:group | %w[foo -g invalid] | nil
:group | %w[foo --group invalid] | nil
end
@@ -178,12 +178,12 @@ RSpec.describe 'bin/feature-flag' do
end
describe '.read_group' do
- let(:group) { 'group::memory' }
+ let(:group) { 'group::geo' }
it 'reads type from stdin' do
expect(Readline).to receive(:readline).and_return(group)
expect do
- expect(described_class.read_group).to eq('group::memory')
+ expect(described_class.read_group).to eq('group::geo')
end.to output(/Specify the group introducing the feature flag/).to_stdout
end
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index 6085f0e1239..c534cf14327 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -52,4 +52,48 @@ RSpec.describe Admin::GroupsController do
post :create, params: { group: { path: 'test', name: 'test' } }
end
end
+
+ describe 'PUT #update' do
+ subject(:update!) do
+ put :update, params: { id: group.to_param, group: { runner_registration_enabled: new_value } }
+ end
+
+ context 'with runner registration disabled' do
+ let(:runner_registration_enabled) { false }
+ let(:new_value) { '1' }
+
+ it 'updates the setting successfully' do
+ update!
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(group.reload.runner_registration_enabled).to eq(true)
+ end
+
+ it 'does not change the registration token' do
+ expect do
+ update!
+ group.reload
+ end.not_to change(group, :runners_token)
+ end
+ end
+
+ context 'with runner registration enabled' do
+ let(:runner_registration_enabled) { true }
+ let(:new_value) { '0' }
+
+ it 'updates the setting successfully' do
+ update!
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(group.reload.runner_registration_enabled).to eq(false)
+ end
+
+ it 'changes the registration token' do
+ expect do
+ update!
+ group.reload
+ end.to change(group, :runners_token)
+ end
+ end
+ end
end
diff --git a/spec/fixtures/pager_duty/webhook_incident_trigger.json b/spec/fixtures/pager_duty/webhook_incident_trigger.json
index 872297adcf6..9994c8c843b 100644
--- a/spec/fixtures/pager_duty/webhook_incident_trigger.json
+++ b/spec/fixtures/pager_duty/webhook_incident_trigger.json
@@ -1,239 +1,61 @@
{
- "messages": [
- {
- "event": "incident.trigger",
- "log_entries": [
+ "event": {
+ "id": "01DDZJG3TC199M1GJQ7LO67JYS",
+ "event_type": "incident.triggered",
+ "resource_type": "incident",
+ "occurred_at": "2022-11-30T08:46:19.079Z",
+ "agent": {
+ "html_url": "https://gitlab-1.pagerduty.com/users/PIN0B5C",
+ "id": "PIN0B5C",
+ "self": "https://api.pagerduty.com/users/PIN0B5C",
+ "summary": "Rajendra Kadam",
+ "type": "user_reference"
+ },
+ "client": "nil",
+ "data": {
+ "id": "Q1XZUF87W1HB5A",
+ "type": "incident",
+ "self": "https://api.pagerduty.com/incidents/Q1XZUF87W1HB5A",
+ "html_url": "https://gitlab-1.pagerduty.com/incidents/Q1XZUF87W1HB5A",
+ "number": 2,
+ "status": "triggered",
+ "incident_key": "[FILTERED]",
+ "created_at": "2022-11-30T08:46:19Z",
+ "title": "[FILTERED]",
+ "service": {
+ "html_url": "https://gitlab-1.pagerduty.com/services/PK6IKMT",
+ "id": "PK6IKMT",
+ "self": "https://api.pagerduty.com/services/PK6IKMT",
+ "summary": "Test service",
+ "type": "service_reference"
+ },
+ "assignees": [
{
- "id": "R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "type": "trigger_log_entry",
- "summary": "Triggered through the website",
- "self": "https://api.pagerduty.com/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "created_at": "2017-09-26T15:14:36Z",
- "agent": {
- "id": "P553OPV",
- "type": "user_reference",
- "summary": "Laura Haley",
- "self": "https://api.pagerduty.com/users/P553OPV",
- "html_url": "https://webdemo.pagerduty.com/users/P553OPV"
- },
- "channel": {
- "type": "web_trigger",
- "summary": "My new incident",
- "subject": "My new incident",
- "details": "Oh my gosh",
- "details_omitted": false
- },
- "service": {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "incident": {
- "id": "PRORDTY",
- "type": "incident_reference",
- "summary": "[#33] My new incident",
- "self": "https://api.pagerduty.com/incidents/PRORDTY",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY"
- },
- "teams": [
- {
- "id": "P4SI59S",
- "type": "team_reference",
- "summary": "Engineering",
- "self": "https://api.pagerduty.com/teams/P4SI59S",
- "html_url": "https://webdemo.pagerduty.com/teams/P4SI59S"
- }
- ],
- "contexts": [],
- "event_details": {
- "description": "My new incident"
- }
+ "html_url": "https://gitlab-1.pagerduty.com/users/PIN0B5C",
+ "id": "PIN0B5C",
+ "self": "https://api.pagerduty.com/users/PIN0B5C",
+ "summary": "Rajendra Kadam",
+ "type": "user_reference"
}
],
- "webhook": {
- "endpoint_url": "https://requestb.in/18ao6fs1",
- "name": "V2 wabhook",
- "description": null,
- "webhook_object": {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "config": {},
- "outbound_integration": {
- "id": "PJFWPEP",
- "type": "outbound_integration_reference",
- "summary": "Generic V2 Webhook",
- "self": "https://api.pagerduty.com/outbound_integrations/PJFWPEP",
- "html_url": null
- },
- "accounts_addon": null,
- "id": "PKT9NNX",
- "type": "webhook",
- "summary": "V2 wabhook",
- "self": "https://api.pagerduty.com/webhooks/PKT9NNX",
- "html_url": null
+ "escalation_policy": {
+ "html_url": "https://gitlab-1.pagerduty.com/escalation_policies/PWP6XTY",
+ "id": "PWP6XTY",
+ "self": "https://api.pagerduty.com/escalation_policies/PWP6XTY",
+ "summary": "Default",
+ "type": "escalation_policy_reference"
},
- "incident": {
- "incident_number": 33,
- "title": "My new incident",
- "description": "My new incident",
- "created_at": "2017-09-26T15:14:36Z",
- "status": "triggered",
- "pending_actions": [
- {
- "type": "escalate",
- "at": "2017-09-26T15:44:36Z"
- },
- {
- "type": "resolve",
- "at": "2017-09-26T19:14:36Z"
- }
- ],
- "incident_key": null,
- "service": {
- "id": "PN49J75",
- "name": "Production XDB Cluster",
- "description": "This service was created during onboarding on July 5, 2017.",
- "auto_resolve_timeout": 14400,
- "acknowledgement_timeout": 1800,
- "created_at": "2017-07-05T17:33:09Z",
- "status": "critical",
- "last_incident_timestamp": "2017-09-26T15:14:36Z",
- "teams": [
- {
- "id": "P4SI59S",
- "type": "team_reference",
- "summary": "Engineering",
- "self": "https://api.pagerduty.com/teams/P4SI59S",
- "html_url": "https://webdemo.pagerduty.com/teams/P4SI59S"
- }
- ],
- "incident_urgency_rule": {
- "type": "constant",
- "urgency": "high"
- },
- "scheduled_actions": [],
- "support_hours": null,
- "escalation_policy": {
- "id": "PINYWEF",
- "type": "escalation_policy_reference",
- "summary": "Default",
- "self": "https://api.pagerduty.com/escalation_policies/PINYWEF",
- "html_url": "https://webdemo.pagerduty.com/escalation_policies/PINYWEF"
- },
- "addons": [],
- "privilege": null,
- "alert_creation": "create_alerts_and_incidents",
- "integrations": [
- {
- "id": "PUAYF96",
- "type": "generic_events_api_inbound_integration_reference",
- "summary": "API",
- "self": "https://api.pagerduty.com/services/PN49J75/integrations/PUAYF96",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75/integrations/PUAYF96"
- },
- {
- "id": "P90GZUH",
- "type": "generic_email_inbound_integration_reference",
- "summary": "Email",
- "self": "https://api.pagerduty.com/services/PN49J75/integrations/P90GZUH",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75/integrations/P90GZUH"
- }
- ],
- "metadata": {},
- "type": "service",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "assignments": [
- {
- "at": "2017-09-26T15:14:36Z",
- "assignee": {
- "id": "P553OPV",
- "type": "user_reference",
- "summary": "Laura Haley",
- "self": "https://api.pagerduty.com/users/P553OPV",
- "html_url": "https://webdemo.pagerduty.com/users/P553OPV"
- }
- }
- ],
- "acknowledgements": [],
- "last_status_change_at": "2017-09-26T15:14:36Z",
- "last_status_change_by": {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- },
- "first_trigger_log_entry": {
- "id": "R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "type": "trigger_log_entry_reference",
- "summary": "Triggered through the website",
- "self": "https://api.pagerduty.com/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY/log_entries/R2XGXEI3W0FHMSDXHDIBQGBQ5E"
- },
- "escalation_policy": {
- "id": "PINYWEF",
- "type": "escalation_policy_reference",
- "summary": "Default",
- "self": "https://api.pagerduty.com/escalation_policies/PINYWEF",
- "html_url": "https://webdemo.pagerduty.com/escalation_policies/PINYWEF"
- },
- "privilege": null,
- "teams": [
- {
- "id": "P4SI59S",
- "type": "team_reference",
- "summary": "Engineering",
- "self": "https://api.pagerduty.com/teams/P4SI59S",
- "html_url": "https://webdemo.pagerduty.com/teams/P4SI59S"
- }
- ],
- "alert_counts": {
- "all": 0,
- "triggered": 0,
- "resolved": 0
- },
- "impacted_services": [
- {
- "id": "PN49J75",
- "type": "service_reference",
- "summary": "Production XDB Cluster",
- "self": "https://api.pagerduty.com/services/PN49J75",
- "html_url": "https://webdemo.pagerduty.com/services/PN49J75"
- }
- ],
- "is_mergeable": true,
- "basic_alert_grouping": null,
- "alert_grouping": null,
- "metadata": {},
- "external_references": [],
- "importance": null,
- "incidents_responders": [],
- "responder_requests": [],
- "subscriber_requests": [],
- "urgency": "high",
- "id": "PRORDTY",
- "type": "incident",
- "summary": "[#33] My new incident",
- "self": "https://api.pagerduty.com/incidents/PRORDTY",
- "html_url": "https://webdemo.pagerduty.com/incidents/PRORDTY",
- "alerts": [
- {
- "alert_key": "c24117fc42e44b44b4d6876190583378"
- }
- ]
+ "teams": [],
+ "priority": {
+ "html_url": "https://gitlab-1.pagerduty.com/account/incident_priorities",
+ "id": "PKWBGFQ",
+ "self": "https://api.pagerduty.com/priorities/PKWBGFQ",
+ "summary": "P2",
+ "type": "priority_reference"
},
- "id": "69a7ced0-a2cd-11e7-a799-22000a15839c",
- "created_on": "2017-09-26T15:14:36Z"
+ "urgency": "high",
+ "conference_bridge": "nil",
+ "resolve_reason": "nil"
}
- ]
+ }
}
diff --git a/spec/frontend/releases/__snapshots__/util_spec.js.snap b/spec/frontend/releases/__snapshots__/util_spec.js.snap
index d88d79d2cde..4084d734b83 100644
--- a/spec/frontend/releases/__snapshots__/util_spec.js.snap
+++ b/spec/frontend/releases/__snapshots__/util_spec.js.snap
@@ -53,6 +53,7 @@ Object {
"title": "Merge branch 'branch-merged' into 'master'",
},
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ "createdAt": 2019-01-03T00:00:00.000Z,
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:23\\" dir=\\"auto\\">An okay release <gl-emoji title=\\"shrug\\" data-name=\\"shrug\\" data-unicode-version=\\"9.0\\">🤷</gl-emoji></p>",
"evidences": Array [],
"historicalRelease": false,
@@ -150,6 +151,7 @@ Object {
"title": "Merge branch 'branch-merged' into 'master'",
},
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ "createdAt": 2018-12-03T00:00:00.000Z,
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:33\\" dir=\\"auto\\">Best. Release. <strong>Ever.</strong> <gl-emoji title=\\"rocket\\" data-name=\\"rocket\\" data-unicode-version=\\"6.0\\">🚀</gl-emoji></p>",
"evidences": Array [
Object {
@@ -253,6 +255,7 @@ Object {
"sources": Array [],
},
"author": undefined,
+ "createdAt": 2018-12-03T00:00:00.000Z,
"description": "Best. Release. **Ever.** :rocket:",
"evidences": Array [],
"milestones": Array [
@@ -372,6 +375,7 @@ Object {
"title": "Merge branch 'branch-merged' into 'master'",
},
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
+ "createdAt": 2018-12-03T00:00:00.000Z,
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:33\\" dir=\\"auto\\">Best. Release. <strong>Ever.</strong> <gl-emoji title=\\"rocket\\" data-name=\\"rocket\\" data-unicode-version=\\"6.0\\">🚀</gl-emoji></p>",
"evidences": Array [
Object {
diff --git a/spec/frontend/releases/components/release_block_footer_spec.js b/spec/frontend/releases/components/release_block_footer_spec.js
index 8f4efad197f..d13cff49db2 100644
--- a/spec/frontend/releases/components/release_block_footer_spec.js
+++ b/spec/frontend/releases/components/release_block_footer_spec.js
@@ -4,6 +4,7 @@ import { cloneDeep } from 'lodash';
import { nextTick } from 'vue';
import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
import { convertOneReleaseGraphQLResponse } from '~/releases/util';
+import { RELEASED_AT_ASC, RELEASED_AT_DESC, CREATED_ASC, CREATED_DESC } from '~/releases/constants';
import { trimText } from 'helpers/text_helper';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
@@ -43,88 +44,118 @@ describe('Release block footer', () => {
const tagInfoSectionLink = () => tagInfoSection().findComponent(GlLink);
const authorDateInfoSection = () => wrapper.find('.js-author-date-info');
- describe('with all props provided', () => {
- beforeEach(() => factory());
-
- it('renders the commit icon', () => {
- const commitIcon = commitInfoSection().findComponent(GlIcon);
-
- expect(commitIcon.exists()).toBe(true);
- expect(commitIcon.props('name')).toBe('commit');
- });
-
- it('renders the commit SHA with a link', () => {
- const commitLink = commitInfoSectionLink();
-
- expect(commitLink.exists()).toBe(true);
- expect(commitLink.text()).toBe(release.commit.shortId);
- expect(commitLink.attributes('href')).toBe(release.commitPath);
- });
-
- it('renders the tag icon', () => {
- const commitIcon = tagInfoSection().findComponent(GlIcon);
-
- expect(commitIcon.exists()).toBe(true);
- expect(commitIcon.props('name')).toBe('tag');
- });
-
- it('renders the tag name with a link', () => {
- const commitLink = tagInfoSection().findComponent(GlLink);
-
- expect(commitLink.exists()).toBe(true);
- expect(commitLink.text()).toBe(release.tagName);
- expect(commitLink.attributes('href')).toBe(release.tagPath);
- });
-
- it('renders the author and creation time info', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(
- `Created 1 year ago by ${release.author.username}`,
- );
- });
-
- describe('when the release date is in the past', () => {
- it('prefixes the creation info with "Created"', () => {
- expect(trimText(authorDateInfoSection().text())).toEqual(expect.stringMatching(/^Created/));
- });
- });
-
- describe('renders the author and creation time info with future release date', () => {
- beforeEach(() => {
- factory({ releasedAt: mockFutureDate });
- });
-
- it('renders the release date without the author name', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(
- `Will be created in 1 month by ${release.author.username}`,
- );
- });
- });
-
- describe('when the release date is in the future', () => {
- beforeEach(() => {
- factory({ releasedAt: mockFutureDate });
- });
-
- it('prefixes the creation info with "Will be created"', () => {
- expect(trimText(authorDateInfoSection().text())).toEqual(
- expect.stringMatching(/^Will be created/),
- );
- });
- });
-
- it("renders the author's avatar image", () => {
- const avatarImg = authorDateInfoSection().find('img');
-
- expect(avatarImg.exists()).toBe(true);
- expect(avatarImg.attributes('src')).toBe(release.author.avatarUrl);
- });
-
- it("renders a link to the author's profile", () => {
- const authorLink = authorDateInfoSection().findComponent(GlLink);
-
- expect(authorLink.exists()).toBe(true);
- expect(authorLink.attributes('href')).toBe(release.author.webUrl);
- });
+ describe.each`
+ sortFlag | expectedInfoString
+ ${null} | ${'Created'}
+ ${CREATED_ASC} | ${'Created'}
+ ${CREATED_DESC} | ${'Created'}
+ ${RELEASED_AT_ASC} | ${'Released'}
+ ${RELEASED_AT_DESC} | ${'Released'}
+ `('with sorting set to $sortFlag', ({ sortFlag, expectedInfoString }) => {
+ const dateAt =
+ expectedInfoString === 'Created' ? originalRelease.createdAt : originalRelease.releasedAt;
+
+ describe.each`
+ dateType | dateFlag | expectedInfoStringPrefix | expectedDateString
+ ${'empty'} | ${undefined} | ${null} | ${null}
+ ${'in the past'} | ${dateAt} | ${null} | ${'1 year ago'}
+ ${'in the future'} | ${mockFutureDate} | ${'Will be'} | ${'in 1 month'}
+ `(
+ 'with date set to $dateType',
+ ({ dateFlag, expectedInfoStringPrefix, expectedDateString }) => {
+ describe.each`
+ authorType | authorFlag | expectedAuthorString
+ ${'empty'} | ${undefined} | ${null}
+ ${'present'} | ${originalRelease.author} | ${'by administrator'}
+ `('with author set to $authorType', ({ authorFlag, expectedAuthorString }) => {
+ const propsData = { sort: sortFlag, author: authorFlag };
+ if (dateFlag !== '') {
+ propsData.createdAt = dateFlag;
+ propsData.releasedAt = dateFlag;
+ }
+
+ beforeEach(() => {
+ factory({ ...propsData });
+ });
+
+ const expectedString = [
+ expectedInfoStringPrefix,
+ expectedInfoStringPrefix ? expectedInfoString.toLowerCase() : expectedInfoString,
+ expectedDateString,
+ expectedAuthorString,
+ ];
+
+ if (authorFlag || dateFlag) {
+ it('renders the author and creation time info', () => {
+ expect(trimText(authorDateInfoSection().text())).toBe(
+ expectedString.filter((n) => n).join(' '),
+ );
+ });
+ if (authorFlag) {
+ it("renders the author's avatar image", () => {
+ const avatarImg = authorDateInfoSection().find('img');
+
+ expect(avatarImg.exists()).toBe(true);
+ expect(avatarImg.attributes('src')).toBe(release.author.avatarUrl);
+ });
+
+ it("renders a link to the author's profile", () => {
+ const authorLink = authorDateInfoSection().findComponent(GlLink);
+
+ expect(authorLink.exists()).toBe(true);
+ expect(authorLink.attributes('href')).toBe(release.author.webUrl);
+ });
+ } else {
+ it("does not render the author's avatar image", () => {
+ const avatarImg = authorDateInfoSection().find('img');
+
+ expect(avatarImg.exists()).toBe(false);
+ });
+
+ it("does not render a link to the author's profile", () => {
+ const authorLink = authorDateInfoSection().findComponent(GlLink);
+
+ expect(authorLink.exists()).toBe(false);
+ });
+ }
+ } else {
+ it('does not render the author and creation time info', () => {
+ expect(authorDateInfoSection().exists()).toBe(false);
+ });
+ }
+
+ it('renders the commit icon', () => {
+ const commitIcon = commitInfoSection().findComponent(GlIcon);
+
+ expect(commitIcon.exists()).toBe(true);
+ expect(commitIcon.props('name')).toBe('commit');
+ });
+
+ it('renders the commit SHA with a link', () => {
+ const commitLink = commitInfoSectionLink();
+
+ expect(commitLink.exists()).toBe(true);
+ expect(commitLink.text()).toBe(release.commit.shortId);
+ expect(commitLink.attributes('href')).toBe(release.commitPath);
+ });
+
+ it('renders the tag icon', () => {
+ const commitIcon = tagInfoSection().findComponent(GlIcon);
+
+ expect(commitIcon.exists()).toBe(true);
+ expect(commitIcon.props('name')).toBe('tag');
+ });
+
+ it('renders the tag name with a link', () => {
+ const commitLink = tagInfoSection().findComponent(GlLink);
+
+ expect(commitLink.exists()).toBe(true);
+ expect(commitLink.text()).toBe(release.tagName);
+ expect(commitLink.attributes('href')).toBe(release.tagPath);
+ });
+ });
+ },
+ );
});
describe('without any commit info', () => {
@@ -160,40 +191,4 @@ describe('Release block footer', () => {
expect(tagInfoSection().text()).toBe(release.tagName);
});
});
-
- describe('without any author info', () => {
- beforeEach(() => factory({ author: undefined }));
-
- it('renders the release date without the author name', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(`Created 1 year ago`);
- });
- });
-
- describe('future release without any author info', () => {
- beforeEach(() => {
- factory({ author: undefined, releasedAt: mockFutureDate });
- });
-
- it('renders the release date without the author name', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(`Will be created in 1 month`);
- });
- });
-
- describe('without a released at date', () => {
- beforeEach(() => factory({ releasedAt: undefined }));
-
- it('renders the author name without the release date', () => {
- expect(trimText(authorDateInfoSection().text())).toBe(
- `Created by ${release.author.username}`,
- );
- });
- });
-
- describe('without a release date or author info', () => {
- beforeEach(() => factory({ author: undefined, releasedAt: undefined }));
-
- it('does not render any author or release date info', () => {
- expect(authorDateInfoSection().exists()).toBe(false);
- });
- });
});
diff --git a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
index c5288b9afbc..e2c67c68eb7 100644
--- a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
+++ b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
@@ -10,8 +10,8 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
[{ 'summary' => 'Laura Haley', 'url' => 'https://webdemo.pagerduty.com/users/P553OPV' }]
end
- let(:impacted_services) do
- [{ 'summary' => 'Production XDB Cluster', 'url' => 'https://webdemo.pagerduty.com/services/PN49J75' }]
+ let(:impacted_service) do
+ { 'summary' => 'Production XDB Cluster', 'url' => 'https://webdemo.pagerduty.com/services/PN49J75' }
end
let(:incident_payload) do
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
'urgency' => 'high',
'incident_key' => 'SOME-KEY',
'assignees' => assignees,
- 'impacted_services' => impacted_services
+ 'impacted_service' => impacted_service
}
end
@@ -40,7 +40,7 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
**Incident key:** SOME-KEY#{markdown_line_break}
**Created at:** 26 September 2017, 3:14PM (UTC)#{markdown_line_break}
**Assignees:** [Laura Haley](https://webdemo.pagerduty.com/users/P553OPV)#{markdown_line_break}
- **Impacted services:** [Production XDB Cluster](https://webdemo.pagerduty.com/services/PN49J75)
+ **Impacted service:** [Production XDB Cluster](https://webdemo.pagerduty.com/services/PN49J75)
MARKDOWN
)
end
@@ -78,18 +78,15 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d
end
end
- context 'when there are several impacted services' do
- let(:impacted_services) do
- [
- { 'summary' => 'XDB Cluster', 'url' => 'https://xdb.pagerduty.com' },
- { 'summary' => 'BRB Cluster', 'url' => 'https://brb.pagerduty.com' }
- ]
+ context 'when there is an impacted service' do
+ let(:impacted_service) do
+ { 'summary' => 'XDB Cluster', 'url' => 'https://xdb.pagerduty.com' }
end
- it 'impacted services is a list of links' do
+ it 'impacted service is a single link' do
expect(to_s).to include(
<<~MARKDOWN.chomp
- **Impacted services:** [XDB Cluster](https://xdb.pagerduty.com), [BRB Cluster](https://brb.pagerduty.com)
+ **Impacted service:** [XDB Cluster](https://xdb.pagerduty.com)
MARKDOWN
)
end
diff --git a/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb b/spec/lib/gitlab/merge_requests/message_generator_spec.rb
index bdc9879f362..fbdd6a1e56d 100644
--- a/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb
+++ b/spec/lib/gitlab/merge_requests/message_generator_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
+RSpec.describe Gitlab::MergeRequests::MessageGenerator do
let(:merge_commit_template) { nil }
let(:squash_commit_template) { nil }
let(:project) do
@@ -59,7 +59,14 @@ RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
context 'when project has commit template with only the title' do
let(:merge_request) do
- double(:merge_request, title: 'Fixes', target_project: project, to_reference: '!123', metrics: nil, merge_user: nil)
+ double(
+ :merge_request,
+ title: 'Fixes',
+ target_project: project,
+ to_reference: '!123',
+ metrics: nil,
+ merge_user: nil
+ )
end
let(message_template_name) { '%{title}' }
@@ -214,7 +221,7 @@ RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
context 'when project has template with CRLF newlines' do
let(message_template_name) do
- "Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}"
+ "Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}" # rubocop: disable Layout/LineLength
end
it 'converts it to LF newlines' do
@@ -717,8 +724,8 @@ RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
end
end
- describe '#merge_message' do
- let(:result_message) { subject.merge_message }
+ describe '#merge_commit_message' do
+ let(:result_message) { subject.merge_commit_message }
it_behaves_like 'commit message with template', :merge_commit_template
@@ -749,8 +756,8 @@ RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
end
end
- describe '#squash_message' do
- let(:result_message) { subject.squash_message }
+ describe '#squash_commit_message' do
+ let(:result_message) { subject.squash_commit_message }
it_behaves_like 'commit message with template', :squash_commit_template
@@ -780,4 +787,95 @@ RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
end
end
end
+
+ describe '#new_mr_description' do
+ let(:merge_request) do
+ build(
+ :merge_request,
+ source_project: project,
+ target_project: project,
+ target_branch: 'master',
+ source_branch: source_branch,
+ author: author,
+ description: merge_request_description,
+ title: merge_request_title
+ )
+ end
+
+ let(:result_message) { subject.new_mr_description }
+
+ before do
+ compare = CompareService.new(
+ project,
+ merge_request.source_branch
+ ).execute(
+ project,
+ merge_request.target_branch
+ )
+
+ merge_request.compare_commits = compare.commits
+ merge_request.compare = compare
+ end
+
+ context 'when project has template with all variables' do
+ let(:merge_request_description) { <<~MSG.rstrip }
+ source_branch:%{source_branch}
+ target_branch:%{target_branch}
+ title:%{title}
+ issues:%{issues}
+ description:%{description}
+ first_commit:%{first_commit}
+ first_multiline_commit:%{first_multiline_commit}
+ url:%{url}
+ approved_by:%{approved_by}
+ merged_by:%{merged_by}
+ co_authored_by:%{co_authored_by}
+ all_commits:%{all_commits}
+ MSG
+
+ it 'renders only variables specific to a new non-persisted merge request' do
+ expect(result_message).to eq <<~MSG.rstrip
+ source_branch:feature
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ first_multiline_commit:Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:Co-authored-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ all_commits:* Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ MSG
+ end
+
+ context 'when no first commit exists' do
+ let(:source_branch) { 'master' }
+
+ it 'does not populate any commit-related variables' do
+ expect(result_message).to eq <<~MSG.rstrip
+ source_branch:master
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:
+ first_multiline_commit:Bugfix
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:
+ all_commits:
+ MSG
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/pager_duty/webhook_payload_parser_spec.rb b/spec/lib/pager_duty/webhook_payload_parser_spec.rb
index 647f19e3d3a..1606d03c746 100644
--- a/spec/lib/pager_duty/webhook_payload_parser_spec.rb
+++ b/spec/lib/pager_duty/webhook_payload_parser_spec.rb
@@ -10,23 +10,27 @@ RSpec.describe PagerDuty::WebhookPayloadParser do
let(:triggered_event) do
{
- 'event' => 'incident.trigger',
+ 'event' => 'incident.triggered',
'incident' => {
- 'url' => 'https://webdemo.pagerduty.com/incidents/PRORDTY',
- 'incident_number' => 33,
- 'title' => 'My new incident',
+ 'url' => 'https://gitlab-1.pagerduty.com/incidents/Q1XZUF87W1HB5A',
+ 'incident_number' => 2,
+ 'title' => '[FILTERED]',
'status' => 'triggered',
- 'created_at' => '2017-09-26T15:14:36Z',
+ 'created_at' => '2022-11-30T08:46:19Z',
'urgency' => 'high',
- 'incident_key' => nil,
- 'assignees' => [{
- 'summary' => 'Laura Haley',
- 'url' => 'https://webdemo.pagerduty.com/users/P553OPV'
- }],
- 'impacted_services' => [{
- 'summary' => 'Production XDB Cluster',
- 'url' => 'https://webdemo.pagerduty.com/services/PN49J75'
- }]
+ 'incident_key' => '[FILTERED]',
+ 'assignees' =>
+ [
+ {
+ 'summary' => 'Rajendra Kadam',
+ 'url' => 'https://gitlab-1.pagerduty.com/users/PIN0B5C'
+ }
+ ],
+ 'impacted_service' =>
+ {
+ 'summary' => 'Test service',
+ 'url' => 'https://gitlab-1.pagerduty.com/services/PK6IKMT'
+ }
}
}
end
@@ -37,74 +41,50 @@ RSpec.describe PagerDuty::WebhookPayloadParser do
let(:payload) { Gitlab::Json.parse(fixture_file) }
it 'returns parsed payload' do
- is_expected.to eq([triggered_event])
+ is_expected.to eq(triggered_event)
end
context 'when assignments summary and html_url are blank' do
before do
- payload['messages'].each do |m|
- m['incident']['assignments'] = [{ 'assignee' => { 'summary' => '', 'html_url' => '' } }]
- end
+ payload['event']['data']['assignees'] = [{ 'summary' => '', 'html_url' => '' }]
end
it 'returns parsed payload with blank assignees' do
- assignees = parse.map { |events| events['incident'].slice('assignees') }
+ assignees = parse['incident'].slice('assignees')
- expect(assignees).to eq([{ 'assignees' => [] }])
+ expect(assignees).to eq({ 'assignees' => [] })
end
end
context 'when impacted_services summary and html_url are blank' do
before do
- payload['messages'].each do |m|
- m['incident']['impacted_services'] = [{ 'summary' => '', 'html_url' => '' }]
- end
+ payload['event']['data']['service'] = { 'summary' => '', 'html_url' => '' }
end
- it 'returns parsed payload with blank assignees' do
- assignees = parse.map { |events| events['incident'].slice('impacted_services') }
+ it 'returns parsed payload with blank impacted service' do
+ assignees = parse['incident'].slice('impacted_service')
- expect(assignees).to eq([{ 'impacted_services' => [] }])
+ expect(assignees).to eq({ 'impacted_service' => {} })
end
end
end
context 'when payload schema is invalid' do
- let(:payload) { { 'messages' => [{ 'event' => 'incident.trigger' }] } }
+ let(:payload) { { 'event' => 'incident.triggered' } }
- it 'returns payload with blank incidents' do
- is_expected.to eq([])
+ it 'returns payload with blank incident' do
+ is_expected.to eq({})
end
end
- context 'when payload consists of two messages' do
- context 'when one of the messages has no incident data' do
- let(:payload) do
- valid_payload = Gitlab::Json.parse(fixture_file)
- event = { 'event' => 'incident.trigger' }
- valid_payload['messages'] = valid_payload['messages'].append(event)
- valid_payload
- end
-
- it 'returns parsed payload with valid events only' do
- is_expected.to eq([triggered_event])
- end
+ context 'when event is unknown' do
+ let(:payload) do
+ valid_payload = Gitlab::Json.parse(fixture_file)
+ valid_payload['event'] = 'incident.unknown'
end
- context 'when one of the messages has unknown event' do
- let(:payload) do
- valid_payload = Gitlab::Json.parse(fixture_file)
- event = { 'event' => 'incident.unknown', 'incident' => valid_payload['messages'].first['incident'] }
- valid_payload['messages'] = valid_payload['messages'].append(event)
- valid_payload
- end
-
- it 'returns parsed payload' do
- unknown_event = triggered_event.dup
- unknown_event['event'] = 'incident.unknown'
-
- is_expected.to contain_exactly(triggered_event, unknown_event)
- end
+ it 'returns empty payload' do
+ is_expected.to be_empty
end
end
end
diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb
index 17c49e13c85..e06a6a30f9a 100644
--- a/spec/models/namespace_setting_spec.rb
+++ b/spec/models/namespace_setting_spec.rb
@@ -178,6 +178,63 @@ RSpec.describe NamespaceSetting, type: :model do
end
end
+ describe '#runner_registration_enabled?' do
+ context 'when not a subgroup' do
+ let_it_be(:settings) { create(:namespace_settings) }
+ let_it_be(:group) { create(:group, namespace_settings: settings) }
+
+ before do
+ group.update!(runner_registration_enabled: runner_registration_enabled)
+ end
+
+ context 'when :runner_registration_enabled is false' do
+ let(:runner_registration_enabled) { false }
+
+ it 'returns false' do
+ expect(group.runner_registration_enabled?).to be_falsey
+ end
+
+ it 'does not query the db' do
+ expect { group.runner_registration_enabled? }.not_to exceed_query_limit(0)
+ end
+ end
+
+ context 'when :runner_registration_enabled is true' do
+ let(:runner_registration_enabled) { true }
+
+ it 'returns true' do
+ expect(group.runner_registration_enabled?).to be_truthy
+ end
+ end
+ end
+
+ context 'when a group has parent groups' do
+ let_it_be(:grandparent) { create(:group) }
+ let_it_be(:parent) { create(:group, parent: grandparent) }
+ let_it_be(:group) { create(:group, parent: parent) }
+
+ before do
+ grandparent.update!(runner_registration_enabled: runner_registration_enabled)
+ end
+
+ context 'when a parent group has runner registration disabled' do
+ let(:runner_registration_enabled) { false }
+
+ it 'returns false' do
+ expect(group.runner_registration_enabled?).to be_falsey
+ end
+ end
+
+ context 'when all parent groups have runner registration enabled' do
+ let(:runner_registration_enabled) { true }
+
+ it 'returns true' do
+ expect(group.runner_registration_enabled?).to be_truthy
+ end
+ end
+ end
+ end
+
describe '#delayed_project_removal' do
it_behaves_like 'a cascading namespace setting boolean attribute', settings_attribute_name: :delayed_project_removal
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 8cf121342a3..65abb43b6c4 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -1191,12 +1191,28 @@ RSpec.describe GroupPolicy do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:register_group_runners) }
+ context 'with specific group runner registration disabled' do
+ before do
+ group.runner_registration_enabled = false
+ end
+
+ it { is_expected.to be_allowed(:register_group_runners) }
+ end
+
context 'with group runner registration disabled' do
before do
stub_application_setting(valid_runner_registrars: ['project'])
end
it { is_expected.to be_allowed(:register_group_runners) }
+
+ context 'with specific group runner registration disabled' do
+ before do
+ group.runner_registration_enabled = false
+ end
+
+ it { is_expected.to be_allowed(:register_group_runners) }
+ end
end
end
@@ -1217,6 +1233,14 @@ RSpec.describe GroupPolicy do
it { is_expected.to be_disallowed(:register_group_runners) }
end
+
+ context 'with specific group runner registration disabled' do
+ before do
+ group.runner_registration_enabled = false
+ end
+
+ it { is_expected.to be_disallowed(:register_group_runners) }
+ end
end
context 'with maintainer' do
diff --git a/spec/requests/groups/usage_quotas_controller_spec.rb b/spec/requests/groups/usage_quotas_controller_spec.rb
new file mode 100644
index 00000000000..3772fc2ba3b
--- /dev/null
+++ b/spec/requests/groups/usage_quotas_controller_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::UsageQuotasController, feature_category: :subscription_usage_reports do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:user) { create(:user) }
+
+ subject(:request) { get group_usage_quotas_path(group) }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'GET /groups/*group_id/-/usage_quotas' do
+ context 'when user has read_usage_quotas permission' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'renders index with 200 status code' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to match(/Placeholder for usage quotas Vue app/)
+ end
+
+ it 'renders 404 page if subgroup' do
+ get group_usage_quotas_path(subgroup)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user does not have read_usage_quotas permission' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it 'renders not_found' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+end
diff --git a/spec/routing/group_routing_spec.rb b/spec/routing/group_routing_spec.rb
index 68e619e5246..54fbe9e962d 100644
--- a/spec/routing/group_routing_spec.rb
+++ b/spec/routing/group_routing_spec.rb
@@ -83,6 +83,10 @@ RSpec.shared_examples 'groups routing' do
it 'routes to the observability controller manage method' do
expect(get("groups/#{group_path}/-/observability/manage")).to route_to('groups/observability#manage', group_id: group_path)
end
+
+ it 'routes to the usage quotas controller' do
+ expect(get("groups/#{group_path}/-/usage_quotas")).to route_to("groups/usage_quotas#index", group_id: group_path)
+ end
end
RSpec.describe "Groups", "routing" do
diff --git a/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb b/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
index 572b1a20166..2fda789cf56 100644
--- a/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
+++ b/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe IncidentManagement::PagerDuty::CreateIncidentIssueService do
let(:webhook_payload) { Gitlab::Json.parse(fixture_file('pager_duty/webhook_incident_trigger.json')) }
let(:parsed_payload) { ::PagerDuty::WebhookPayloadParser.call(webhook_payload) }
- let(:incident_payload) { parsed_payload.first['incident'] }
+ let(:incident_payload) { parsed_payload['incident'] }
subject(:execute) { described_class.new(project, incident_payload).execute }
@@ -41,14 +41,14 @@ RSpec.describe IncidentManagement::PagerDuty::CreateIncidentIssueService do
expect(execute.payload[:issue].description).to eq(
<<~MARKDOWN.chomp
- **Incident:** [My new incident](https://webdemo.pagerduty.com/incidents/PRORDTY)#{markdown_line_break}
- **Incident number:** 33#{markdown_line_break}
+ **Incident:** [[FILTERED]](https://gitlab-1.pagerduty.com/incidents/Q1XZUF87W1HB5A)#{markdown_line_break}
+ **Incident number:** 2#{markdown_line_break}
**Urgency:** high#{markdown_line_break}
**Status:** triggered#{markdown_line_break}
- **Incident key:** #{markdown_line_break}
- **Created at:** 26 September 2017, 3:14PM (UTC)#{markdown_line_break}
- **Assignees:** [Laura Haley](https://webdemo.pagerduty.com/users/P553OPV)#{markdown_line_break}
- **Impacted services:** [Production XDB Cluster](https://webdemo.pagerduty.com/services/PN49J75)
+ **Incident key:** [FILTERED]#{markdown_line_break}
+ **Created at:** 30 November 2022, 8:46AM (UTC)#{markdown_line_break}
+ **Assignees:** [Rajendra Kadam](https://gitlab-1.pagerduty.com/users/PIN0B5C)#{markdown_line_break}
+ **Impacted service:** [Test service](https://gitlab-1.pagerduty.com/services/PK6IKMT)
MARKDOWN
)
end
diff --git a/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb b/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
index 8b6eb21c25d..e2aba0b61af 100644
--- a/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
+++ b/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe IncidentManagement::PagerDuty::ProcessWebhookService do
end
it 'processes issues' do
- incident_payload = ::PagerDuty::WebhookPayloadParser.call(webhook_payload).first['incident']
+ incident_payload = ::PagerDuty::WebhookPayloadParser.call(webhook_payload)['incident']
expect(::IncidentManagement::PagerDuty::ProcessIncidentWorker)
.to receive(:perform_async)
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 4f27ff30da7..79c779678a4 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -25,7 +25,9 @@ RSpec.describe MergeRequests::BuildService do
safe_message: 'Initial commit',
gitaly_commit?: false,
id: 'f00ba6',
- parent_ids: ['f00ba5'])
+ parent_ids: ['f00ba5'],
+ author_email: 'tom@example.com',
+ author_name: 'Tom Example')
end
let(:commit_2) do
@@ -34,7 +36,9 @@ RSpec.describe MergeRequests::BuildService do
safe_message: "Closes #1234 Second commit\n\nCreate the app",
gitaly_commit?: false,
id: 'f00ba7',
- parent_ids: ['f00ba6'])
+ parent_ids: ['f00ba6'],
+ author_email: 'alice@example.com',
+ author_name: 'Alice Example')
end
let(:commit_3) do
@@ -43,7 +47,9 @@ RSpec.describe MergeRequests::BuildService do
safe_message: 'This is a bad commit message!',
gitaly_commit?: false,
id: 'f00ba8',
- parent_ids: ['f00ba7'])
+ parent_ids: ['f00ba7'],
+ author_email: 'jo@example.com',
+ author_name: 'Jo Example')
end
let(:commits) { nil }
@@ -742,4 +748,91 @@ RSpec.describe MergeRequests::BuildService do
end
end
end
+
+ describe '#replace_variables_in_description' do
+ context 'when the merge request description is blank' do
+ let(:description) { nil }
+
+ it 'does not update the description' do
+ expect(merge_request.description).to eq(nil)
+ end
+ end
+
+ context 'when the merge request description contains template variables' do
+ let(:description) { <<~MSG.rstrip }
+ source_branch:%{source_branch}
+ target_branch:%{target_branch}
+ title:%{title}
+ issues:%{issues}
+ description:%{description}
+ first_commit:%{first_commit}
+ first_multiline_commit:%{first_multiline_commit}
+ url:%{url}
+ approved_by:%{approved_by}
+ merged_by:%{merged_by}
+ co_authored_by:%{co_authored_by}
+ all_commits:%{all_commits}
+ MSG
+
+ context 'when there are multiple commits in the diff' do
+ let(:commits) { Commit.decorate([commit_1, commit_2, commit_3], project) }
+
+ before do
+ stub_compare
+ end
+
+ it 'replaces the variables in the description' do
+ expect(merge_request.description).to eq <<~MSG.rstrip
+ source_branch:feature-branch
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:Initial commit
+ first_multiline_commit:Closes #1234 Second commit
+
+ Create the app
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:Co-authored-by: Jo Example <jo@example.com>
+ Co-authored-by: Alice Example <alice@example.com>
+ Co-authored-by: Tom Example <tom@example.com>
+ all_commits:* This is a bad commit message!
+
+ * Closes #1234 Second commit
+
+ Create the app
+
+ * Initial commit
+ MSG
+ end
+ end
+
+ context 'when there are no commits in the diff' do
+ let(:commits) { [] }
+
+ before do
+ stub_compare
+ end
+
+ it 'replaces the variables in the description' do
+ expect(merge_request.description).to eq <<~MSG.rstrip
+ source_branch:feature-branch
+ target_branch:master
+ title:
+ issues:
+ description:
+ first_commit:
+ first_multiline_commit:
+ url:
+ approved_by:
+ merged_by:
+ co_authored_by:
+ all_commits:
+ MSG
+ end
+ end
+ end
+ end
end
diff --git a/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb b/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb
index 2b2903733a2..b81f1a575b5 100644
--- a/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb
+++ b/spec/workers/incident_management/pager_duty/process_incident_worker_spec.rb
@@ -22,9 +22,9 @@ RSpec.describe IncidentManagement::PagerDuty::ProcessIncidentWorker do
'assignees' => [{
'summary' => 'Laura Haley', 'url' => 'https://webdemo.pagerduty.com/users/P553OPV'
}],
- 'impacted_services' => [{
+ 'impacted_service' => {
'summary' => 'Production XDB Cluster', 'url' => 'https://webdemo.pagerduty.com/services/PN49J75'
- }]
+ }
}
end
diff --git a/yarn.lock b/yarn.lock
index 369a3776295..af6245ccb37 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5525,10 +5525,10 @@ eslint-plugin-no-jquery@2.7.0:
resolved "https://registry.yarnpkg.com/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.7.0.tgz#855f5631cf5b8e25b930cf6f06e02dd81f132e72"
integrity sha512-Aeg7dA6GTH1AcWLlBtWNzOU9efK5KpNi7b0EhBO0o0M+awyzguUUo8gF6hXGjQ9n5h8/uRtYv9zOqQkeC5CG0w==
-eslint-plugin-no-unsanitized@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.0.1.tgz#e2343265467ba2270ade478cbe07bbafeaea412d"
- integrity sha512-y/lAMWnPPC7RYuUdxlEL/XiCL8FehN9h9s3Kjqbp/Kv0i9NZs+IXSC2kS546Fa4Bumwy31HlVS/OdWX0Kxb5Xg==
+eslint-plugin-no-unsanitized@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.0.2.tgz#e872b302cdfb5fe1262db989ba29cfcc334b499b"
+ integrity sha512-Pry0S9YmHoz8NCEMRQh7N0Yexh2MYCNPIlrV52hTmS7qXnTghWsjXouF08bgsrrZqaW9tt1ZiK3j5NEmPE+EjQ==
eslint-plugin-promise@^6.0.1:
version "6.0.1"
@@ -5614,10 +5614,10 @@ eslint-visitor-keys@^3.3.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
-eslint@8.26.0:
- version "8.26.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.26.0.tgz#2bcc8836e6c424c4ac26a5674a70d44d84f2181d"
- integrity sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==
+eslint@8.28.0:
+ version "8.28.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.28.0.tgz#81a680732634677cc890134bcdd9fdfea8e63d6e"
+ integrity sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==
dependencies:
"@eslint/eslintrc" "^1.3.3"
"@humanwhocodes/config-array" "^0.11.6"