diff options
| author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-06 12:08:29 +0000 |
|---|---|---|
| committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-06 12:08:29 +0000 |
| commit | 39c98649d20e08428f507e0728b0bd87a299e5cf (patch) | |
| tree | 6ca0ae12d9498f9158b7f8da9bad97f1517b0d83 | |
| parent | c16b752f86f78442349b7b2cd9106109de10763b (diff) | |
| download | gitlab-ce-39c98649d20e08428f507e0728b0bd87a299e5cf.tar.gz | |
Add latest changes from gitlab-org/gitlab@master
42 files changed, 666 insertions, 125 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 21671ba56ae..534861d90b2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -143,25 +143,6 @@ Performance/ConstantRegexp: Performance/MethodObjectAsBlock: Enabled: false -# Offense count: 18 -# Cop supports --auto-correct. -# Configuration parameters: AutoCorrect. -Performance/StringInclude: - Exclude: - - 'app/models/snippet_repository.rb' - - 'config/initializers/macos.rb' - - 'config/spring.rb' - - 'ee/app/models/ee/container_registry/event.rb' - - 'ee/lib/gitlab/auth/smartcard/certificate.rb' - - 'lib/gitlab/database/migration_helpers.rb' - - 'lib/kramdown/parser/atlassian_document_format.rb' - - 'lib/prometheus/pid_provider.rb' - - 'qa/qa/ee/page/merge_request/show.rb' - - 'qa/qa/specs/runner.rb' - - 'spec/features/projects/jobs_spec.rb' - - 'spec/spec_helper.rb' - - 'spec/support_specs/helpers/active_record/query_recorder_spec.rb' - # Offense count: 15209 # Configuration parameters: Prefixes. # Prefixes: when, with, without @@ -282,11 +263,6 @@ Rails/HelperInstanceVariable: Rails/IndexWith: Enabled: false -# Offense count: 1 -Rails/Inquiry: - Exclude: - - 'spec/helpers/labels_helper_spec.rb' - # Offense count: 118 # Configuration parameters: Include. # Include: app/models/**/*.rb @@ -335,11 +311,6 @@ Rails/RakeEnvironment: Rails/RedundantForeignKey: Enabled: false -# Offense count: 1 -Rails/RenderInline: - Exclude: - - 'ee/app/controllers/sitemap_controller.rb' - # Offense count: 1144 # Configuration parameters: ForbiddenMethods, AllowedMethods. # ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all diff --git a/.rubocop_todo/performance/string_include.yml b/.rubocop_todo/performance/string_include.yml new file mode 100644 index 00000000000..cb6e566094e --- /dev/null +++ b/.rubocop_todo/performance/string_include.yml @@ -0,0 +1,15 @@ +--- +# Cop supports --auto-correct. +Performance/StringInclude: + Exclude: + - 'app/models/snippet_repository.rb' + - 'config/initializers/macos.rb' + - 'config/spring.rb' + - 'ee/app/models/ee/container_registry/event.rb' + - 'ee/lib/gitlab/auth/smartcard/certificate.rb' + - 'lib/gitlab/database/migration_helpers.rb' + - 'lib/kramdown/parser/atlassian_document_format.rb' + - 'lib/prometheus/pid_provider.rb' + - 'qa/qa/specs/runner.rb' + - 'spec/features/projects/jobs_spec.rb' + - 'spec/spec_helper.rb' diff --git a/.rubocop_todo/rails/inquiry.yml b/.rubocop_todo/rails/inquiry.yml new file mode 100644 index 00000000000..3c999dfbc47 --- /dev/null +++ b/.rubocop_todo/rails/inquiry.yml @@ -0,0 +1,4 @@ +--- +Rails/Inquiry: + Exclude: + - 'spec/helpers/labels_helper_spec.rb' diff --git a/.rubocop_todo/rails/render_inline.yml b/.rubocop_todo/rails/render_inline.yml new file mode 100644 index 00000000000..fbebec72cd7 --- /dev/null +++ b/.rubocop_todo/rails/render_inline.yml @@ -0,0 +1,4 @@ +--- +Rails/RenderInline: + Exclude: + - 'ee/app/controllers/sitemap_controller.rb' diff --git a/app/assets/images/learn_gitlab/graduation_hat.svg b/app/assets/images/learn_gitlab/graduation_hat.svg deleted file mode 100644 index 998d8d9b935..00000000000 --- a/app/assets/images/learn_gitlab/graduation_hat.svg +++ /dev/null @@ -1 +0,0 @@ -<svg width="16" height="17" xmlns="http://www.w3.org/2000/svg"><path fill="#fffff" d="M1.53 7.639l-.476.88.476-.88zm0-1.758L1.054 5l.476.88zm2.257 2.982h1v-.596l-.523-.283-.477.879zm8.424 0l-.476-.88-.524.284v.596h1zm2.257-1.224l.477.88-.477-.88zm0-1.758l-.476.879.476-.88zM8.476 2.632l-.477.88.477-.88zm-.953 0l.476.88-.476-.88zM2.007 6.76l-.953-1.758c-1.396.756-1.396 2.76 0 3.516l.953-1.758zm2.257 1.224L2.007 6.76l-.953 1.758L3.31 9.742l.953-1.758zm.523 1.995V8.863h-2v1.116h2zM8 12.5c-1.949 0-3.212-1.289-3.212-2.52h-2c0 2.656 2.51 4.52 5.212 4.52v-2zm3.212-2.52c0 1.231-1.262 2.52-3.212 2.52v2c2.704 0 5.212-1.864 5.212-4.52h-2zm0-1.117v1.116h2V8.863h-2zm2.78-2.103l-2.256 1.223.953 1.759 2.257-1.224-.953-1.758zm0 0l.954 1.758c1.396-.757 1.396-2.76 0-3.516l-.953 1.758zM8 3.51l5.993 3.249.953-1.758-5.993-3.249L8 3.511zm0 0l.953-1.758a2 2 0 00-1.906 0L8 3.511zM2.007 6.76l5.992-3.25-.953-1.758-5.992 3.249.953 1.758z"/><path fill="#fffff" d="M7.228 7.541c-.187-.112-.277-.427-.201-.704.076-.276.288-.41.475-.297L11 8.644v5.316c0 .298-.163.54-.365.54-.2 0-.364-.242-.364-.54V9.37L7.228 7.54z"/></svg>
\ No newline at end of file diff --git a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue index 662cf2a7e36..bde76c46b4b 100644 --- a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue +++ b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue @@ -3,6 +3,7 @@ import { GlDropdown, GlDropdownItem, GlDropdownDivider, + GlDropdownText, GlSearchBoxByType, GlSprintf, } from '@gitlab/ui'; @@ -15,6 +16,7 @@ export default { GlDropdown, GlDropdownItem, GlDropdownDivider, + GlDropdownText, GlSearchBoxByType, GlSprintf, }, @@ -73,13 +75,24 @@ export default { this.clearSearch(); this.focusSearch(); }, + onKeyEnter() { + if (!this.searchTerm?.length) { + return; + } + this.$refs.dropdown.hide(); + this.selectAgent(this.searchTerm); + }, }, }; </script> <template> - <gl-dropdown :text="dropdownText" :loading="isRegistering" @shown="handleShow"> + <gl-dropdown ref="dropdown" :text="dropdownText" :loading="isRegistering" @shown="handleShow"> <template #header> - <gl-search-box-by-type ref="searchInput" v-model.trim="searchTerm" /> + <gl-search-box-by-type + ref="searchInput" + v-model.trim="searchTerm" + @keydown.enter.stop.prevent="onKeyEnter" + /> </template> <gl-dropdown-item v-for="agent in filteredResults" @@ -90,9 +103,9 @@ export default { > {{ agent }} </gl-dropdown-item> - <gl-dropdown-item v-if="!filteredResults.length" ref="noMatchingResults">{{ + <gl-dropdown-text v-if="!filteredResults.length" ref="noMatchingResults">{{ $options.i18n.noResults - }}</gl-dropdown-item> + }}</gl-dropdown-text> <template v-if="shouldRenderCreateButton"> <gl-dropdown-divider /> <gl-dropdown-item data-testid="create-config-button" @click="selectAgent(searchTerm)"> diff --git a/app/assets/javascripts/jobs/components/trigger_block.vue b/app/assets/javascripts/jobs/components/trigger_block.vue index b1ddede8fe8..1afc1c9a595 100644 --- a/app/assets/javascripts/jobs/components/trigger_block.vue +++ b/app/assets/javascripts/jobs/components/trigger_block.vue @@ -1,5 +1,5 @@ <script> -import { GlButton, GlTable } from '@gitlab/ui'; +import { GlButton, GlTableLite } from '@gitlab/ui'; import { __ } from '~/locale'; const DEFAULT_TD_CLASSES = 'gl-w-half gl-font-sm! gl-border-gray-200!'; @@ -25,7 +25,7 @@ export default { ], components: { GlButton, - GlTable, + GlTableLite, }, props: { trigger: { @@ -84,7 +84,7 @@ export default { > </p> - <gl-table :items="trigger.variables" :fields="$options.fields" small bordered fixed> + <gl-table-lite :items="trigger.variables" :fields="$options.fields" small bordered fixed> <template #cell(key)="{ item }"> <span class="gl-overflow-break-word">{{ item.key }}</span> </template> @@ -92,7 +92,7 @@ export default { <template #cell(value)="data"> <span class="gl-overflow-break-word">{{ getDisplayValue(data.value) }}</span> </template> - </gl-table> + </gl-table-lite> </template> </div> </template> diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index a80643e695b..d3de284a2d6 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -1,3 +1,5 @@ +@import '../highlight/hljs'; + .file-content.code { border: 0; box-shadow: none; diff --git a/app/assets/stylesheets/highlight/hljs.scss b/app/assets/stylesheets/highlight/hljs.scss new file mode 100644 index 00000000000..2e31e7c1f6d --- /dev/null +++ b/app/assets/stylesheets/highlight/hljs.scss @@ -0,0 +1,125 @@ +.code.highlight { + .hljs-comment { + color: var(--color-hljs-comment); + } + + .hljs-link { + color: var(--color-hljs-link); + } + + .hljs-meta { + color: var(--color-hljs-meta); + } + + .hljs-keyword { + color: var(--color-hljs-keyword); + } + + .hljs-type { + color: var(--color-hljs-type); + } + + .hljs-attr, + .hljs-property { + color: var(--color-hljs-attr); + } + + .hljs-built_in { + color: var(--color-hljs-builtin); + } + + .hljs-literal { + color: var(--color-hljs-literal); + } + + .hljs-title { + color: var(--color-hljs-title); + + &.class_ { + color: var(--color-hljs-class); + } + + &.function_ { + color: var(--color-hljs-function); + } + } + + .hljs-tag , + .hljs-name { + color: var(--color-hljs-tag); + } + + .hljs-number { + color: var(--color-hljs-number); + } + + .hljs-subst { + color: var(--color-hljs-subst); + } + + .hljs-string, + .hljs-section, + .hljs-bullet { + color: var(--color-hljs-string); + } + + .hljs-symbol { + color: var(--color-hljs-symbol); + } + + .hljs-variable { + color: var(--color-hljs-variable); + + &.language_ { + color: var(--color-hljs-language); + } + + &.constant_ { + color: var(--color-hljs-constant); + } + } + + .hljs-attribute { + color: var(--color-hljs-attribute); + } + + .hljs-operator { + color: var(--color-hljs-operator); + } + + .hljs-punctuation { + color: var(--color-hljs-punctuation); + } + + .hljs-regexp { + color: var(--color-hljs-regexp); + } + + .hljs-params { + color: var(--color-hljs-params); + } + + .hljs-doctag { + color: var(--color-hljs-doctag); + } + + .hljs-selector-tag { + color: var(--color-hljs-selector-tag); + } + + .hljs-selector-class { + color: var(--color-hljs-selector-class); + } + + .hljs-selector-id { + color: var(--color-hljs-selector-id); + } + + .hljs-selector-attr { + color: var(--color-hljs-selector-attr); + } + + .hljs-selector-pseudo { + color: var(--color-hljs-selector-pseudo); + } +} diff --git a/app/assets/stylesheets/highlight/themes/dark.scss b/app/assets/stylesheets/highlight/themes/dark.scss index 28878280d24..fa2415bcb5e 100644 --- a/app/assets/stylesheets/highlight/themes/dark.scss +++ b/app/assets/stylesheets/highlight/themes/dark.scss @@ -88,6 +88,39 @@ $dark-vg: #c66; $dark-vi: #c66; $dark-il: #de935f; +:root { + --color-hljs-comment: #{$dark-c}; + --color-hljs-variable: #{$dark-k}; + --color-hljs-link: #{$dark-l}; + --color-hljs-meta: #{$dark-cp}; + --color-hljs-keyword: #{$dark-kd}; + --color-hljs-type: #{$dark-kt}; + --color-hljs-attr: #{$dark-na}; + --color-hljs-builtin: #{$dark-nb}; + --color-hljs-title: #{$dark-n}; + --color-hljs-class: #{$dark-nc}; + --color-hljs-function: #{$dark-nf}; + --color-hljs-tag: #{$dark-nt}; + --color-hljs-number: #{$dark-mi}; + --color-hljs-subst: #{$dark-sc}; + --color-hljs-string: #{$dark-s1}; + --color-hljs-symbol: #{$dark-ss}; + --color-hljs-variable: #{$dark-vi}; + --color-hljs-operator: #{$dark-o}; + --color-hljs-punctuation: #{$dark-p}; + --color-hljs-regexp: #{$dark-sr}; + --color-hljs-constant: #{$dark-nx}; + --color-hljs-literal: #{$dark-kc}; + --color-hljs-language: #{$dark-nx}; + --color-hljs-params: #{$dark-nx}; + --color-hljs-selector-doctag: #{$dark-cm}; + --color-hljs-selector-tag: #{$dark-nt}; + --color-hljs-selector-class: #{$dark-nc}; + --color-hljs-selector-id: #{$dark-nn}; + --color-hljs-selector-attr: #{$dark-nt}; + --color-hljs-selector-pseudo: #{$dark-nd}; +} + .code.dark { // Line numbers .file-line-num { diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 729170da242..f8ac180d3d7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -200,6 +200,18 @@ module Issuable incident? end + # When :incident_escalations feature flag is disabled, new + # incidents should not have a status record unless the incident + # is associated with the alert. However, escalation attributes + # are synced with Alert/IssuableEscalationStatus, so we want to + # ensure that parity is kept prior to rollout. + # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/345769. + def sync_escalation_attributes_from_alert? + incident? && + ::Feature.disabled?(:incident_escalations, project) && + alert_management_alert.present? + end + def incident? is_a?(Issue) && super end diff --git a/app/policies/project_member_policy.rb b/app/policies/project_member_policy.rb index 91f1eb35506..40ba30fce5e 100644 --- a/app/policies/project_member_policy.rb +++ b/app/policies/project_member_policy.rb @@ -3,13 +3,16 @@ class ProjectMemberPolicy < BasePolicy delegate { @subject.project } - condition(:target_is_owner, scope: :subject) { @subject.user == @subject.project.owner } + condition(:target_is_holder_of_the_personal_namespace, scope: :subject) do + @subject.project.personal_namespace_holder?(@subject.user) + end + condition(:target_is_self) { @user && @subject.user == @user } condition(:project_bot) { @subject.user&.project_bot? } rule { anonymous }.prevent_all - rule { target_is_owner }.policy do + rule { target_is_holder_of_the_personal_namespace }.policy do prevent :update_project_member prevent :destroy_project_member end diff --git a/app/services/alert_management/alerts/update_service.rb b/app/services/alert_management/alerts/update_service.rb index 0769adc862e..7ca0fb4c6fb 100644 --- a/app/services/alert_management/alerts/update_service.rb +++ b/app/services/alert_management/alerts/update_service.rb @@ -151,14 +151,25 @@ module AlertManagement status_change_reason: " by changing the status of #{alert.to_reference(project)}" } } - ).execute(alert.issue) + ).execute(issue) + end + + def issue + strong_memoize(:issue) { alert.issue } end def should_sync_to_incident? - alert.issue && - alert.issue.supports_escalation? && - alert.issue.escalation_status && - alert.issue.escalation_status.status != alert.status + return false unless sync_available? + + issue.escalation_status&.status != alert.status + end + + def sync_available? + return false unless issue.present? + + # Remove sync check with https://gitlab.com/gitlab-org/gitlab/-/issues/345769 + issue.supports_escalation? || + issue.sync_escalation_attributes_from_alert? end def filter_duplicate diff --git a/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb b/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb index 1d0504a6e80..d755cf61207 100644 --- a/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb +++ b/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb @@ -31,7 +31,10 @@ module IncidentManagement attr_reader :issuable, :param_errors def available? - issuable.supports_escalation? && user_has_permissions? + ( + issuable.supports_escalation? || + issuable.sync_escalation_attributes_from_alert? # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/345769 + ) && user_has_permissions? end def user_has_permissions? @@ -60,6 +63,14 @@ module IncidentManagement status = params.delete(:status) return unless status + # If we're updating the escalation status because the + # alert was updated & the feature flag is disabled, then + # we should not allow the status to be different from the alert's. + # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/345769 + if issuable.sync_escalation_attributes_from_alert? && status != issuable.alert_management_alert.status_name + add_param_error(:status) && return + end + status_event = escalation_status.status_event_for(status) add_param_error(:status) && return unless status_event diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index 7ab663718db..e7d7b3a70eb 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -87,7 +87,10 @@ module Issues attr_reader :spam_params def create_escalation_status(issue) - ::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute if issue.supports_escalation? + # Remove sync check with https://gitlab.com/gitlab-org/gitlab/-/issues/345769 + return unless issue.supports_escalation? || issue.sync_escalation_attributes_from_alert? + + ::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute end def user_agent_detail_service diff --git a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml index 03a16b98a9b..217c20e574f 100644 --- a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml +++ b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml @@ -167,7 +167,7 @@ options: - p_ci_templates_implicit_security_cluster_image_scanning - p_ci_templates_kaniko - p_ci_templates_qualys_iac_security - - p_ci_templates_database_liquibase + - p_ci_templates_liquibase distribution: - ce - ee diff --git a/config/metrics/counts_28d/20220307150912_p_ci_templates_database_liquibase_monthly.yml b/config/metrics/counts_28d/20220307150912_p_ci_templates_database_liquibase_monthly.yml index 07a5548474a..214c6504051 100644 --- a/config/metrics/counts_28d/20220307150912_p_ci_templates_database_liquibase_monthly.yml +++ b/config/metrics/counts_28d/20220307150912_p_ci_templates_database_liquibase_monthly.yml @@ -1,5 +1,5 @@ --- -key_path: redis_hll_counters.ci_templates.p_ci_templates_database_liquibase_monthly +key_path: redis_hll_counters.ci_templates.p_ci_templates_liquibase_monthly description: "" product_section: "" product_stage: "" @@ -22,4 +22,4 @@ tier: - ultimate options: events: - - p_ci_templates_database_liquibase + - p_ci_templates_liquibase diff --git a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml index 062ed14d649..9368500ab13 100644 --- a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml +++ b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml @@ -167,7 +167,7 @@ options: - p_ci_templates_implicit_security_cluster_image_scanning - p_ci_templates_kaniko - p_ci_templates_qualys_iac_security - - p_ci_templates_database_liquibase + - p_ci_templates_liquibase distribution: - ce - ee diff --git a/config/metrics/counts_7d/20220307150854_p_ci_templates_database_liquibase_weekly.yml b/config/metrics/counts_7d/20220307150854_p_ci_templates_database_liquibase_weekly.yml index de9757b9f17..c47c1fb51d5 100644 --- a/config/metrics/counts_7d/20220307150854_p_ci_templates_database_liquibase_weekly.yml +++ b/config/metrics/counts_7d/20220307150854_p_ci_templates_database_liquibase_weekly.yml @@ -1,5 +1,5 @@ --- -key_path: redis_hll_counters.ci_templates.p_ci_templates_database_liquibase_weekly +key_path: redis_hll_counters.ci_templates.p_ci_templates_liquibase_weekly description: "" product_section: "" product_stage: "" @@ -22,4 +22,4 @@ tier: - ultimate options: events: - - p_ci_templates_database_liquibase + - p_ci_templates_liquibase diff --git a/db/post_migrate/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size.rb b/db/post_migrate/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size.rb new file mode 100644 index 00000000000..49c9efc497c --- /dev/null +++ b/db/post_migrate/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class BackfillNamespaceStatisticsWithDependencyProxySize < Gitlab::Database::Migration[1.0] + DELAY_INTERVAL = 2.minutes.to_i + BATCH_SIZE = 500 + MIGRATION = 'PopulateNamespaceStatistics' + + disable_ddl_transaction! + + def up + groups = exec_query <<~SQL + SELECT dependency_proxy_manifests.group_id FROM dependency_proxy_manifests + UNION + SELECT dependency_proxy_blobs.group_id from dependency_proxy_blobs + SQL + + groups.rows.flatten.in_groups_of(BATCH_SIZE, false).each_with_index do |group_ids, index| + migrate_in(index * DELAY_INTERVAL, MIGRATION, [group_ids, [:dependency_proxy_size]]) + end + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20220204095121 b/db/schema_migrations/20220204095121 new file mode 100644 index 00000000000..bf12ca08f5a --- /dev/null +++ b/db/schema_migrations/20220204095121 @@ -0,0 +1 @@ +bce595c1c6587e785bc49d6e5a7181b5cc0164f2201375ad82d4bd19c217cd35
\ No newline at end of file diff --git a/doc/ci/unit_test_reports.md b/doc/ci/unit_test_reports.md index 14350ac884f..029bd20c553 100644 --- a/doc/ci/unit_test_reports.md +++ b/doc/ci/unit_test_reports.md @@ -77,7 +77,7 @@ If a test failed in the project's default branch in the last 14 days, a message To enable the Unit test reports in merge requests, you must add [`artifacts:reports:junit`](yaml/artifacts_reports.md#artifactsreportsjunit) -in `.gitlab-ci.yml`, and specify the path(s) of the generated test reports. +in `.gitlab-ci.yml`, and specify the paths of the generated test reports. The reports must be `.xml` files, otherwise [GitLab returns an Error 500](https://gitlab.com/gitlab-org/gitlab/-/issues/216575). In the following examples, the job in the `test` stage runs and GitLab diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md index bb8f6a4b4a9..9facb22669b 100644 --- a/doc/development/documentation/testing.md +++ b/doc/development/documentation/testing.md @@ -365,6 +365,35 @@ file for the [`gitlab`](https://gitlab.com/gitlab-org/gitlab) project. To set up `lefthook` for documentation linting, see [Pre-push static analysis](../contributing/style_guides.md#pre-push-static-analysis-with-lefthook). +#### Show Vale warnings on push + +By default, `lefthook` shows only Vale errors when pushing changes to a branch. The default branches +have no Vale errors, so any errors listed here are introduced by commits to the branch. + +To also see the Vale warnings when pushing to a branch, set a local environment variable: `VALE_WARNINGS=true`. + +Enable Vale warnings on push to improve the documentation suite by: + +- Detecting warnings you might be introducing with your commits. +- Identifying warnings that already exist in the page, which you can resolve to reduce technical debt. + +These warnings: + +- Don't stop the push from working. +- Don't result in a broken pipeline. +- Include all warnings for a file, not just warnings that are introduced by the commits. + +To enable Vale warnings on push: + +- Automatically, add `VALE_WARNINGS=true` to your shell configuration. +- Manually, prepend `VALE_WARNINGS=true` to invocations of `lefthook`. For example: + + ```shell + VALE_WARNINGS=true bundle exec lefthook run pre-push + ``` + +You can also [configure your editor](#configure-editors) to show Vale warnings. + ### Show subset of Vale alerts You can set Visual Studio Code to display only a subset of Vale alerts when viewing files: diff --git a/doc/index.md b/doc/index.md index 57eb9e195ff..2f3bfefe819 100644 --- a/doc/index.md +++ b/doc/index.md @@ -32,7 +32,7 @@ No matter how you use GitLab, we have documentation for you. | Essential documentation | Essential documentation | |:------------------------|:------------------------| | [**User documentation**](user/index.md)<br>Discover features and concepts for GitLab users. | [**Administrator documentation**](administration/index.md)<br/>Everything GitLab self-managed administrators need to know. | -| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](#new-to-git-and-gitlab)<br/>We have the resources to get you started. | +| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](tutorials/index.md)<br/>We have the resources to get you started. | | [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Consult our integration documentation. | [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Consult our guides. | | [**Install GitLab**](https://about.gitlab.com/install/)<br/>Installation options for different platforms. | [**Customers**](subscriptions/index.md)<br/>Information for new and existing customers. | | [**Update GitLab**](update/index.md)<br/>Update your GitLab self-managed instance to the latest version. | [**Reference Architectures**](administration/reference_architectures/index.md)<br/>GitLab reference architectures. | @@ -64,20 +64,6 @@ GitLab makes the software lifecycle faster and radically improves the speed of b GitLab provides solutions for [each of the stages of the DevOps lifecycle](https://about.gitlab.com/stages-devops-lifecycle/). -## New to Git and GitLab? - -Working with new systems can be daunting. - -We have the following documentation to rapidly uplift your GitLab knowledge: - -| Topic | Description | -|:--------------------------------------------------------------------------------------------------|:------------| -| [GitLab basics guides](gitlab-basics/index.md) | Start working on the command line and with GitLab. | -| [What is GitLab Flow?](https://about.gitlab.com/topics/version-control/what-is-gitlab-flow/) | Enhance your workflow with the best of GitLab Flow. | -| [Get started with GitLab CI/CD](ci/quick_start/index.md) | Quickly implement GitLab CI/CD. | -| [Auto DevOps](topics/autodevops/index.md) | Learn more about Auto DevOps in GitLab. | -| [GitLab Markdown](user/markdown.md) | Advanced formatting system (GitLab Flavored Markdown). | - ### User account Learn more about GitLab account management: @@ -89,16 +75,6 @@ Learn more about GitLab account management: | [User settings](user/profile/index.md#access-your-user-settings) | Manage your user settings, two factor authentication, and more. | | [User permissions](user/permissions.md) | Learn what each role in a project can do. | -### Git and GitLab - -Learn more about using Git, and using Git with GitLab: - -| Topic | Description | -|:-----------------------------------------------------------------------------|:------------| -| [Git](topics/git/index.md) | Getting started with Git, branching strategies, Git LFS, and advanced use. | -| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF describing the most used Git operations. | -| [GitLab Flow](topics/gitlab_flow.md) | Explore the best of Git with the GitLab Flow strategy. | - ## Coming to GitLab from another platform If you are coming to GitLab from another platform, the following information is useful: diff --git a/lefthook.yml b/lefthook.yml index 086e70fcbb9..e79df94c708 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -42,7 +42,7 @@ pre-push: tags: documentation style files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD glob: 'doc/*.md' - run: 'if command -v vale > /dev/null 2>&1; then if ! vale --config .vale.ini --minAlertLevel error {files}; then echo "ERROR: Fix any linting errors and make sure you are using the latest version of Vale."; exit 1; fi; else echo "ERROR: Vale not found. For more information, see https://docs.errata.ai/vale/install."; exit 1; fi' + run: 'if [ $VALE_WARNINGS ]; then minWarnings=warning; else minWarnings=error; fi; if command -v vale > /dev/null 2>&1; then if ! vale --config .vale.ini --minAlertLevel $minWarnings {files}; then echo "ERROR: Fix any linting errors and make sure you are using the latest version of Vale."; exit 1; fi; else echo "ERROR: Vale not found. For more information, see https://docs.errata.ai/vale/install."; exit 1; fi' gettext: skip: true # This is disabled by default. You can enable this check by adding skip: false in lefhook-local.yml https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md#skipping-commands tags: backend frontend view haml diff --git a/lib/gitlab/background_migration/populate_namespace_statistics.rb b/lib/gitlab/background_migration/populate_namespace_statistics.rb index e873ad412f2..97927ef48c2 100644 --- a/lib/gitlab/background_migration/populate_namespace_statistics.rb +++ b/lib/gitlab/background_migration/populate_namespace_statistics.rb @@ -5,9 +5,40 @@ module Gitlab # This class creates/updates those namespace statistics # that haven't been created nor initialized. # It also updates the related namespace statistics - # This is only required in EE class PopulateNamespaceStatistics def perform(group_ids, statistics) + # Updating group statistics might involve calling Gitaly. + # For example, when calculating `wiki_size`, we will need + # to perform the request to check if the repo exists and + # also the repository size. + # + # The `allow_n_plus_1_calls` method is only intended for + # dev and test. It won't be raised in prod. + ::Gitlab::GitalyClient.allow_n_plus_1_calls do + relation(group_ids).each do |group| + upsert_namespace_statistics(group, statistics) + end + end + end + + private + + def upsert_namespace_statistics(group, statistics) + response = ::Groups::UpdateStatisticsService.new(group, statistics: statistics).execute + + error_message("#{response.message} group: #{group.id}") if response.error? + end + + def logger + @logger ||= ::Gitlab::BackgroundMigration::Logger.build + end + + def error_message(message) + logger.error(message: "Namespace Statistics Migration: #{message}") + end + + def relation(group_ids) + Group.includes(:namespace_statistics).where(id: group_ids) end end end diff --git a/lib/gitlab/ci/templates/Go.gitlab-ci.yml b/lib/gitlab/ci/templates/Go.gitlab-ci.yml index 8514e50b256..bd8e1020c4e 100644 --- a/lib/gitlab/ci/templates/Go.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Go.gitlab-ci.yml @@ -5,21 +5,6 @@ image: golang:latest -variables: - # Please edit to your GitLab project - REPO_NAME: gitlab.com/namespace/project - -# The problem is that to be able to use go get, one needs to put -# the repository in the $GOPATH. So for example if your gitlab domain -# is gitlab.com, and that your repository is namespace/project, and -# the default GOPATH being /go, then you'd need to have your -# repository in /go/src/gitlab.com/namespace/project -# Thus, making a symbolic link corrects this. -before_script: - - mkdir -p "$GOPATH/src/$(dirname $REPO_NAME)" - - ln -svf "$CI_PROJECT_DIR" "$GOPATH/src/$REPO_NAME" - - cd "$GOPATH/src/$REPO_NAME" - stages: - test - build diff --git a/lib/gitlab/ci/templates/Database/liquibase.gitlab-ci.yml b/lib/gitlab/ci/templates/liquibase.gitlab-ci.yml index 49bafca9952..18d59035b78 100644 --- a/lib/gitlab/ci/templates/Database/liquibase.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/liquibase.gitlab-ci.yml @@ -5,13 +5,13 @@ # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Database/liquibase.gitlab-ci.yml +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/liquibase.gitlab-ci.yml # This template must be configured with CI/CD variables before it will work. # See https://www.liquibase.com/blog/secure-database-developer-flow-using-gitlab-pipelines # to learn how to configure the Liquibase template by using variables. # Be sure to add the variables before running pipelines with this template. -# You may not want to run all the jobs in this template. You can comment out or delete the jobs you don't wish to use. +# You may not want to run all the jobs in this template. You can comment out or delete the jobs you don't wish to use. # List of stages for jobs and their order of execution. stages: @@ -132,7 +132,7 @@ TEST->PROD: expire_in: 1 week -# This job creates a snapshot of prod database. You can use the snapshot file to run comparisons with the production database to investigate for any potential issues. https://www.liquibase.com/devsecops +# This job creates a snapshot of prod database. You can use the snapshot file to run comparisons with the production database to investigate for any potential issues. https://www.liquibase.com/devsecops snapshot PROD: image: liquibase/liquibase:latest # Using the Liquibase Docker Image stage: .post diff --git a/lib/gitlab/data_builder/deployment.rb b/lib/gitlab/data_builder/deployment.rb index a4508bc93c5..92c99d99210 100644 --- a/lib/gitlab/data_builder/deployment.rb +++ b/lib/gitlab/data_builder/deployment.rb @@ -12,6 +12,11 @@ module Gitlab Gitlab::UrlBuilder.build(deployment.deployable) end + commit_url = + if (commit = deployment.commit) + Gitlab::UrlBuilder.build(commit) + end + { object_kind: 'deployment', status: deployment.status, @@ -24,8 +29,8 @@ module Gitlab short_sha: deployment.short_sha, user: deployment.deployed_by.hook_attrs, user_url: Gitlab::UrlBuilder.build(deployment.deployed_by), - commit_url: Gitlab::UrlBuilder.build(deployment.commit), - commit_title: deployment.commit.title, + commit_url: commit_url, + commit_title: deployment.commit&.title, ref: deployment.ref } end diff --git a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml index a5ec72fe8c9..917a7b5caec 100644 --- a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml +++ b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml @@ -615,7 +615,7 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_database_liquibase +- name: p_ci_templates_liquibase category: ci_templates redis_slot: ci_templates aggregation: weekly diff --git a/lib/sidebars/projects/menus/learn_gitlab_menu.rb b/lib/sidebars/projects/menus/learn_gitlab_menu.rb index 16335f5b076..5de70ea7d7f 100644 --- a/lib/sidebars/projects/menus/learn_gitlab_menu.rb +++ b/lib/sidebars/projects/menus/learn_gitlab_menu.rb @@ -45,9 +45,9 @@ module Sidebars } end - override :image_path - def image_path - 'learn_gitlab/graduation_hat.svg' + override :sprite_icon + def sprite_icon + 'bulb' end override :render? diff --git a/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js b/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js index eca2b1f5cb1..197735d3c77 100644 --- a/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js +++ b/spec/frontend/clusters_list/components/available_agents_dropwdown_spec.js @@ -1,5 +1,6 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { ENTER_KEY } from '~/lib/utils/keys'; import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue'; import { I18N_AVAILABLE_AGENTS_DROPDOWN } from '~/clusters_list/constants'; @@ -18,6 +19,7 @@ describe('AvailableAgentsDropdown', () => { propsData, stubs: { GlDropdown }, }); + wrapper.vm.$refs.dropdown.hide = jest.fn(); }; afterEach(() => { @@ -96,6 +98,25 @@ describe('AvailableAgentsDropdown', () => { expect(findDropdown().props('text')).toBe('new-agent'); }); }); + + describe('click enter to register new agent without configuration', () => { + beforeEach(async () => { + await findSearchInput().vm.$emit('input', 'new-agent'); + await findSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY })); + }); + + it('emits agentSelected with the name of the clicked agent', () => { + expect(wrapper.emitted('agentSelected')).toEqual([['new-agent']]); + }); + + it('marks the clicked item as selected', () => { + expect(findDropdown().props('text')).toBe('new-agent'); + }); + + it('closes the dropdown', () => { + expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalledTimes(1); + }); + }); }); describe('registration in progress', () => { diff --git a/spec/frontend/jobs/components/trigger_block_spec.js b/spec/frontend/jobs/components/trigger_block_spec.js index e0eb873dc2f..78596612d23 100644 --- a/spec/frontend/jobs/components/trigger_block_spec.js +++ b/spec/frontend/jobs/components/trigger_block_spec.js @@ -1,12 +1,12 @@ -import { GlButton, GlTable } from '@gitlab/ui'; +import { GlButton, GlTableLite } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import TriggerBlock from '~/jobs/components/trigger_block.vue'; describe('Trigger block', () => { let wrapper; - const findRevealButton = () => wrapper.find(GlButton); - const findVariableTable = () => wrapper.find(GlTable); + const findRevealButton = () => wrapper.findComponent(GlButton); + const findVariableTable = () => wrapper.findComponent(GlTableLite); const findShortToken = () => wrapper.find('[data-testid="trigger-short-token"]'); const findVariableValue = (index) => wrapper.findAll('[data-testid="trigger-build-value"]').at(index); @@ -22,7 +22,6 @@ describe('Trigger block', () => { afterEach(() => { wrapper.destroy(); - wrapper = null; }); describe('with short token and no variables', () => { diff --git a/spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb b/spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb new file mode 100644 index 00000000000..98b2bc437f3 --- /dev/null +++ b/spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::PopulateNamespaceStatistics do + let_it_be(:namespaces) { table(:namespaces) } + let_it_be(:namespace_statistics) { table(:namespace_statistics) } + let_it_be(:dependency_proxy_manifests) { table(:dependency_proxy_manifests) } + let_it_be(:dependency_proxy_blobs) { table(:dependency_proxy_blobs) } + + let!(:group1) { namespaces.create!(id: 10, type: 'Group', name: 'group1', path: 'group1') } + let!(:group2) { namespaces.create!(id: 20, type: 'Group', name: 'group2', path: 'group2') } + + let!(:group1_manifest) do + dependency_proxy_manifests.create!(group_id: 10, size: 20, file_name: 'test-file', file: 'test', digest: 'abc123') + end + + let!(:group2_manifest) do + dependency_proxy_manifests.create!(group_id: 20, size: 20, file_name: 'test-file', file: 'test', digest: 'abc123') + end + + let!(:group1_stats) { namespace_statistics.create!(id: 10, namespace_id: 10) } + + let(:ids) { namespaces.pluck(:id) } + let(:statistics) { [] } + + subject(:perform) { described_class.new.perform(ids, statistics) } + + it 'creates/updates all namespace_statistics and updates root storage statistics', :aggregate_failures do + expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async).with(group1.id) + expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async).with(group2.id) + + expect { perform }.to change(namespace_statistics, :count).from(1).to(2) + + namespace_statistics.all.each do |stat| + expect(stat.dependency_proxy_size).to eq 20 + expect(stat.storage_size).to eq 20 + end + end + + context 'when just a stat is passed' do + let(:statistics) { [:dependency_proxy_size] } + + it 'calls the statistics update service with just that stat' do + expect(Groups::UpdateStatisticsService) + .to receive(:new) + .with(anything, statistics: [:dependency_proxy_size]) + .twice.and_call_original + + perform + end + end + + context 'when a statistics update fails' do + before do + error_response = instance_double(ServiceResponse, message: 'an error', error?: true) + + allow_next_instance_of(Groups::UpdateStatisticsService) do |instance| + allow(instance).to receive(:execute).and_return(error_response) + end + end + + it 'logs an error' do + expect_next_instance_of(Gitlab::BackgroundMigration::Logger) do |instance| + expect(instance).to receive(:error).twice + end + + perform + end + end +end diff --git a/spec/lib/gitlab/data_builder/deployment_spec.rb b/spec/lib/gitlab/data_builder/deployment_spec.rb index ab8c8a51694..e64e6ace6b4 100644 --- a/spec/lib/gitlab/data_builder/deployment_spec.rb +++ b/spec/lib/gitlab/data_builder/deployment_spec.rb @@ -46,5 +46,24 @@ RSpec.describe Gitlab::DataBuilder::Deployment do expect(data[:deployable_url]).to be_nil end + + context 'when commit does not exist in the repository' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:deployment) { create(:deployment, project: project) } + + subject(:data) { described_class.build(deployment, Time.current) } + + before(:all) do + project.repository.remove + end + + it 'does not include commit_url' do + expect(data[:commit_url]).to be_nil + end + + it 'does not include commit_title' do + expect(data[:commit_title]).to be_nil + end + end end end diff --git a/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb b/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb new file mode 100644 index 00000000000..39398fa058d --- /dev/null +++ b/spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe BackfillNamespaceStatisticsWithDependencyProxySize do + let_it_be(:groups) { table(:namespaces) } + let_it_be(:group1) { groups.create!(id: 10, name: 'test1', path: 'test1', type: 'Group') } + let_it_be(:group2) { groups.create!(id: 20, name: 'test2', path: 'test2', type: 'Group') } + let_it_be(:group3) { groups.create!(id: 30, name: 'test3', path: 'test3', type: 'Group') } + let_it_be(:group4) { groups.create!(id: 40, name: 'test4', path: 'test4', type: 'Group') } + + let_it_be(:dependency_proxy_blobs) { table(:dependency_proxy_blobs) } + let_it_be(:dependency_proxy_manifests) { table(:dependency_proxy_manifests) } + + let_it_be(:group1_manifest) { create_manifest(10, 10) } + let_it_be(:group2_manifest) { create_manifest(20, 20) } + let_it_be(:group3_manifest) { create_manifest(30, 30) } + + let_it_be(:group1_blob) { create_blob(10, 10) } + let_it_be(:group2_blob) { create_blob(20, 20) } + let_it_be(:group3_blob) { create_blob(30, 30) } + + describe '#up' do + it 'correctly schedules background migrations' do + stub_const("#{described_class}::BATCH_SIZE", 2) + + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + aggregate_failures do + expect(described_class::MIGRATION) + .to be_scheduled_migration([10, 30], ['dependency_proxy_size']) + + expect(described_class::MIGRATION) + .to be_scheduled_delayed_migration(2.minutes, [20], ['dependency_proxy_size']) + + expect(BackgroundMigrationWorker.jobs.size).to eq(2) + end + end + end + end + end + + def create_manifest(group_id, size) + dependency_proxy_manifests.create!( + group_id: group_id, + size: size, + file_name: 'test-file', + file: 'test', + digest: 'abc123' + ) + end + + def create_blob(group_id, size) + dependency_proxy_blobs.create!( + group_id: group_id, + size: size, + file_name: 'test-file', + file: 'test' + ) + end +end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index b38135fc0b2..12da5de5ef1 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -993,6 +993,33 @@ RSpec.describe Issuable do end end + describe '#sync_escalation_attributes_from_alert?' do + where(:issuable_type, :args, :sync_escalation_attributes_from_alert) do + :issue | {} | false + :issue | ref(:alert_args) | false + :incident | {} | false + :incident | ref(:alert_args) | true + :merge_request | {} | false + end + + with_them do + let(:alert_args) { { alert_management_alert: build_stubbed(:alert_management_alert) } } + let(:issuable) { build_stubbed(issuable_type, **args) } + + subject { issuable.sync_escalation_attributes_from_alert? } + + it { is_expected.to eq(false) } + + context 'with feature disabled' do + before do + stub_feature_flags(incident_escalations: false) + end + + it { is_expected.to eq(sync_escalation_attributes_from_alert) } + end + end + end + describe '#incident?' do where(:issuable_type, :incident) do :issue | false diff --git a/spec/policies/project_member_policy_spec.rb b/spec/policies/project_member_policy_spec.rb index 12b3e60fdb2..b19ab71fcb5 100644 --- a/spec/policies/project_member_policy_spec.rb +++ b/spec/policies/project_member_policy_spec.rb @@ -23,9 +23,9 @@ RSpec.describe ProjectMemberPolicy do it { is_expected.not_to be_allowed(:destroy_project_bot_member) } end - context 'when user is project owner' do - let(:member_user) { project.first_owner } - let(:member) { project.members.find_by!(user: member_user) } + context 'when user is the holder of personal namespace in which the project resides' do + let(:namespace_holder) { project.namespace.owner } + let(:member) { project.members.find_by!(user: namespace_holder) } it { is_expected.to be_allowed(:read_project) } it { is_expected.to be_disallowed(:update_project_member) } diff --git a/spec/services/alert_management/alerts/update_service_spec.rb b/spec/services/alert_management/alerts/update_service_spec.rb index 882543fd701..0388fe9e25c 100644 --- a/spec/services/alert_management/alerts/update_service_spec.rb +++ b/spec/services/alert_management/alerts/update_service_spec.rb @@ -253,25 +253,51 @@ RSpec.describe AlertManagement::Alerts::UpdateService do end end + shared_examples 'updates the incident escalation status with the new alert status' do + specify do + expect(::Issues::UpdateService).to receive(:new).once.and_call_original + expect(described_class).to receive(:new).once.and_call_original + + expect { response }.to change { escalation_status&.reload&.acknowledged? }.to(true) + .and change { alert.reload.acknowledged? }.to(true) + end + end + it_behaves_like 'does not sync with the incident status' + context 'when feature flag is disabled' do + before do + stub_feature_flags(incident_escalations: false) + end + + it_behaves_like 'does not sync with the incident status' + end + context 'when the issue is an incident' do before do issue.update!(issue_type: Issue.issue_types[:incident]) end - it_behaves_like 'does not sync with the incident status' + context 'when the incident does not have an escalation status' do + it_behaves_like 'updates the incident escalation status with the new alert status' - context 'when the incident has an escalation status' do - let_it_be(:escalation_status, reload: true) { create(:incident_management_issuable_escalation_status, issue: issue) } + context 'when feature flag is disabled' do + before do + stub_feature_flags(incident_escalations: false) + end - it 'updates the incident escalation status with the new alert status' do - expect(::Issues::UpdateService).to receive(:new).once.and_call_original - expect(described_class).to receive(:new).once.and_call_original + it_behaves_like 'updates the incident escalation status with the new alert status' + end - expect { response }.to change { escalation_status.reload.acknowledged? }.to(true) - .and change { alert.reload.acknowledged? }.to(true) + def escalation_status + issue.reload.escalation_status end + end + + context 'when the incident has an escalation status' do + let_it_be(:escalation_status, reload: true) { create(:incident_management_issuable_escalation_status, issue: issue) } + + it_behaves_like 'updates the incident escalation status with the new alert status' context 'when the statuses match' do before do @@ -286,7 +312,7 @@ RSpec.describe AlertManagement::Alerts::UpdateService do stub_feature_flags(incident_escalations: false) end - it_behaves_like 'does not sync with the incident status' + it_behaves_like 'updates the incident escalation status with the new alert status' end end end diff --git a/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb b/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb index 25164df40ca..0ba26bf2c13 100644 --- a/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb +++ b/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb @@ -48,6 +48,29 @@ RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateServ end it_behaves_like 'availability error response' + + context 'with incident associated with alert' do + let(:alert_status) { :acknowledged } + + before do + create(:alert_management_alert, alert_status, project: issue.project, issue: issue) + issue.reload + end + + it_behaves_like 'successful response', { status_event: :acknowledge } + + context 'when alert status does not match incident status' do + let(:alert_status) { :triggered } + + include_examples 'error response', 'Invalid value was provided for parameters: status' + end + + context 'with a standard issue' do + let(:issue) { create(:issue) } + + it_behaves_like 'availability error response' + end + end end context 'when user is anonymous' do diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 6b7b72d83fc..4b11b3cf40b 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -98,6 +98,7 @@ RSpec.describe Issues::CreateService do end it_behaves_like 'not an incident issue' + include_examples 'does not call the escalation status CreateService' context 'when issue is incident type' do before do @@ -118,12 +119,22 @@ RSpec.describe Issues::CreateService do end it_behaves_like 'incident issue' + include_examples 'calls the escalation status CreateService' - it 'calls IncidentManagement::Incidents::CreateEscalationStatusService' do - expect_next(::IncidentManagement::IssuableEscalationStatuses::CreateService, a_kind_of(Issue)) - .to receive(:execute) + context 'when :incident_escalations feature flag is disabled' do + before do + stub_feature_flags(incident_escalations: false) + end + + include_examples 'does not call the escalation status CreateService' - issue + context 'with associated alert' do + before do + opts.merge!(alert_management_alert: build(:alert_management_alert, project: project)) + end + + include_examples 'calls the escalation status CreateService' + end end context 'when invalid' do diff --git a/spec/support/shared_examples/services/incident_management/escalation_status_shared_examples.rb b/spec/support/shared_examples/services/incident_management/escalation_status_shared_examples.rb new file mode 100644 index 00000000000..d9fe217921c --- /dev/null +++ b/spec/support/shared_examples/services/incident_management/escalation_status_shared_examples.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# This shared_example requires the following variables: +# - issue (required) +RSpec.shared_examples 'calls the escalation status CreateService' do + it 'calls IncidentManagement::Incidents::CreateEscalationStatusService' do + expect_next(::IncidentManagement::IssuableEscalationStatuses::CreateService, a_kind_of(Issue)) + .to receive(:execute) + + issue + end +end + +# This shared_example requires the following variables: +# - issue (required) +RSpec.shared_examples 'does not call the escalation status CreateService' do + it 'does not call the ::IncidentManagement::IssuableEscalationStatuses::CreateService' do + expect(::IncidentManagement::IssuableEscalationStatuses::CreateService).not_to receive(:new) + + issue + end +end |
