diff options
71 files changed, 1487 insertions, 1255 deletions
diff --git a/.gitlab/ci/rails/shared.gitlab-ci.yml b/.gitlab/ci/rails/shared.gitlab-ci.yml index ba4bde9d07b..e282781b7b5 100644 --- a/.gitlab/ci/rails/shared.gitlab-ci.yml +++ b/.gitlab/ci/rails/shared.gitlab-ci.yml @@ -54,10 +54,14 @@ include: RECORD_DEPRECATIONS: "true" GEO_SECONDARY_PROXY: 0 RSPEC_TESTS_FILTER_FILE: "${RSPEC_MATCHING_TESTS_PATH}" + SUCCESSFULLY_RETRIED_TEST_EXIT_CODE: 137 needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"] script: - !reference [.base-script, script] - rspec_paralellized_job "--tag ~quarantine --tag ~level:migration" + allow_failure: + # the exit code listed here must match the one defined for the variable SUCCESSFULLY_RETRIED_TEST_EXIT_CODE + exit_codes: 137 .base-artifacts: artifacts: diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index 507bb73c723..b6c273aeb99 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -7,20 +7,13 @@ review-cleanup: environment: name: review/regular-cleanup action: access - variables: - KUBE_NAMESPACE: "review-apps" # gcp_cleanup.sh requires passing KUBE_NAMESPACE variable before_script: - source scripts/utils.sh - - source scripts/review_apps/gcp_cleanup.sh - !reference [".use-kube-context", before_script] - install_gitlab_gem - - setup_gcp_dependencies + - setup_gcloud script: - - exit_code_cmd_1=0; - - exit_code_cmd_2=0; - - scripts/review_apps/automated_cleanup.rb || exit_code_cmd_1=$? - - gcp_cleanup || exit_code_cmd_2=$? - - if [ $exit_code_cmd_1 -ne 0 ] || [ $exit_code_cmd_2 -ne 0 ]; then (scripts/slack review-apps-monitoring "☠️ \`${CI_JOB_NAME}\` failed! ☠️ See ${CI_JOB_URL} - <https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/review-apps.md#review-cleanup-job-failed|📗 RUNBOOK 📕>" warning "GitLab Bot" && exit 1); fi; + - scripts/review_apps/automated_cleanup.rb || (scripts/slack review-apps-monitoring "☠️ \`${CI_JOB_NAME}\` failed! ☠️ See ${CI_JOB_URL} - <https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/review-apps.md#review-cleanup-job-failed|📗 RUNBOOK 📕>" warning "GitLab Bot" && exit 1); .base-review-checks: extends: @@ -28,8 +21,8 @@ review-cleanup: image: ${REVIEW_APPS_IMAGE} stage: prepare before_script: - - source scripts/review_apps/gcp_cleanup.sh - - setup_gcp_dependencies + - source scripts/utils.sh + - setup_gcloud - !reference [".use-kube-context", before_script] review-k8s-resources-count-checks: diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 2f86c449b79..f458f47f6b7 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -38,11 +38,16 @@ # Once https://gitlab.com/gitlab-org/gitlab/-/issues/373904 is implemented, we should be able to change this back to # if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_APPROVALS_COUNT > 0' # or any similar condition to check that the MR has *any* approval (not just required approval). +# +# Temprorarily adding || $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-full-rspec/ for backward compatibility, +# remove once https://gitlab.com/gitlab-org/quality/quality-engineering/team-tasks/-/issues/1557 is fully rolled out .if-merge-request-approved: &if-merge-request-approved - if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-full-rspec/' + if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS =~ /pipeline:mr-approved/ || $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-full-rspec/' +# Temprorarily adding && $CI_MERGE_REQUEST_LABELS !~ /pipeline:run-full-rspec/ for backward compatibility, +# remove once https://gitlab.com/gitlab-org/quality/quality-engineering/team-tasks/-/issues/1557 is fully rolled out .if-merge-request-not-approved: &if-merge-request-not-approved - if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS !~ /pipeline:run-full-rspec/' + if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_LABELS !~ /pipeline:mr-approved/ && $CI_MERGE_REQUEST_LABELS !~ /pipeline:run-full-rspec/' .if-automated-merge-request: &if-automated-merge-request if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "release-tools/update-gitaly" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /stable-ee$/' diff --git a/.rubocop_todo/style/empty_method.yml b/.rubocop_todo/style/empty_method.yml index 3c00f6278c5..0c7eb5c0d92 100644 --- a/.rubocop_todo/style/empty_method.yml +++ b/.rubocop_todo/style/empty_method.yml @@ -163,29 +163,3 @@ Style/EmptyMethod: - 'qa/qa/service/cluster_provider/k3d.rb' - 'qa/qa/service/cluster_provider/k3s.rb' - 'qa/qa/service/cluster_provider/minikube.rb' - - 'spec/controllers/concerns/check_rate_limit_spec.rb' - - 'spec/controllers/concerns/issuable_actions_spec.rb' - - 'spec/initializers/forbid_sidekiq_in_transactions_spec.rb' - - 'spec/lib/api/helpers/rate_limiter_spec.rb' - - 'spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb' - - 'spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb' - - 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb' - - 'spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb' - - 'spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb' - - 'spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb' - - 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb' - - 'spec/lib/gitlab/repository_archive_rate_limiter_spec.rb' - - 'spec/lib/gitlab/repository_cache_adapter_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb' - - 'spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb' - - 'spec/lib/gitlab/ssh_public_key_spec.rb' - - 'spec/lib/gitlab/utils/delegator_override/validator_spec.rb' - - 'spec/lib/gitlab/utils/delegator_override_spec.rb' - - 'spec/lib/gitlab/utils/override_spec.rb' - - 'spec/lib/gitlab/utils/strong_memoize_spec.rb' diff --git a/app/assets/javascripts/issues/show/components/incidents/constants.js b/app/assets/javascripts/issues/show/components/incidents/constants.js index db846009409..22db19610c1 100644 --- a/app/assets/javascripts/issues/show/components/incidents/constants.js +++ b/app/assets/javascripts/issues/show/components/incidents/constants.js @@ -14,6 +14,7 @@ export const timelineFormI18n = Object.freeze({ areaPlaceholder: s__('Incident|Timeline text...'), save: __('Save'), cancel: __('Cancel'), + delete: __('Delete'), description: __('Description'), hint: __('You can enter up to 280 characters'), textRemaining: (count) => n__('%d character remaining', '%d characters remaining', count), diff --git a/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue b/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue index 60fa8cb949b..8cdd62ca9ef 100644 --- a/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue +++ b/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue @@ -40,8 +40,10 @@ export default { :is-event-processed="editTimelineEventActive" :previous-occurred-at="event.occurredAt" :previous-note="event.note" + show-delete @save-event="saveEvent" @cancel="$emit('hide-edit')" + @delete="$emit('delete')" /> </div> </template> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue index 0f000815978..f1a3aebc990 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue @@ -32,6 +32,11 @@ export default { required: false, default: false, }, + showDelete: { + type: Boolean, + required: false, + default: false, + }, isEventProcessed: { type: Boolean, required: true, @@ -181,32 +186,42 @@ export default { </gl-form-group> </div> <gl-form-group class="gl-mb-0"> - <gl-button - variant="confirm" - category="primary" - class="gl-mr-3" - data-testid="save-button" - :disabled="!isTimelineTextValid" - :loading="isEventProcessed" - @click="handleSave(false)" - > - {{ $options.i18n.save }} - </gl-button> - <gl-button - v-if="showSaveAndAdd" - variant="confirm" - category="secondary" - class="gl-mr-3 gl-ml-n2" - data-testid="save-and-add-button" - :disabled="!isTimelineTextValid" - :loading="isEventProcessed" - @click="handleSave(true)" - > - {{ $options.i18n.saveAndAdd }} - </gl-button> - <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')"> - {{ $options.i18n.cancel }} - </gl-button> + <div class="gl-display-flex"> + <gl-button + variant="confirm" + category="primary" + class="gl-mr-3" + data-testid="save-button" + :disabled="!isTimelineTextValid" + :loading="isEventProcessed" + @click="handleSave(false)" + > + {{ $options.i18n.save }} + </gl-button> + <gl-button + v-if="showSaveAndAdd" + variant="confirm" + category="secondary" + class="gl-mr-3 gl-ml-n2" + data-testid="save-and-add-button" + :disabled="!isTimelineTextValid" + :loading="isEventProcessed" + @click="handleSave(true)" + > + {{ $options.i18n.saveAndAdd }} + </gl-button> + <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')"> + {{ $options.i18n.cancel }} + </gl-button> + <gl-button + v-if="showDelete" + class="gl-ml-auto btn-danger" + :disabled="isEventProcessed" + @click="$emit('delete')" + > + {{ $options.i18n.delete }} + </gl-button> + </div> <div class="timeline-event-bottom-border"></div> </gl-form-group> </form> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue index 321b7ccc14a..fad4f07cfc3 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue @@ -153,6 +153,7 @@ export default { :edit-timeline-event-active="editTimelineEventActive" @handle-save-edit="handleSaveEdit" @hide-edit="hideEdit()" + @delete="handleDelete(event)" /> <incident-timeline-event-item v-else diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss index addb5f8b9c2..afb0106452d 100644 --- a/app/assets/stylesheets/_page_specific_files.scss +++ b/app/assets/stylesheets/_page_specific_files.scss @@ -4,7 +4,6 @@ @import './pages/events'; @import './pages/groups'; @import './pages/hierarchy'; -@import './pages/issuable'; @import './pages/issues'; @import './pages/labels'; @import './pages/login'; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 9b4323979b7..a4693c20cef 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -280,3 +280,644 @@ grid-area: user; } } + +@mixin right-sidebar { + position: fixed; + top: $header-height; + // Default value for CSS var must contain a unit + // stylelint-disable-next-line length-zero-no-unit + bottom: var(--review-bar-height, 0px); + right: 0; + transition: width $gl-transition-duration-medium; + background-color: $white; + z-index: 200; + overflow: hidden; + +} + +.right-sidebar { + &:not(.right-sidebar-merge-requests) { + @include right-sidebar; + } + + &.right-sidebar-merge-requests { + @include media-breakpoint-down(md) { + @include right-sidebar; + z-index: 251; + } + } + + @include media-breakpoint-down(sm) { + z-index: 251; + } + + a:not(.btn) { + color: inherit; + + &:hover { + color: $blue-800; + } + } + + .gl-label .gl-label-link:hover { + color: inherit; + } + + .btn-link { + color: inherit; + } + + .issuable-header-text { + margin-top: 7px; + } + + .gutter-toggle { + display: flex; + align-items: center; + margin-left: 20px; + padding: 4px; + border-radius: 4px; + height: 24px; + + &:hover { + color: $gl-text-color; + background: $gray-50; + } + + &:hover, + &:focus { + text-decoration: none; + } + } + + &.right-sidebar-merge-requests { + .block, + .sidebar-contained-width, + .issuable-sidebar-header { + width: 100%; + } + + .block { + @include media-breakpoint-up(lg) { + padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5; + } + + &.participants { + border-bottom: 0; + } + } + } + + .block, + .sidebar-contained-width, + .issuable-sidebar-header { + @include clearfix; + padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5; + border-bottom: 1px solid $border-gray-normal; + // This prevents the mess when resizing the sidebar + // of elements repositioning themselves.. + width: $gutter-inner-width; + // -- + + &:last-child { + border: 0; + } + + &.assignee { + .author-link { + display: block; + position: relative; + + &:hover { + .author { + text-decoration: underline; + } + } + } + } + + &.time-tracking, + &.participants, + &.subscriptions, + &.with-sub-blocks { + padding-top: $gl-spacing-scale-5; + } + } + + .block-first { + padding-top: 0; + } + + .title { + color: $gl-text-color; + line-height: $gl-line-height-20; + + .avatar { + margin-left: 0; + } + } + + .selectbox { + display: none; + + &.show { + display: block; + } + } + + .btn-clipboard:hover { + color: $gl-text-color; + } + + .issuable-sidebar { + height: 100%; + + &:not(.is-merge-request) { + overflow-y: scroll; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + } + + &.is-merge-request { + @include media-breakpoint-down(sm) { + overflow-y: scroll; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + } + } + } + + &.right-sidebar-expanded { + &:not(.right-sidebar-merge-requests) { + width: $gutter-width; + } + + .value { + line-height: 1; + } + + .issuable-sidebar { + padding: 0 20px; + + &.is-merge-request { + @include media-breakpoint-up(lg) { + padding: 0; + + .issuable-context-form { + --initial-top: calc(#{$header-height} + 76px); + --top: var(--initial-top); + + @include gl-sticky; + @include gl-overflow-auto; + + top: var(--top); + height: calc(100vh - var(--top)); + padding: 0 15px; + margin-bottom: calc(var(--top) * -1); + + .with-performance-bar & { + --top: calc(var(--initial-top) + #{$performance-bar-height}); + } + + .with-system-header & { + --top: calc(var(--initial-top) + #{$system-header-height}); + } + + .with-performance-bar.with-system-header & { + --top: calc(var(--initial-top) + #{$system-header-height} + #{$performance-bar-height}); + } + } + } + } + } + + &:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) { + .issuable-sidebar-header { + display: none; + } + } + + .light { + font-weight: $gl-font-weight-normal; + } + + .no-value { + color: $gl-text-color-secondary; + } + + .sidebar-collapsed-icon { + display: none; + } + + .gutter-toggle { + text-align: center; + } + + .title .gutter-toggle { + margin-top: 0; + } + + .assignee .user-list .avatar { + margin: 0; + } + + .hide-expanded { + display: none; + } + } + + &.right-sidebar-collapsed { + /* Extra small devices (phones, less than 768px) */ + display: none; + /* Small devices (tablets, 768px and up) */ + + &:not(.right-sidebar-merge-requests) { + @include media-breakpoint-up(sm) { + display: block; + } + } + + &.right-sidebar-merge-requests { + @include media-breakpoint-up(lg) { + display: block; + } + } + + width: $gutter-collapsed-width; + padding: 0; + + .block, + .sidebar-contained-width, + .issuable-sidebar-header { + width: $gutter-collapsed-width - 2px; + padding: 0; + border-bottom: 0; + overflow: hidden; + } + + .block, + .gutter-toggle, + .sidebar-collapsed-container { + &.with-sub-blocks .sub-block:hover, + &:not(.with-sub-blocks):hover { + background-color: $gray-100; + } + } + + .participants { + border-bottom: 1px solid $border-gray-normal; + } + + .hide-collapsed { + display: none; + } + + .gutter-toggle { + width: 100%; + height: $sidebar-toggle-height; + margin-left: 0; + border-bottom: 1px solid $border-white-normal; + border-radius: 0; + } + + a.gutter-toggle { + display: flex; + justify-content: center; + flex-direction: column; + text-align: center; + } + + .merge-icon { + height: 12px; + width: 12px; + bottom: -5px; + right: 4px; + } + + .sidebar-collapsed-icon { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: $sidebar-toggle-height; + text-align: center; + color: $gl-text-color-secondary; + + > svg { + fill: $gl-text-color-secondary; + } + + &:hover:not(.disabled), + &:hover .todo-undone { + color: $gl-text-color; + + > svg { + fill: $gl-text-color; + } + } + + .todo-undone { + color: $blue-600; + fill: $blue-600; + } + + .author { + display: none; + } + + .avatar-counter:hover { + color: $gl-text-color-secondary; + border-color: $gl-text-color-secondary; + } + + .btn-clipboard { + /* + This change should be temporary, because the DOM currently gets + generated from a ruby definition in `app/helpers/button_helper.rb`. + As soon as the `copy to clipboard` button will be transferred to + Vue this should be adjusted as well. + */ + flex: 1; + align-self: stretch; + padding: 0; + + border: 0; + background: transparent; + color: $gl-text-color-secondary; + + &:hover { + color: $gl-text-color; + } + } + + &.multiple-users { + display: flex; + justify-content: center; + } + } + + .sidebar-avatar-counter { + width: 24px; + height: 24px; + border-radius: 12px; + + ~.merge-icon { + bottom: 0; + } + } + + .sidebar-collapsed-user { + padding-bottom: 0; + + .author-link { + padding-left: 0; + + .avatar { + position: static; + margin: 0; + } + } + } + + .issuable-header-btn { + display: none; + } + + .multiple-users { + .btn-link { + padding: 0; + border: 0; + + .avatar { + margin: 0; + } + } + + .btn-link:first-child { + position: absolute; + left: 10px; + z-index: 1; + } + + .btn-link:last-child { + position: absolute; + right: 10px; + + &:hover { + text-decoration: none; + } + } + } + + .milestone-title span, + .collapse-truncated-title { + @include str-truncated(100%); + display: block; + margin: 0 4px; + } + } + + .dropdown-menu-toggle { + width: 100%; + padding-top: 6px; + } + + .dropdown-menu { + width: 100%; + + /* + * Overwrite hover style for dropdown items, so that they are not blue + * This should be removed during dev of https://gitlab.com/gitlab-org/gitlab-foss/issues/44040 + */ + li a { + &:hover, + &:active, + &:focus, + &.is-focused { + @include dropdown-item-hover; + } + } + + } +} + +.with-performance-bar .right-sidebar { + top: calc(#{$header-height} + #{$performance-bar-height}); +} + +.sidebar-move-issue-confirmation-button { + width: 100%; + + &.is-loading { + .sidebar-move-issue-confirmation-loading-icon { + display: inline-block; + } + } +} + +.sidebar-move-issue-confirmation-loading-icon { + display: none; +} + +.issuable-show-labels { + .gl-label { + margin-bottom: 5px; + margin-right: 5px; + } + + a { + display: inline-block; + + .color-label { + padding: 4px $grid-size; + border-radius: $label-border-radius; + margin-right: 4px; + margin-bottom: 4px; + } + + &:hover .color-label { + text-decoration: underline; + } + } + + &.has-labels { + // this font size is a fix to + // prevent unintended spacing between labels + // which shows up when rendering markup has white-space + // characters present. + // see: https://css-tricks.com/fighting-the-space-between-inline-block-elements/#article-header-id-3 + font-size: 0; + margin-bottom: -5px; + } +} + +.assignee, +.reviewer { + .merge-icon { + color: $orange-400; + position: absolute; + bottom: -3px; + right: -3px; + filter: drop-shadow(0 0 0.5px $white) drop-shadow(0 0 1px $white) drop-shadow(0 0 2px $white); + } +} + +.participants-author { + &:nth-of-type(8n) { + padding-right: 0; + } + + .avatar.avatar-inline { + margin: 0; + } +} + +.participants-more, +.user-list-more { + margin-left: 5px; + + a, + .btn-link { + color: $gl-text-color-secondary; + } + + .btn-link { + padding: 0; + } + + .btn-link:hover { + color: $blue-800; + text-decoration: none; + } + + .btn-link:focus { + text-decoration: none; + } +} + +.sidebar-help-wrap { + .sidebar-help-state { + margin: 16px -20px -20px; + padding: 16px 20px; + } + + .help-state-toggle-enter-active { + transition: all 0.8s ease; + } + + .help-state-toggle-leave-active { + transition: all 0.5s ease; + } + + .help-state-toggle-enter, + .help-state-toggle-leave-active { + opacity: 0; + } +} + +.time-tracker { + .sidebar-collapsed-icon { + > .stopwatch-svg { + display: inline-block; + } + + svg { + width: 16px; + height: 16px; + fill: $gl-text-color-secondary; + } + + &:hover svg { + fill: $gl-text-color; + } + } + + .compare-meter { + &.over_estimate { + .time-remaining, + .compare-value.spent { + color: $red-500; + } + } + } + + .compare-display-container { + font-size: 13px; + } +} + +/* + * Following overrides are done to prevent + * legacy dropdown styles from influencing + * GitLab UI components used within GlDropdown + */ +.right-sidebar-collapsed { + .sidebar-grouped-item { + .sidebar-collapsed-icon { + margin-bottom: 0; + } + + .sidebar-collapsed-divider { + line-height: 5px; + font-size: 12px; + color: $gray-500; + + + .sidebar-collapsed-icon { + padding-top: 0; + } + } + } +} + +@include media-breakpoint-down(sm) { + // Overriding the following rule with the negative margin + // https://gitlab.com/gitlab-org/gitlab/-/blob/146c43c931c3743a140529307aea616e4aa9ff21/app/assets/stylesheets/framework/sidebar.scss#L1-5 + .container-fluid { + .issuable-list, + .issues-filters, + .epics-filters { + margin: 0 (-$gl-padding); + } + } +} diff --git a/app/assets/stylesheets/page_bundles/issuable.scss b/app/assets/stylesheets/page_bundles/issuable.scss new file mode 100644 index 00000000000..f364170c99f --- /dev/null +++ b/app/assets/stylesheets/page_bundles/issuable.scss @@ -0,0 +1,183 @@ +@import 'mixins_and_variables_and_functions'; + +.status-box { + padding: 0 $gl-btn-padding; + border-radius: $border-radius-default; + display: block; + float: left; + margin-right: $gl-padding-8; + color: var(--white, $white); + font-size: $gl-font-size; + line-height: $gl-line-height-24; +} + +.issuable-warning-icon { + background-color: var(--orange-50, $orange-50); + border-radius: $border-radius-default; + color: var(--orange-600, $orange-600); + width: $issuable-warning-size; + height: $issuable-warning-size; + text-align: center; + margin-right: $issuable-warning-icon-margin; + line-height: $gl-line-height-24; + flex: 0 0 auto; +} + +.limit-container-width { + .flash-container, + .detail-page-header, + .page-content-header, + .commit-box, + .info-well, + .commit-ci-menu, + .files-changed-inner, + .limited-header-width, + .limited-width-notes { + @include fixed-width-container; + } + + .issuable-details { + .detail-page-description, + .mr-source-target, + .mr-state-widget, + .merge-manually { + @include fixed-width-container; + } + } + + .merge-request-details { + .emoji-list-container { + @include fixed-width-container; + } + } +} + +.issuable-details { + section { + .issuable-discussion { + margin-right: 1px; + } + } + + .title-container { + display: flex; + align-items: flex-start; + } + + .title { + padding: 0; + margin-bottom: $gl-padding; + border-bottom: 0; + word-wrap: break-word; + overflow-wrap: break-word; + min-width: 0; + width: 100%; + text-align: initial; + } + + .btn-edit { + margin-left: auto; + } +} + +.detail-page-description { + padding: 16px 0; + + small { + color: var(--gray-500, $gray-500); + } +} + +.edited-text { + color: var(--gray-500, $gray-500); + display: block; + margin: 16px 0 0; + font-size: 85%; + + .author-link { + color: var(--gray-500, $gray-500); + } +} + +.user-item { + padding: 5px; + flex-basis: 20%; + + .user-link { + display: inline-block; + } +} + +.issuable-gutter-toggle { + @include media-breakpoint-down(sm) { + margin-left: $btn-side-margin; + } +} + +.issuable-meta { + flex: 1; + display: inline-block; + font-size: 14px; + align-self: center; + overflow: hidden; + text-overflow: ellipsis; + + .user-status-emoji { + margin-left: $gl-padding-4; + margin-right: 0; + } +} + +.js-issuable-selector-wrap { + .js-issuable-selector { + width: 100%; + } + + @include media-breakpoint-down(sm) { + margin-bottom: $gl-padding; + } +} + +.add-issuable-form-input-wrapper { + &.focus { + border-color: var(--gray-700, $gray-700); + @include gl-focus; + + input { + @include gl-shadow-none; + } + } + + .gl-show-field-errors &.form-control:not(textarea) { + height: auto; + } +} + +/* + * Following overrides are done to prevent + * legacy dropdown styles from influencing + * GitLab UI components used within GlDropdown + */ +.issuable-move-dropdown { + .b-dropdown-form { + @include gl-p-0; + } + + .gl-search-box-by-type button.gl-clear-icon-button:hover { + @include gl-bg-transparent; + } + + .issuable-move-button:not(.disabled):hover { + @include gl-text-white; + } +} + +.suggestion-footer { + font-size: 12px; + line-height: 15px; + + .avatar { + margin-top: -3px; + border: 0; + } +} diff --git a/app/assets/stylesheets/page_bundles/issuable_list.scss b/app/assets/stylesheets/page_bundles/issuable_list.scss new file mode 100644 index 00000000000..b08e129a805 --- /dev/null +++ b/app/assets/stylesheets/page_bundles/issuable_list.scss @@ -0,0 +1,96 @@ +@import 'mixins_and_variables_and_functions'; + +.issuable-list { + li { + .issuable-info-container { + flex: 1; + display: flex; + } + + .issuable-main-info { + flex: 1 auto; + margin-right: 10px; + min-width: 0; + + .issue-weight-icon, + .issue-estimate-icon { + vertical-align: sub; + } + } + + .issuable-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + flex: 1 0 auto; + + .controls { + margin-bottom: 2px; + line-height: 20px; + padding: 0; + } + } + + @include media-breakpoint-down(xs) { + .issuable-meta { + .controls li { + margin-right: 0; + } + } + } + + .issue-check { + min-width: 15px; + } + + .issuable-milestone, + .issuable-info, + .task-status, + .issuable-timestamp { + font-weight: $gl-font-weight-normal; + color: var(--gray-500, $gl-text-color-secondary); + + a { + color: var(--gl-text-color, $gl-text-color); + } + + .gl-label-link { + color: inherit; + + &:hover { + text-decoration: none; + + .gl-label-text:last-of-type { + text-decoration: underline; + } + } + } + + .milestone { + color: var(--gray-700, $gray-700); + } + } + + @media(max-width: map-get($grid-breakpoints, lg)-1) { + .task-status, + .issuable-due-date, + .issuable-weight, + .project-ref-path { + display: none; + } + } + } +} + +.issuable-list li, +.issuable-info-container .controls { + .avatar-counter { + display: inline-block; + vertical-align: middle; + min-width: 16px; + line-height: 14px; + height: 16px; + padding-left: 2px; + padding-right: 2px; + } +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss deleted file mode 100644 index 1b6e7954366..00000000000 --- a/app/assets/stylesheets/pages/issuable.scss +++ /dev/null @@ -1,912 +0,0 @@ -.status-box { - padding: 0 $gl-btn-padding; - border-radius: $border-radius-default; - display: block; - float: left; - margin-right: $gl-padding-8; - color: $white; - font-size: $gl-font-size; - line-height: $gl-line-height-24; -} - -.issuable-warning-icon { - background-color: $orange-50; - border-radius: $border-radius-default; - color: $orange-600; - width: $issuable-warning-size; - height: $issuable-warning-size; - text-align: center; - margin-right: $issuable-warning-icon-margin; - line-height: $gl-line-height-24; - flex: 0 0 auto; -} - -.limit-container-width { - .flash-container, - .detail-page-header, - .page-content-header, - .commit-box, - .info-well, - .commit-ci-menu, - .files-changed-inner, - .limited-header-width, - .limited-width-notes { - @include fixed-width-container; - } - - .issuable-details { - .detail-page-description, - .mr-source-target, - .mr-state-widget, - .merge-manually { - @include fixed-width-container; - } - } - - .merge-request-details { - .emoji-list-container { - @include fixed-width-container; - } - } -} - -.issuable-details { - section { - .issuable-discussion { - margin-right: 1px; - } - } - - .title-container { - display: flex; - align-items: flex-start; - } - - .title { - padding: 0; - margin-bottom: $gl-padding; - border-bottom: 0; - word-wrap: break-word; - overflow-wrap: break-word; - min-width: 0; - width: 100%; - text-align: initial; - } - - .btn-edit { - margin-left: auto; - } -} - -.issuable-show-labels { - .gl-label { - margin-bottom: 5px; - margin-right: 5px; - } - - a { - display: inline-block; - - .color-label { - padding: 4px $grid-size; - border-radius: $label-border-radius; - margin-right: 4px; - margin-bottom: 4px; - } - - &:hover .color-label { - text-decoration: underline; - } - } - - &.has-labels { - // this font size is a fix to - // prevent unintended spacing between labels - // which shows up when rendering markup has white-space - // characters present. - // see: https://css-tricks.com/fighting-the-space-between-inline-block-elements/#article-header-id-3 - font-size: 0; - margin-bottom: -5px; - } -} - -.assignee, -.reviewer { - .merge-icon { - color: $orange-400; - position: absolute; - bottom: -3px; - right: -3px; - filter: drop-shadow(0 0 0.5px $white) drop-shadow(0 0 1px $white) drop-shadow(0 0 2px $white); - } -} - -@mixin right-sidebar { - position: fixed; - top: $header-height; - // Default value for CSS var must contain a unit - // stylelint-disable-next-line length-zero-no-unit - bottom: var(--review-bar-height, 0px); - right: 0; - transition: width $gl-transition-duration-medium; - background-color: $white; - z-index: 200; - overflow: hidden; - -} - -.right-sidebar { - &:not(.right-sidebar-merge-requests) { - @include right-sidebar; - } - - &.right-sidebar-merge-requests { - @include media-breakpoint-down(md) { - @include right-sidebar; - z-index: 251; - } - } - - @include media-breakpoint-down(sm) { - z-index: 251; - } - - a:not(.btn) { - color: inherit; - - &:hover { - color: $blue-800; - } - } - - .gl-label .gl-label-link:hover { - color: inherit; - } - - .btn-link { - color: inherit; - } - - .issuable-header-text { - margin-top: 7px; - } - - .gutter-toggle { - display: flex; - align-items: center; - margin-left: 20px; - padding: 4px; - border-radius: 4px; - height: 24px; - - &:hover { - color: $gl-text-color; - background: $gray-50; - } - - &:hover, - &:focus { - text-decoration: none; - } - } - - &.right-sidebar-merge-requests { - .block, - .sidebar-contained-width, - .issuable-sidebar-header { - width: 100%; - } - - .block { - @include media-breakpoint-up(lg) { - padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5; - } - - &.participants { - border-bottom: 0; - } - } - } - - .block, - .sidebar-contained-width, - .issuable-sidebar-header { - @include clearfix; - padding: $gl-spacing-scale-4 0 $gl-spacing-scale-5; - border-bottom: 1px solid $border-gray-normal; - // This prevents the mess when resizing the sidebar - // of elements repositioning themselves.. - width: $gutter-inner-width; - // -- - - &:last-child { - border: 0; - } - - &.assignee { - .author-link { - display: block; - position: relative; - - &:hover { - .author { - text-decoration: underline; - } - } - } - } - - &.time-tracking, - &.participants, - &.subscriptions, - &.with-sub-blocks { - padding-top: $gl-spacing-scale-5; - } - } - - .block-first { - padding-top: 0; - } - - .title { - color: $gl-text-color; - line-height: $gl-line-height-20; - - .avatar { - margin-left: 0; - } - } - - .selectbox { - display: none; - - &.show { - display: block; - } - } - - .btn-clipboard:hover { - color: $gl-text-color; - } - - .issuable-sidebar { - height: 100%; - - &:not(.is-merge-request) { - overflow-y: scroll; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - } - - &.is-merge-request { - @include media-breakpoint-down(sm) { - overflow-y: scroll; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - } - } - } - - &.right-sidebar-expanded { - &:not(.right-sidebar-merge-requests) { - width: $gutter-width; - } - - .value { - line-height: 1; - } - - .issuable-sidebar { - padding: 0 20px; - - &.is-merge-request { - @include media-breakpoint-up(lg) { - padding: 0; - - .issuable-context-form { - --initial-top: calc(#{$header-height} + 76px); - --top: var(--initial-top); - - @include gl-sticky; - @include gl-overflow-auto; - - top: var(--top); - height: calc(100vh - var(--top)); - padding: 0 15px; - margin-bottom: calc(var(--top) * -1); - - .with-performance-bar & { - --top: calc(var(--initial-top) + #{$performance-bar-height}); - } - - .with-system-header & { - --top: calc(var(--initial-top) + #{$system-header-height}); - } - - .with-performance-bar.with-system-header & { - --top: calc(var(--initial-top) + #{$system-header-height} + #{$performance-bar-height}); - } - } - } - } - } - - &:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) { - .issuable-sidebar-header { - display: none; - } - } - - .light { - font-weight: $gl-font-weight-normal; - } - - .no-value { - color: $gl-text-color-secondary; - } - - .sidebar-collapsed-icon { - display: none; - } - - .gutter-toggle { - text-align: center; - } - - .title .gutter-toggle { - margin-top: 0; - } - - .assignee .user-list .avatar { - margin: 0; - } - - .hide-expanded { - display: none; - } - } - - &.right-sidebar-collapsed { - /* Extra small devices (phones, less than 768px) */ - display: none; - /* Small devices (tablets, 768px and up) */ - - &:not(.right-sidebar-merge-requests) { - @include media-breakpoint-up(sm) { - display: block; - } - } - - &.right-sidebar-merge-requests { - @include media-breakpoint-up(lg) { - display: block; - } - } - - width: $gutter-collapsed-width; - padding: 0; - - .block, - .sidebar-contained-width, - .issuable-sidebar-header { - width: $gutter-collapsed-width - 2px; - padding: 0; - border-bottom: 0; - overflow: hidden; - } - - .block, - .gutter-toggle, - .sidebar-collapsed-container { - &.with-sub-blocks .sub-block:hover, - &:not(.with-sub-blocks):hover { - background-color: $gray-100; - } - } - - .participants { - border-bottom: 1px solid $border-gray-normal; - } - - .hide-collapsed { - display: none; - } - - .gutter-toggle { - width: 100%; - height: $sidebar-toggle-height; - margin-left: 0; - border-bottom: 1px solid $border-white-normal; - border-radius: 0; - } - - a.gutter-toggle { - display: flex; - justify-content: center; - flex-direction: column; - text-align: center; - } - - .merge-icon { - height: 12px; - width: 12px; - bottom: -5px; - right: 4px; - } - - .sidebar-collapsed-icon { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: $sidebar-toggle-height; - text-align: center; - color: $gl-text-color-secondary; - - > svg { - fill: $gl-text-color-secondary; - } - - &:hover:not(.disabled), - &:hover .todo-undone { - color: $gl-text-color; - - > svg { - fill: $gl-text-color; - } - } - - .todo-undone { - color: $blue-600; - fill: $blue-600; - } - - .author { - display: none; - } - - .avatar-counter:hover { - color: $gl-text-color-secondary; - border-color: $gl-text-color-secondary; - } - - .btn-clipboard { - /* - This change should be temporary, because the DOM currently gets - generated from a ruby definition in `app/helpers/button_helper.rb`. - As soon as the `copy to clipboard` button will be transferred to - Vue this should be adjusted as well. - */ - flex: 1; - align-self: stretch; - padding: 0; - - border: 0; - background: transparent; - color: $gl-text-color-secondary; - - &:hover { - color: $gl-text-color; - } - } - - &.multiple-users { - display: flex; - justify-content: center; - } - } - - .sidebar-avatar-counter { - width: 24px; - height: 24px; - border-radius: 12px; - - ~.merge-icon { - bottom: 0; - } - } - - .sidebar-collapsed-user { - padding-bottom: 0; - - .author-link { - padding-left: 0; - - .avatar { - position: static; - margin: 0; - } - } - } - - .issuable-header-btn { - display: none; - } - - .multiple-users { - .btn-link { - padding: 0; - border: 0; - - .avatar { - margin: 0; - } - } - - .btn-link:first-child { - position: absolute; - left: 10px; - z-index: 1; - } - - .btn-link:last-child { - position: absolute; - right: 10px; - - &:hover { - text-decoration: none; - } - } - } - - .milestone-title span, - .collapse-truncated-title { - @include str-truncated(100%); - display: block; - margin: 0 4px; - } - } - - .dropdown-menu-toggle { - width: 100%; - padding-top: 6px; - } - - .dropdown-menu { - width: 100%; - - /* - * Overwrite hover style for dropdown items, so that they are not blue - * This should be removed during dev of https://gitlab.com/gitlab-org/gitlab-foss/issues/44040 - */ - li a { - &:hover, - &:active, - &:focus, - &.is-focused { - @include dropdown-item-hover; - } - } - - } -} - -.with-performance-bar .right-sidebar { - top: calc(#{$header-height} + #{$performance-bar-height}); -} - -.sidebar-move-issue-confirmation-button { - width: 100%; - - &.is-loading { - .sidebar-move-issue-confirmation-loading-icon { - display: inline-block; - } - } -} - -.sidebar-move-issue-confirmation-loading-icon { - display: none; -} - -.detail-page-description { - padding: 16px 0; - - small { - color: $gray-500; - } -} - -.edited-text { - color: $gray-500; - display: block; - margin: 16px 0 0; - font-size: 85%; - - .author-link { - color: $gray-500; - } -} - -.participants-author { - &:nth-of-type(8n) { - padding-right: 0; - } - - .avatar.avatar-inline { - margin: 0; - } -} - -.user-item { - padding: 5px; - flex-basis: 20%; - - .user-link { - display: inline-block; - } -} - -.participants-more, -.user-list-more { - margin-left: 5px; - - a, - .btn-link { - color: $gl-text-color-secondary; - } - - .btn-link { - padding: 0; - } - - .btn-link:hover { - color: $blue-800; - text-decoration: none; - } - - .btn-link:focus { - text-decoration: none; - } -} - -.issuable-gutter-toggle { - @include media-breakpoint-down(sm) { - margin-left: $btn-side-margin; - } -} - -.issuable-meta { - flex: 1; - display: inline-block; - font-size: 14px; - align-self: center; - overflow: hidden; - text-overflow: ellipsis; - - .user-status-emoji { - margin-left: $gl-padding-4; - margin-right: 0; - } -} - -.js-issuable-selector-wrap { - .js-issuable-selector { - width: 100%; - } - - @include media-breakpoint-down(sm) { - margin-bottom: $gl-padding; - } -} - -.issuable-list { - li { - .issuable-info-container { - flex: 1; - display: flex; - } - - .issuable-main-info { - flex: 1 auto; - margin-right: 10px; - min-width: 0; - - .issue-weight-icon, - .issue-estimate-icon { - vertical-align: sub; - } - } - - .issuable-meta { - display: flex; - flex-direction: column; - align-items: flex-end; - flex: 1 0 auto; - - .controls { - margin-bottom: 2px; - line-height: 20px; - padding: 0; - } - } - - @include media-breakpoint-down(xs) { - .issuable-meta { - .controls li { - margin-right: 0; - } - } - } - - .issue-check { - min-width: 15px; - } - - .issuable-milestone, - .issuable-info, - .task-status, - .issuable-timestamp { - font-weight: $gl-font-weight-normal; - color: $gl-text-color-secondary; - - a { - color: $gl-text-color; - } - - .gl-label-link { - color: inherit; - - &:hover { - text-decoration: none; - - .gl-label-text:last-of-type { - text-decoration: underline; - } - } - } - - .milestone { - color: $gray-700; - } - } - - @media(max-width: map-get($grid-breakpoints, lg)-1) { - .task-status, - .issuable-due-date, - .issuable-weight, - .project-ref-path { - display: none; - } - } - } -} - -.issuable-list li, -.issuable-info-container .controls { - .avatar-counter { - display: inline-block; - vertical-align: middle; - min-width: 16px; - line-height: 14px; - height: 16px; - padding-left: 2px; - padding-right: 2px; - } -} - -.add-issuable-form-input-wrapper { - &.focus { - border-color: $gray-700; - @include gl-focus; - - input { - @include gl-shadow-none; - } - } - - .gl-show-field-errors &.form-control:not(textarea) { - height: auto; - } -} - -.sidebar-help-wrap { - .sidebar-help-state { - margin: 16px -20px -20px; - padding: 16px 20px; - } - - .help-state-toggle-enter-active { - transition: all 0.8s ease; - } - - .help-state-toggle-leave-active { - transition: all 0.5s ease; - } - - .help-state-toggle-enter, - .help-state-toggle-leave-active { - opacity: 0; - } -} - -.time-tracker { - .sidebar-collapsed-icon { - > .stopwatch-svg { - display: inline-block; - } - - svg { - width: 16px; - height: 16px; - fill: $gl-text-color-secondary; - } - - &:hover svg { - fill: $gl-text-color; - } - } - - .compare-meter { - &.over_estimate { - .time-remaining, - .compare-value.spent { - color: $red-500; - } - } - } - - .compare-display-container { - font-size: 13px; - } -} - -/* - * Following overrides are done to prevent - * legacy dropdown styles from influencing - * GitLab UI components used within GlDropdown - */ -.issuable-move-dropdown { - .b-dropdown-form { - @include gl-p-0; - } - - .gl-search-box-by-type button.gl-clear-icon-button:hover { - @include gl-bg-transparent; - } - - .issuable-move-button:not(.disabled):hover { - @include gl-text-white; - } -} - -.right-sidebar-collapsed { - .sidebar-grouped-item { - .sidebar-collapsed-icon { - margin-bottom: 0; - } - - .sidebar-collapsed-divider { - line-height: 5px; - font-size: 12px; - color: $gray-500; - - + .sidebar-collapsed-icon { - padding-top: 0; - } - } - } -} - -.suggestion-footer { - font-size: 12px; - line-height: 15px; - - .avatar { - margin-top: -3px; - border: 0; - } -} - -@include media-breakpoint-down(sm) { - // Overriding the following rule with the negative margin - // https://gitlab.com/gitlab-org/gitlab/-/blob/146c43c931c3743a140529307aea616e4aa9ff21/app/assets/stylesheets/framework/sidebar.scss#L1-5 - .container-fluid { - .issuable-list, - .issues-filters, - .epics-filters { - margin: 0 (-$gl-padding); - } - } -} diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 7e85337c21f..71ca09717d9 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -46,7 +46,7 @@ module Ci # DELETE queries when the Ci::Build is destroyed. The next step is to remove `dependent: :destroy`. # Details: https://gitlab.com/gitlab-org/gitlab/-/issues/24644#note_689472685 has_many :job_artifacts, class_name: 'Ci::JobArtifact', foreign_key: :job_id, dependent: :destroy, inverse_of: :job # rubocop:disable Cop/ActiveRecordDependent - has_many :job_variables, class_name: 'Ci::JobVariable', foreign_key: :job_id + has_many :job_variables, class_name: 'Ci::JobVariable', foreign_key: :job_id, inverse_of: :job has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_job_id has_many :pages_deployments, inverse_of: :ci_build diff --git a/app/models/ci/job_variable.rb b/app/models/ci/job_variable.rb index 332a78b66ae..998f0647ad5 100644 --- a/app/models/ci/job_variable.rb +++ b/app/models/ci/job_variable.rb @@ -2,12 +2,15 @@ module Ci class JobVariable < Ci::ApplicationRecord + include Ci::Partitionable include Ci::NewHasVariable include Ci::RawVariable include BulkInsertSafe belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id + partitionable scope: :job + alias_attribute :secret_value, :value validates :key, uniqueness: { scope: :job_id }, unless: :dotenv_source? diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb index 6e01da2e9df..88692a735ba 100644 --- a/app/models/concerns/ci/partitionable.rb +++ b/app/models/concerns/ci/partitionable.rb @@ -32,6 +32,7 @@ module Ci Ci::BuildTraceMetadata Ci::BuildPendingState Ci::JobArtifact + Ci::JobVariable Ci::Pipeline Ci::PendingBuild Ci::RunningBuild diff --git a/app/models/packages/rpm/repository_file.rb b/app/models/packages/rpm/repository_file.rb index 4b5fa59c6ee..614ec9b3e56 100644 --- a/app/models/packages/rpm/repository_file.rb +++ b/app/models/packages/rpm/repository_file.rb @@ -8,6 +8,8 @@ module Packages include Packages::Installable INSTALLABLE_STATUSES = [:default].freeze + FILELISTS_FILENAME = 'filelists.xml' + FILELISTS_SIZE_LIMITATION = 20.megabytes enum status: { default: 0, pending_destruction: 1, processing: 2, error: 3 } @@ -20,6 +22,14 @@ module Packages mount_file_store_uploader Packages::Rpm::RepositoryFileUploader update_project_statistics project_statistics_name: :packages_size + + def self.has_oversized_filelists?(project_id:) + where( + project_id: project_id, + file_name: FILELISTS_FILENAME, + size: [FILELISTS_SIZE_LIMITATION..] + ).exists? + end end end end diff --git a/app/views/admin/application_settings/_error_tracking.html.haml b/app/views/admin/application_settings/_error_tracking.html.haml index 5a8aba5784e..aa42cd99e89 100644 --- a/app/views/admin/application_settings/_error_tracking.html.haml +++ b/app/views/admin/application_settings/_error_tracking.html.haml @@ -37,4 +37,4 @@ = f.label :error_tracking_api_url, _('Opstrace endpoint for Error Tracking integration'), class: 'label-light' = f.text_field :error_tracking_api_url, class: 'form-control gl-form-input' - = f.submit _('Save changes'), class: 'gl-button btn btn-confirm' + = f.submit _('Save changes'), pajamas_button: true diff --git a/app/views/admin/topics/_form.html.haml b/app/views/admin/topics/_form.html.haml index 9b9d97950cc..544310e312c 100644 --- a/app/views/admin/topics/_form.html.haml +++ b/app/views/admin/topics/_form.html.haml @@ -38,10 +38,13 @@ - if @topic.new_record? .form-actions - = f.submit _('Create topic'), class: "gl-button btn btn-confirm" - = link_to _('Cancel'), admin_topics_path, class: "gl-button btn btn-default btn-cancel" + = f.submit _('Create topic'), pajamas_button: true + = render Pajamas::ButtonComponent.new(href: admin_topics_path) do + = _('Cancel') - else .form-actions - = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' } - = link_to _('Cancel'), admin_topics_path, class: "gl-button btn btn-cancel" + = f.submit _('Save changes'), pajamas_button: true, data: { qa_selector: 'save_changes_button' } + = render Pajamas::ButtonComponent.new(href: admin_topics_path) do + = _('Cancel') + diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml index abaf169afd5..b9d50e48d05 100644 --- a/app/views/devise/unlocks/new.html.haml +++ b/app/views/devise/unlocks/new.html.haml @@ -1,14 +1,14 @@ = render 'devise/shared/tab_single', tab_title: _('Resend unlock instructions') .login-box .login-body - = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f| + = gitlab_ui_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f| .devise-errors = render "devise/shared/error_messages", resource: resource .form-group.gl-mb-6 = f.label :email = f.email_field :email, class: 'form-control', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', title: _('Please provide a valid email address.') .clearfix - = f.submit _('Resend unlock instructions'), class: 'gl-button btn btn-confirm' + = f.submit _('Resend unlock instructions'), pajamas_button: true, class: 'gl-w-full' .clearfix.prepend-top-20 = render 'devise/shared/sign_in_link' diff --git a/app/views/groups/_import_group_from_another_instance_panel.html.haml b/app/views/groups/_import_group_from_another_instance_panel.html.haml index d48bf0173a4..19c11d110b7 100644 --- a/app/views/groups/_import_group_from_another_instance_panel.html.haml +++ b/app/views/groups/_import_group_from_another_instance_panel.html.haml @@ -24,7 +24,7 @@ .gl-font-weight-normal - pat_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('user/profile/personal_access_tokens') } - short_living_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('security/token_overview', anchor: 'security-considerations') } - = s_('GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token.').html_safe % { pat_link_start: pat_link_start, pat_link_end: '</a>'.html_safe, short_living_link_start: short_living_link_start, short_living_link_end: '</a>'.html_safe } + = s_('GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time.').html_safe % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe, pat_link_start: pat_link_start, pat_link_end: '</a>'.html_safe, short_living_link_start: short_living_link_start, short_living_link_end: '</a>'.html_safe } = f.text_field :bulk_import_gitlab_access_token, placeholder: s_('GroupsNew|e.g. h8d3f016698e...'), class: 'gl-form-input gl-mt-3 col-xs-12 col-sm-8', required: true, autocomplete: 'off', diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index e032d64e219..6faa4758d66 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,4 +1,5 @@ - page_title _('Issues') +- add_page_specific_style 'page_bundles/issuable_list' - add_page_specific_style 'page_bundles/issues_list' = content_for :meta_tags do = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@group.name} issues") diff --git a/app/views/projects/issuable/_show.html.haml b/app/views/projects/issuable/_show.html.haml index 0898e0ae52d..ec233bc9aff 100644 --- a/app/views/projects/issuable/_show.html.haml +++ b/app/views/projects/issuable/_show.html.haml @@ -3,6 +3,7 @@ - page_card_attributes issuable.card_attributes - if issuable.relocation_target - page_canonical_link issuable.relocation_target.present(current_user: current_user).web_url +- add_page_specific_style 'page_bundles/issuable' = render "projects/issues/service_desk/alert_moved_from_service_desk", issue: issuable diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index ae6b157f452..f8f57934303 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -1,4 +1,5 @@ - page_title _('Issues') +- add_page_specific_style 'page_bundles/issuable_list' - add_page_specific_style 'page_bundles/issues_list' = content_for :meta_tags do = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} issues") diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index a3f40207d20..79da09c5205 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -5,6 +5,7 @@ - page_title _("Merge requests") - new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request') +- add_page_specific_style 'page_bundles/issuable_list' = content_for :meta_tags do = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} merge requests") diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 41c2187f0b1..e42501ea278 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -8,6 +8,7 @@ - page_card_attributes @merge_request.card_attributes - suggest_changes_help_path = help_page_path('user/project/merge_requests/reviews/suggestions.md') - mr_action = j(params[:tab].presence || 'show') +- add_page_specific_style 'page_bundles/issuable' - add_page_specific_style 'page_bundles/design_management' - add_page_specific_style 'page_bundles/merge_requests' - add_page_specific_style 'page_bundles/pipelines' diff --git a/config/application.rb b/config/application.rb index 51818579ccf..da6fb5e4c31 100644 --- a/config/application.rb +++ b/config/application.rb @@ -284,6 +284,8 @@ module Gitlab config.assets.precompile << "page_bundles/incident_management_list.css" config.assets.precompile << "page_bundles/incidents.css" config.assets.precompile << "page_bundles/issues_analytics.css" + config.assets.precompile << "page_bundles/issuable.css" + config.assets.precompile << "page_bundles/issuable_list.css" config.assets.precompile << "page_bundles/issues_list.css" config.assets.precompile << "page_bundles/issues_show.css" config.assets.precompile << "page_bundles/jira_connect.css" diff --git a/doc/architecture/blueprints/ci_pipeline_components/index.md b/doc/architecture/blueprints/ci_pipeline_components/index.md index 27b82a7e6b0..87a0e7c0b66 100644 --- a/doc/architecture/blueprints/ci_pipeline_components/index.md +++ b/doc/architecture/blueprints/ci_pipeline_components/index.md @@ -173,6 +173,20 @@ A `gitlab-<component-type>.yml` file: - Can optionally define **output data** that it returns. - Should be **validated statically** (for example: using JSON schema validators). +```yaml +spec: + inputs: + website: + environment: + default: test + test_run: + options: + - unit + - integration + - system +content: { ... } +``` + Components that are released in the catalog must have a `README.md` file in the same directory as the metadata YAML file. The `README.md` represents the documentation for the specific component, hence it's recommended even when not releasing versions in the catalog. @@ -274,6 +288,166 @@ NOTE: Any nesting more than 1 level is initially not permitted. This limitation encourages cohesion at project level and keeps complexity low. +## Input parameters `spec:inputs:` parameters + +If the component takes any input parameters they must be specified according to the following schema: + +```yaml +spec: + inputs: + website: # by default all declared inputs are mandatory. + environment: + default: test # apply default if not provided. This makes the input optional. + test_run: + options: # a choice must be made from the list since there is no default value. + - unit + - integration + - system +``` + +When using the component we pass the input parameters as follows: + +```yaml +include: + - component: org/my-component@1.0 + with: + website: ${MY_WEBSITE} # variables expansion + test_run: system + environment: $[[ inputs.environment ]] # interpolation of upstream inputs +``` + +Variables expansion must be supported for `with:` syntax as well as interpolation of +possible [inputs provided upstream](#input-parameters-for-pipelines). + +Input parameters are validated as soon as possible: + +1. Read the file `gitlab-template.yml` inside `org/my-component`. +1. Parse `spec:inputs` and validate the parameters against this schema. +1. If successfully validated, proceed with parsing `content:`. Return an error otherwise. +1. Interpolate input parameters inside the component's `content:`. + +```yaml +spec: + inputs: + environment: + options: [test, staging, production] +content: + "run-tests-$[[ inputs.environment ]]": + script: ./run-test + + scan-website: + script: ./scan-website $[[ inputs.environment ]] + rules: + - if: $[[ inputs.environment ]] == 'staging' + - if: $[[ inputs.environment ]] == 'production' +``` + +With `$[[ inputs.XXX ]]` inputs are interpolated immediately after parsing the `content:`. + +### Why input parameters and not environment variables? + +Until today we have been leveraging environment variables to pass information around. +For example, we use environment variables to pass information from an upstream pipeline to a +downstream pipeline. + +Using environment variables for passing information to a component is like declaring global +variables in programming languages. The more variables we declare the more we risk variable +conflicts and increase variables scope. + +Input parameters are like variables passed to the component which exist inside a specific +scope and they don't leak to the outside. +Inputs are not inherited from upstream `include`s. They must be passed explicitly. + +This paradigm allows to build more robust and isolated components as well as declare and +enforce contracts. + +### Input parameters for existing `include:` syntax + +Because we are adding input parameters to components used via `include:component` we have an opportunity to +extend it to other `include:` types support inputs via `with:` syntax: + +```yaml +include: + - component: org/my-component@1.0 + with: + foo: bar + - local: path/to/file.yml + with: + foo: bar + - project: org/another + file: .gitlab-ci.yml + with: + foo: bar + - remote: http://example.com/ci/config + with: + foo: bar + - template: Auto-DevOps.gitlab-ci.yml + with: + foo: bar +``` + +Then the configuration being included must specify the inputs: + +```yaml +spec: + inputs: + foo: + +# rest of the configuration +``` + +If a YAML includes content using `with:` but the including YAML doesn't specify `inputs:`, an error should be raised. + +|`with:`| `inputs:` | result | +| --- | --- | --- | +| specified | | raise error | +| specified | specified | validate inputs | +| | specified | use defaults | +| | | legacy `include:` without input passing | + +### Input parameters for pipelines + +Inputs can also be used to pass parameters to a pipeline when triggered and benefit from immediate validation. + +Today we have different use cases where using explicit input parameters would be beneficial: + +1. `Run Pipeline` UI form. + - **Problem today**: We are using top-level variables with `variables:*:description` to surface environment variables to the UI. + The problem with this is the mix of responsibilities as well as the jump in [precedence](../../../ci/variables/index.md#cicd-variable-precedence) + that a variable gets (from a YAML variable to a pipeline variable). + Building validation and features on top of this solution is challenging and complex. +1. Trigger a pipeline via API. For example `POST /projects/:id/pipelines/trigger` with `{ inputs: { provider: 'aws' } }` +1. Trigger a pipeline via `trigger:` syntax. + +```yaml +deploy-app: + trigger: + project: org/deployer + with: + provider: aws + deploy_environment: staging +``` + +To solve the problem of `Run Pipeline` UI form we could fully leverage the `spec:inputs` schema: + +```yaml +spec: + inputs: + concurrency: + default: 10 # displayed as default value in the input box + provider: # can enforce `required` in the form validation + description: Deployment provider # optional: render as input label. + deploy_environment: + options: # render a selectbox with options in order of how they are defined below + - staging # 1st option + - canary # 2nd option + - production # 3rd option + default: staging # selected by default in the UI. + # if `default:` is not specified, the user must explicitly select + # an option. + description: Deployment environment # optional: render as input label. +``` + ## Limits Any MVC that exposes a feature should be added with limitations from the beginning. diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index 852110a1415..93dcb57fe96 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -40,7 +40,7 @@ as it can cause the pipeline to behave unexpectedly. | `CI_COMMIT_SHORT_SHA` | 11.7 | all | The first eight characters of `CI_COMMIT_SHA`. | | `CI_COMMIT_TAG` | 9.0 | 0.5 | The commit tag name. Available only in pipelines for tags. | | `CI_COMMIT_TAG_MESSAGE` | 15.5 | all | The commit tag message. Available only in pipelines for tags. | -| `CI_COMMIT_TIMESTAMP` | 13.4 | all | The timestamp of the commit in the ISO 8601 format. | +| `CI_COMMIT_TIMESTAMP` | 13.4 | all | The timestamp of the commit in the [ISO 8601](https://www.rfc-editor.org/rfc/rfc3339#appendix-A) format. | | `CI_COMMIT_TITLE` | 10.8 | all | The title of the commit. The full first line of the message. | | `CI_CONCURRENT_ID` | all | 11.10 | The unique ID of build execution in a single executor. | | `CI_CONCURRENT_PROJECT_ID` | all | 11.10 | The unique ID of build execution in a single executor and project. | diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index 342d6b7845f..90f93e1db3f 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -345,6 +345,14 @@ Some contractions, however, should be avoided: <!-- vale gitlab.Possessive = YES --> +### Possessives + +Try to avoid using possessives (`'s`) for proper nouns, like organization or product names. + +For example, instead of `Docker's CLI`, use `the Docker CLI`. + +For details, see [the Google documentation style guide](https://developers.google.com/style/possessives#product,-feature,-and-company-names). + ### Prepositions Use prepositions at the end of the sentence when needed. @@ -361,7 +369,12 @@ These constructions are more casual than the alternatives: ### Acronyms If you use an acronym, spell it out on first use on a page. You do not need to spell it out more than once on a page. -When possible, try to avoid acronyms in topic titles. + +- **Titles:** Try to avoid acronyms in topic titles, especially if the acronym is not widely used. +- **Plurals:** Try not to make acronyms plural. For example, use `YAML files`, not `YAMLs`. If you must make an acronym plural, do not use an apostrophe. For example, use `APIs`, not `API's`. +- **Possessives:** Use caution when making an acronym possessive. If possible, + write the sentence to avoid making the acronym possessive. If you must make the + acronym possessive, consider spelling out the words. ### Numbers diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md index 05e9332b40f..883bede6724 100644 --- a/doc/development/pipelines/index.md +++ b/doc/development/pipelines/index.md @@ -68,8 +68,8 @@ Later on in [the `rspec fail-fast` job](#fail-fast-job-in-merge-request-pipeline In addition, there are a few circumstances where we would always run the full RSpec tests: -- when the `pipeline:run-all-rspec` label is set on the merge request -- when the `pipeline:run-full-rspec` label is set on the merge request, this label is assigned by triage automation when the merge request is approved by any reviewer +- when the `pipeline:run-all-rspec` label is set on the merge request. This label will trigger all RSpec tests including those run in the `as-if-foss` jobs. +- when the `pipeline:mr-approved` label is set on the merge request, and if the code changes satisfy the `backend-patterns` rule. Note that this label is assigned by triage automation when the merge request is approved by any reviewer. It is not recommended to apply this label manually. - when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch) - when the merge request is created in a security mirror - when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`) diff --git a/doc/operations/incident_management/incident_timeline_events.md b/doc/operations/incident_management/incident_timeline_events.md index 58448222356..897fcb9efc6 100644 --- a/doc/operations/incident_management/incident_timeline_events.md +++ b/doc/operations/incident_management/incident_timeline_events.md @@ -85,6 +85,8 @@ of an incident. ## Delete an event +> Ability to delete an event when editing it [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/372265) in GitLab 15.7. + You can also delete timeline events. Prerequisites: @@ -99,3 +101,9 @@ To delete a timeline event: 1. Select the **Timeline** tab. 1. On the right of a timeline event, select **More actions** (**{ellipsis_v}**) and then select **Delete**. 1. To confirm, select **Delete Event**. + +Alternatively: + +1. On the right of a timeline event, select **More actions** (**{ellipsis_v}**) and then select **Edit**. +1. Select **Delete**. +1. To confirm, select **Delete event**. diff --git a/lib/api/rpm_project_packages.rb b/lib/api/rpm_project_packages.rb index 40b8d022c6c..db85113f7a7 100644 --- a/lib/api/rpm_project_packages.rb +++ b/lib/api/rpm_project_packages.rb @@ -64,6 +64,10 @@ module API bad_request!('File is too large') end + if Packages::Rpm::RepositoryFile.has_oversized_filelists?(project_id: authorized_user_project.id) + bad_request!('Repository packages limit exceeded') + end + track_package_event( 'push_package', :rpm, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2cf744a96f0..8b88701ef0a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -19748,6 +19748,9 @@ msgstr "" msgid "GroupsNew|Contact an administrator to enable options for importing your group." msgstr "" +msgid "GroupsNew|Create a token with %{code_start}api%{code_end} and %{code_start}read_repository%{code_end} scopes in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, set a short expiration date for the token. Keep in mind that large migrations take more time." +msgstr "" + msgid "GroupsNew|Create group" msgstr "" @@ -19757,9 +19760,6 @@ msgstr "" msgid "GroupsNew|Create subgroup" msgstr "" -msgid "GroupsNew|Create this in the %{pat_link_start}user settings%{pat_link_end} of the source GitLab instance. For %{short_living_link_start}security reasons%{short_living_link_end}, use a short expiration date when creating the token." -msgstr "" - msgid "GroupsNew|GitLab source URL" msgstr "" @@ -37259,6 +37259,9 @@ msgstr "" msgid "SecurityReports|No vulnerabilities found for this pipeline" msgstr "" +msgid "SecurityReports|Not available" +msgstr "" + msgid "SecurityReports|Oops, something doesn't seem right." msgstr "" diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb index 7e1773a9c60..df1019f758b 100755 --- a/scripts/review_apps/automated_cleanup.rb +++ b/scripts/review_apps/automated_cleanup.rb @@ -22,6 +22,7 @@ module ReviewApps IGNORED_KUBERNETES_ERRORS = [ 'NotFound' ].freeze + ENVIRONMENTS_NOT_FOUND_THRESHOLD = 3 # $GITLAB_PROJECT_REVIEW_APP_CLEANUP_API_TOKEN => `Automated Review App Cleanup` project token def initialize( @@ -30,10 +31,11 @@ module ReviewApps api_endpoint: ENV['CI_API_V4_URL'], options: {} ) - @project_path = project_path - @gitlab_token = gitlab_token - @api_endpoint = api_endpoint - @dry_run = options[:dry_run] + @project_path = project_path + @gitlab_token = gitlab_token + @api_endpoint = api_endpoint + @dry_run = options[:dry_run] + @environments_not_found_count = 0 puts "Dry-run mode." if dry_run end @@ -172,7 +174,7 @@ module ReviewApps private - attr_reader :project_path, :gitlab_token, :api_endpoint, :dry_run + attr_reader :api_endpoint, :dry_run, :environments_not_found_count, :gitlab_token, :project_path def fetch_environment(environment) gitlab.environment(project_path, environment.id) @@ -186,10 +188,17 @@ module ReviewApps print_release_state(subject: 'Review app', release_name: environment.slug, release_date: release_date, action: 'deleting') gitlab.delete_environment(project_path, environment.id) unless dry_run + rescue Gitlab::Error::NotFound + puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) was not found: ignoring it" + environments_not_found_count += 1 + + if environments_not_found_count >= ENVIRONMENTS_NOT_FOUND_THRESHOLD + raise "At least #{ENVIRONMENTS_NOT_FOUND_THRESHOLD} environments were missing when we tried to delete them. Please investigate" + end rescue Gitlab::Error::Forbidden puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) is forbidden: skipping it" rescue Gitlab::Error::InternalServerError - puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) 500 error - ignoring it" + puts "Review app '#{environment.name}' / '#{environment.slug}' (##{environment.id}) 500 error: ignoring it" end def stop_environment(environment, deployment) diff --git a/scripts/review_apps/gcp_cleanup.sh b/scripts/review_apps/gcp_cleanup.sh deleted file mode 100755 index 114ac6f7ec0..00000000000 --- a/scripts/review_apps/gcp_cleanup.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash - -source scripts/utils.sh - -function setup_gcp_dependencies() { - apt-get update && apt-get install -y jq - - gcloud auth activate-service-account --key-file="${REVIEW_APPS_GCP_CREDENTIALS}" - gcloud config set project "${REVIEW_APPS_GCP_PROJECT}" -} - -# These scripts require the following environment variables: -# - REVIEW_APPS_GCP_REGION - e.g `us-central1` -# - KUBE_NAMESPACE - e.g `review-apps` - -function delete_firewall_rules() { - if [[ ${#@} -eq 0 ]]; then - echoinfo "No firewall rules to be deleted" true - return - fi - - echoinfo "Deleting firewall rules:" true - echo "${@}" - - if [[ ${DRY_RUN} = 1 ]]; then - echo "[DRY RUN] gcloud compute firewall-rules delete -q" "${@}" - else - gcloud compute firewall-rules delete -q "${@}" - fi -} - -function delete_forwarding_rules() { - if [[ ${#@} -eq 0 ]]; then - echoinfo "No forwarding rules to be deleted" true - return - fi - - echoinfo "Deleting forwarding rules:" true - echo "${@}" - - if [[ ${DRY_RUN} = 1 ]]; then - echo "[DRY RUN] gcloud compute forwarding-rules delete -q" "${@}" "--region ${REVIEW_APPS_GCP_REGION}" - else - gcloud compute forwarding-rules delete -q "${@}" --region "${REVIEW_APPS_GCP_REGION}" - fi -} - -function delete_target_pools() { - if [[ ${#@} -eq 0 ]]; then - echoinfo "No target pools to be deleted" true - return - fi - - echoinfo "Deleting target pools:" true - echo "${@}" - - if [[ ${DRY_RUN} = 1 ]]; then - echo "[DRY RUN] gcloud compute target-pools delete -q" "${@}" "--region ${REVIEW_APPS_GCP_REGION}" - else - gcloud compute target-pools delete -q "${@}" --region "${REVIEW_APPS_GCP_REGION}" - fi -} - -function delete_http_health_checks() { - if [[ ${#@} -eq 0 ]]; then - echoinfo "No http health checks to be deleted" true - return - fi - - echoinfo "Deleting http health checks:" true - echo "${@}" - - if [[ ${DRY_RUN} = 1 ]]; then - echo "[DRY RUN] gcloud compute http-health-checks delete -q" "${@}" - else - gcloud compute http-health-checks delete -q "${@}" - fi -} - -function get_related_firewall_rules() { - local forwarding_rule=${1} - - gcloud compute firewall-rules list --filter "name~${forwarding_rule}" --format "value(name)" -} - -function get_service_name_in_forwarding_rule() { - local forwarding_rule=${1} - - gcloud compute forwarding-rules describe "${forwarding_rule}" --region "${REVIEW_APPS_GCP_REGION}" --format "value(description)" | jq -r '.["kubernetes.io/service-name"]' -} - -function forwarding_rule_k8s_service_exists() { - local namespace="${KUBE_NAMESPACE}" - local namespaced_service_name=$(get_service_name_in_forwarding_rule "$forwarding_rule") - - if [[ ! $namespaced_service_name =~ ^"${namespace}" ]]; then - return 0 # this prevents `review-apps-ee` pipeline from deleting `review-apps-ce` resources and vice versa - fi - - local service_name=$(echo "${namespaced_service_name}" | sed -e "s/${namespace}\///g") - - kubectl get svc "${service_name}" -n "${namespace}" >/dev/null 2>&1 - local status=$? - - return $status -} - -function gcp_cleanup() { - if [[ ! $(command -v kubectl) ]]; then - echoerr "kubectl executable not found" - return 1 - fi - - if [[ -z "${REVIEW_APPS_GCP_REGION}" ]]; then - echoerr "REVIEW_APPS_GCP_REGION is not set." - return 1 - fi - - if [[ -z "${KUBE_NAMESPACE}" ]]; then - echoerr "KUBE_NAMESPACE is not set." - return 1 - fi - - if [[ -n "${DRY_RUN}" ]]; then - echoinfo "Running in DRY_RUN" - fi - - local target_pools_to_delete=() - local firewall_rules_to_delete=() - local forwarding_rules_to_delete=() - local http_health_checks_to_delete=() - - for forwarding_rule in $(gcloud compute forwarding-rules list --filter="region:(${REVIEW_APPS_GCP_REGION})" --format "value(name)"); do - echoinfo "Inspecting forwarding rule ${forwarding_rule}" true - - # We perform clean up when there is no more kubernetes service that require the resources. - # To identify the kubernetes service using the resources, - # we find the service name indicated in the forwarding rule description, e.g: - # - # $ gcloud compute forwarding-rules describe aff68b997da1211e984a042010af0019 - # # ... - # description: '{"kubernetes.io/service-name":"review-apps-ee/review-winh-eslin-809vqz-nginx-ingress-controller"}' - # # ... - if forwarding_rule_k8s_service_exists "${forwarding_rule}"; then - echoinfo "Skip clean up for ${forwarding_rule}" - else - echoinfo "Queuing forwarding rule, firewall rule, target pool and health check for ${forwarding_rule} to be cleaned up" - - firewall_rules_to_delete+=($(get_related_firewall_rules "${forwarding_rule}")) - forwarding_rules_to_delete+=(${forwarding_rule}) - target_pools_to_delete+=(${forwarding_rule}) - http_health_checks_to_delete+=(${forwarding_rule}) - fi - done - - delete_firewall_rules "${firewall_rules_to_delete[@]}" - delete_forwarding_rules "${forwarding_rules_to_delete[@]}" - delete_target_pools "${target_pools_to_delete[@]}" - delete_http_health_checks "${http_health_checks_to_delete[@]}" -} diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh index cdf397ed72d..e0586b8efdf 100644 --- a/scripts/rspec_helpers.sh +++ b/scripts/rspec_helpers.sh @@ -248,9 +248,37 @@ function retry_failed_rspec_examples() { # Merge the JUnit report from retry into the first-try report junit_merge "${JUNIT_RETRY_FILE}" "${JUNIT_RESULT_FILE}" --update-only + if [[ $rspec_run_status -eq 0 ]]; then + # The test is flaky because it succeeded after being retried. + # Make the pipeline "pass with warnings" if the flaky test is part of this MR. + warn_on_successfully_retried_test + fi + exit $rspec_run_status } +# Exit with an allowed_failure exit code if the flaky test was part of the MR that triggered this pipeline +function warn_on_successfully_retried_test { + local changed_files=$(git diff --name-only $CI_MERGE_REQUEST_TARGET_BRANCH_SHA | grep spec) + echoinfo "A test was flaky and succeeded after being retried. Checking to see if flaky test is part of this MR..." + + if [[ "$changed_files" == "" ]]; then + echoinfo "Flaky test was not part of this MR." + return + fi + + while read changed_file + do + echoinfo "Searching flaky tests for ${changed_file}" + if grep -q "$changed_file" "$RETRIED_TESTS_REPORT_PATH"; then + echoinfo "Exiting with code $SUCCESSFULLY_RETRIED_TEST_EXIT_CODE because the flaky test was part of this MR." + exit $SUCCESSFULLY_RETRIED_TEST_EXIT_CODE + fi + done <<< "$changed_files" + + echoinfo "Flaky test was not part of this MR." +} + function rspec_rerun_previous_failed_tests() { local test_file_count_threshold=${RSPEC_PREVIOUS_FAILED_TEST_FILE_COUNT_THRESHOLD:-10} local matching_tests_file=${1} diff --git a/scripts/utils.sh b/scripts/utils.sh index b9b40d3dbd5..c9e4a6a487d 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -218,3 +218,8 @@ function assets_image_tag() { echo -n "${CI_COMMIT_SHA}" fi } + +function setup_gcloud() { + gcloud auth activate-service-account --key-file="${REVIEW_APPS_GCP_CREDENTIALS}" + gcloud config set project "${REVIEW_APPS_GCP_PROJECT}" +} diff --git a/spec/controllers/concerns/check_rate_limit_spec.rb b/spec/controllers/concerns/check_rate_limit_spec.rb index 34ececfe639..75776acd520 100644 --- a/spec/controllers/concerns/check_rate_limit_spec.rb +++ b/spec/controllers/concerns/check_rate_limit_spec.rb @@ -19,11 +19,9 @@ RSpec.describe CheckRateLimit do @current_user = current_user end - def redirect_back_or_default(**args) - end + def redirect_back_or_default(**args); end - def render(**args) - end + def render(**args); end end end diff --git a/spec/controllers/concerns/issuable_actions_spec.rb b/spec/controllers/concerns/issuable_actions_spec.rb index 37d9dc080e1..34f47ed16f2 100644 --- a/spec/controllers/concerns/issuable_actions_spec.rb +++ b/spec/controllers/concerns/issuable_actions_spec.rb @@ -14,8 +14,7 @@ RSpec.describe IssuableActions do klass = Class.new do attr_reader :current_user, :project, :issuable - def self.before_action(action = nil, params = nil) - end + def self.before_action(action = nil, params = nil); end include IssuableActions @@ -40,8 +39,7 @@ RSpec.describe IssuableActions do [] end - def render(options) - end + def render(options); end end klass.new(issuable, project, user, finder_params_for_issuable) diff --git a/spec/factories/packages/rpm/rpm_repository_files.rb b/spec/factories/packages/rpm/rpm_repository_files.rb index 00755f49d98..7b86c593627 100644 --- a/spec/factories/packages/rpm/rpm_repository_files.rb +++ b/spec/factories/packages/rpm/rpm_repository_files.rb @@ -34,5 +34,9 @@ FactoryBot.define do trait :pending_destruction do status { :pending_destruction } end + + trait :filelists do + file_name { 'filelists.xml' } + end end end diff --git a/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js b/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js index 4c1638a9147..81c3c30bf8a 100644 --- a/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js +++ b/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js @@ -40,5 +40,13 @@ describe('Edit Timeline events', () => { expect(wrapper.emitted()).toEqual(cancelEvent); }); + + it('should emit the delete event', async () => { + const deleteEvent = { delete: [[]] }; + + await findTimelineEventsForm().vm.$emit('delete'); + + expect(wrapper.emitted()).toEqual(deleteEvent); + }); }); }); diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js index 0ce3f75f576..d5b199cc790 100644 --- a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js +++ b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js @@ -22,11 +22,12 @@ describe('Timeline events form', () => { useFakeDate(fakeDate); let wrapper; - const mountComponent = ({ mountMethod = shallowMountExtended } = {}) => { + const mountComponent = ({ mountMethod = shallowMountExtended } = {}, props = {}) => { wrapper = mountMethod(TimelineEventsForm, { propsData: { showSaveAndAdd: true, isEventProcessed: false, + ...props, }, stubs: { GlButton: true, @@ -43,6 +44,7 @@ describe('Timeline events form', () => { const findSubmitButton = () => wrapper.findByText(timelineFormI18n.save); const findSubmitAndAddButton = () => wrapper.findByText(timelineFormI18n.saveAndAdd); const findCancelButton = () => wrapper.findByText(timelineFormI18n.cancel); + const findDeleteButton = () => wrapper.findByText(timelineFormI18n.delete); const findDatePicker = () => wrapper.findComponent(GlDatepicker); const findHourInput = () => wrapper.findByTestId('input-hours'); const findMinuteInput = () => wrapper.findByTestId('input-minutes'); @@ -68,6 +70,9 @@ describe('Timeline events form', () => { findCancelButton().vm.$emit('click'); await waitForPromises(); }; + const deleteForm = () => { + findDeleteButton().vm.$emit('click'); + }; it('renders markdown-field component with correct list of toolbar items', () => { mountComponent({ mountMethod: mountExtended }); @@ -165,4 +170,38 @@ describe('Timeline events form', () => { expect(findSubmitAndAddButton().props('disabled')).toBe(true); }); }); + + describe('Delete button', () => { + it('does not show the delete button if showDelete prop is false', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: false }); + + expect(findDeleteButton().exists()).toBe(false); + }); + + it('shows the delete button if showDelete prop is true', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true }); + + expect(findDeleteButton().exists()).toBe(true); + }); + + it('disables the delete button if isEventProcessed prop is true', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true }); + + expect(findDeleteButton().props('disabled')).toBe(true); + }); + + it('does not disable the delete button if isEventProcessed prop is false', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: false }); + + expect(findDeleteButton().props('disabled')).toBe(false); + }); + + it('emits delete event on click', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true }); + + deleteForm(); + + expect(wrapper.emitted('delete')).toEqual([[]]); + }); + }); }); diff --git a/spec/initializers/forbid_sidekiq_in_transactions_spec.rb b/spec/initializers/forbid_sidekiq_in_transactions_spec.rb index a89ac73f6fa..7b1907a7451 100644 --- a/spec/initializers/forbid_sidekiq_in_transactions_spec.rb +++ b/spec/initializers/forbid_sidekiq_in_transactions_spec.rb @@ -34,8 +34,7 @@ RSpec.describe 'Sidekiq::Worker' do Class.new do include Sidekiq::Worker - def perform - end + def perform; end end end @@ -47,8 +46,7 @@ RSpec.describe 'Sidekiq::Worker' do context 'for mailers' do let(:mailer_class) do Class.new(ApplicationMailer) do - def test_mail - end + def test_mail; end end end diff --git a/spec/lib/api/helpers/rate_limiter_spec.rb b/spec/lib/api/helpers/rate_limiter_spec.rb index 2fed1cf3604..3640c7e30e7 100644 --- a/spec/lib/api/helpers/rate_limiter_spec.rb +++ b/spec/lib/api/helpers/rate_limiter_spec.rb @@ -19,8 +19,7 @@ RSpec.describe API::Helpers::RateLimiter do @current_user = current_user end - def render_api_error!(**args) - end + def render_api_error!(**args); end end end diff --git a/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb b/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb index b7915e6cf69..bd143c99d64 100644 --- a/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb @@ -34,8 +34,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqClientMiddleware do data_consistency data_consistency, feature_flag: feature_flag - def perform(*args) - end + def perform(*args); end end end diff --git a/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb b/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb index 8a9d4ba0b25..abf10456d0a 100644 --- a/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb @@ -33,8 +33,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_ data_consistency data_consistency, feature_flag: feature_flag - def perform(*args) - end + def perform(*args); end end end diff --git a/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb b/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb index 4619ca21a13..e8045f5afec 100644 --- a/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb @@ -185,8 +185,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a execute("create schema __test_schema") end - def down - end + def down; end end, query_matcher: /create schema __test_schema/, expected: { @@ -306,8 +305,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a detached_partitions_class.create!(drop_after: Time.current, table_name: '_test_table') end - def down - end + def down; end def detached_partitions_class Class.new(Gitlab::Database::Migration[2.0]::MigrationRecord) do @@ -450,8 +448,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a ApplicationSetting.last end - def down - end + def down; end end, query_matcher: /FROM "application_settings"/, expected: { @@ -475,8 +472,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a Feature.enabled?(:redis_hll_tracking, type: :ops) end - def down - end + def down; end end, query_matcher: /FROM "features"/, expected: { @@ -505,8 +501,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a end end - def down - end + def down; end end, query_matcher: /FROM ci_builds/, setup: -> (_) { skip_if_multiple_databases_not_setup }, diff --git a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb index 550f254c4da..e6014f81b74 100644 --- a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb +++ b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb @@ -229,11 +229,9 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do next_partition_if: method(:next_partition_if_wrapper), detach_partition_if: method(:detach_partition_if_wrapper) - def self.next_partition?(current_partition) - end + def self.next_partition?(current_partition); end - def self.detach_partition?(partition) - end + def self.detach_partition?(partition); end end end diff --git a/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb b/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb index 8b06f068503..884c4f625dd 100644 --- a/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb +++ b/spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb @@ -9,8 +9,7 @@ RSpec.describe Gitlab::Database::PostgresqlAdapter::DumpSchemaVersionsMixin do original_dump_schema_information end - def original_dump_schema_information - end + def original_dump_schema_information; end end klass.prepend(described_class) diff --git a/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb b/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb index 3e675a85999..3bb206c6627 100644 --- a/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb +++ b/spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb @@ -9,8 +9,7 @@ RSpec.describe Gitlab::Database::PostgresqlDatabaseTasks::LoadSchemaVersionsMixi original_structure_load end - def original_structure_load - end + def original_structure_load; end end klass.prepend(described_class) diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb index 524b373a5b7..960e2ed77fb 100644 --- a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb +++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb @@ -18,8 +18,7 @@ RSpec.describe Gitlab::Git::RuggedImpl::UseRugged do klazz = Class.new do include Gitlab::Git::RuggedImpl::UseRugged - def rugged_test(ref, test_number) - end + def rugged_test(ref, test_number); end end klazz.new diff --git a/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb b/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb index 49df70f3cb3..4599c647d5c 100644 --- a/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb +++ b/spec/lib/gitlab/repository_archive_rate_limiter_spec.rb @@ -7,8 +7,7 @@ RSpec.describe ::Gitlab::RepositoryArchiveRateLimiter do Class.new do include ::Gitlab::RepositoryArchiveRateLimiter - def check_rate_limit!(**args) - end + def check_rate_limit!(**args); end end end diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb index d14c3f44c6f..71a20cc58fd 100644 --- a/spec/lib/gitlab/repository_cache_adapter_spec.rb +++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb @@ -27,8 +27,7 @@ RSpec.describe Gitlab::RepositoryCacheAdapter do 'foo/bar' end - def project - end + def project; end def cached_methods [:letters] diff --git a/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb index dca00c85e30..472591bde5e 100644 --- a/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb @@ -40,8 +40,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::ClientMetrics do TestWorker.class_eval do include Sidekiq::Worker - def perform(*args) - end + def perform(*args); end end allow(Gitlab::Metrics).to receive(:counter).and_return(Gitlab::Metrics::NullMetric.instance) diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb index 44c8df73463..14eb568b974 100644 --- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb @@ -17,8 +17,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Client, :clean_gitlab_r include ApplicationWorker - def perform(*args) - end + def perform(*args); end end end diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb index 09548d21106..1b01793d80d 100644 --- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb @@ -18,8 +18,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_r self.class.work end - def self.work - end + def self.work; end end end diff --git a/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb b/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb index 8cf65e1be5b..cfb2c7ab5c3 100644 --- a/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb @@ -13,8 +13,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::InstrumentationLogger do include ApplicationWorker - def perform(*args) - end + def perform(*args); end end end diff --git a/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb index e58af1d60fe..c31f05f00e4 100644 --- a/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb @@ -10,8 +10,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::QueryAnalyzer, query_analyzers: false let(:queue) { 'some-queue' } let(:middleware) { described_class.new } - def do_queries - end + def do_queries; end subject { middleware.call(worker, job, queue) { do_queries } } diff --git a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb index 1a53a9b8701..f7cee6beb58 100644 --- a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb @@ -231,8 +231,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do include Sidekiq::Worker include WorkerAttributes - def perform(*args) - end + def perform(*args); end end allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer).and_return(load_balancer) @@ -306,8 +305,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do feature_category :not_owned end - def perform - end + def perform; end end end diff --git a/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb b/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb index 821d8b8fe7b..1b6cd7ac5fb 100644 --- a/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb @@ -17,8 +17,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do jobs.find { |job| job['args'] == args } end - def perform(*args) - end + def perform(*args); end end end @@ -38,8 +37,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do 'TestMailer' end - def test_mail - end + def test_mail; end end end diff --git a/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb index 05b328e55d3..2deab3064eb 100644 --- a/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb @@ -41,8 +41,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Server do include Sidekiq::Worker - def perform - end + def perform; end end end diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb index a2524314458..d4b0b1ea53b 100644 --- a/spec/lib/gitlab/ssh_public_key_spec.rb +++ b/spec/lib/gitlab/ssh_public_key_spec.rb @@ -260,8 +260,7 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true, fips_mode: false do context 'when the key is represented by a subclass of the class that is in the list of supported technologies' do it 'raises error' do rsa_subclass = Class.new(described_class.technology(:rsa).key_class) do - def initialize - end + def initialize; end end key = rsa_subclass.new diff --git a/spec/lib/gitlab/utils/delegator_override/validator_spec.rb b/spec/lib/gitlab/utils/delegator_override/validator_spec.rb index a58bc65c708..4fcf01ea256 100644 --- a/spec/lib/gitlab/utils/delegator_override/validator_spec.rb +++ b/spec/lib/gitlab/utils/delegator_override/validator_spec.rb @@ -7,8 +7,7 @@ RSpec.describe Gitlab::Utils::DelegatorOverride::Validator do Class.new(::SimpleDelegator) do extend(::Gitlab::Utils::DelegatorOverride) - def foo - end + def foo; end end.prepend(ee_delegator_extension) end @@ -16,18 +15,15 @@ RSpec.describe Gitlab::Utils::DelegatorOverride::Validator do Module.new do extend(::Gitlab::Utils::DelegatorOverride) - def bar - end + def bar; end end end let(:target_class) do Class.new do - def foo - end + def foo; end - def bar - end + def bar; end end end diff --git a/spec/lib/gitlab/utils/delegator_override_spec.rb b/spec/lib/gitlab/utils/delegator_override_spec.rb index 2dafa75e344..b566b7a2cad 100644 --- a/spec/lib/gitlab/utils/delegator_override_spec.rb +++ b/spec/lib/gitlab/utils/delegator_override_spec.rb @@ -7,25 +7,21 @@ RSpec.describe Gitlab::Utils::DelegatorOverride do Class.new(::SimpleDelegator) do extend(::Gitlab::Utils::DelegatorOverride) - def foo - end + def foo; end end end let(:target_class) do Class.new do - def foo - end + def foo; end - def bar - end + def bar; end end end let(:dummy_module) do Module.new do - def foobar - end + def foobar; end end end diff --git a/spec/lib/gitlab/utils/override_spec.rb b/spec/lib/gitlab/utils/override_spec.rb index a5e53c1dfc1..63f7b1623d8 100644 --- a/spec/lib/gitlab/utils/override_spec.rb +++ b/spec/lib/gitlab/utils/override_spec.rb @@ -35,8 +35,7 @@ RSpec.describe Gitlab::Utils::Override do override :good if bad_arity - def good(num) - end + def good(num); end elsif negative_arity def good(*args) super.succ diff --git a/spec/lib/gitlab/utils/strong_memoize_spec.rb b/spec/lib/gitlab/utils/strong_memoize_spec.rb index 6a09a8da58e..2b925d90369 100644 --- a/spec/lib/gitlab/utils/strong_memoize_spec.rb +++ b/spec/lib/gitlab/utils/strong_memoize_spec.rb @@ -59,22 +59,19 @@ RSpec.describe Gitlab::Utils::StrongMemoize do protected - def private_method - end + def private_method; end private :private_method strong_memoize_attr :private_method public - def protected_method - end + def protected_method; end protected :protected_method strong_memoize_attr :protected_method private - def public_method - end + def public_method; end public :public_method strong_memoize_attr :public_method end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 3589c1156a3..51ac7adc019 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -5632,4 +5632,28 @@ RSpec.describe Ci::Build do expect(runtime_hooks[0].script).to eq(["echo 'hello pre_get_sources_script'"]) end end + + describe 'partitioning', :ci_partitionable do + include Ci::PartitioningHelpers + + let(:new_pipeline) { create(:ci_pipeline) } + let(:ci_build) { FactoryBot.build(:ci_build, pipeline: new_pipeline) } + + before do + stub_current_partition_id + end + + it 'assigns partition_id to job variables successfully', :aggregate_failures do + ci_build.job_variables_attributes = [ + { key: 'TEST_KEY', value: 'new value' }, + { key: 'NEW_KEY', value: 'exciting new value' } + ] + + ci_build.save! + + expect(ci_build.job_variables.count).to eq(2) + expect(ci_build.job_variables.first.partition_id).to eq(ci_testing_partition_id) + expect(ci_build.job_variables.second.partition_id).to eq(ci_testing_partition_id) + end + end end diff --git a/spec/models/ci/job_variable_spec.rb b/spec/models/ci/job_variable_spec.rb index 4aebd3283f0..0a65708160a 100644 --- a/spec/models/ci/job_variable_spec.rb +++ b/spec/models/ci/job_variable_spec.rb @@ -2,11 +2,63 @@ require 'spec_helper' -RSpec.describe Ci::JobVariable do - subject { build(:ci_job_variable) } - +RSpec.describe Ci::JobVariable, feature_category: :continuous_integration do it_behaves_like "CI variable" - it { is_expected.to belong_to(:job) } - it { is_expected.to validate_uniqueness_of(:key).scoped_to(:job_id) } + describe 'associations' do + let!(:job_variable) { create(:ci_job_variable) } + + it { is_expected.to belong_to(:job) } + it { is_expected.to validate_uniqueness_of(:key).scoped_to(:job_id) } + end + + describe 'partitioning' do + let(:job_variable) { build(:ci_job_variable, job: ci_build) } + + context 'with build' do + let(:ci_build) { build(:ci_build, partition_id: ci_testing_partition_id) } + + it 'copies the partition_id from build' do + expect { job_variable.valid? }.to change { job_variable.partition_id }.to(ci_testing_partition_id) + end + + context 'when it is already set' do + let(:job_variable) { build(:ci_job_variable, partition_id: 125) } + + it 'does not change the partition_id value' do + expect { job_variable.valid? }.not_to change { job_variable.partition_id } + end + end + end + + context 'without build' do + subject(:job_variable) { build(:ci_job_variable, job: nil, partition_id: 125) } + + it { is_expected.to validate_presence_of(:partition_id) } + + it 'does not change the partition_id value' do + expect { job_variable.valid? }.not_to change { job_variable.partition_id } + end + end + + context 'when using bulk_insert', :ci_partitionable do + include Ci::PartitioningHelpers + + let(:new_pipeline) { create(:ci_pipeline) } + let(:ci_build) { create(:ci_build, pipeline: new_pipeline) } + let(:job_variable_2) { build(:ci_job_variable, job: ci_build) } + + before do + stub_current_partition_id + end + + it 'creates job variables successfully', :aggregate_failures do + described_class.bulk_insert!([job_variable, job_variable_2]) + + expect(described_class.count).to eq(2) + expect(described_class.first.partition_id).to eq(ci_testing_partition_id) + expect(described_class.last.partition_id).to eq(ci_testing_partition_id) + end + end + end end diff --git a/spec/models/packages/rpm/repository_file_spec.rb b/spec/models/packages/rpm/repository_file_spec.rb index 34347793dd8..1147fd66ac6 100644 --- a/spec/models/packages/rpm/repository_file_spec.rb +++ b/spec/models/packages/rpm/repository_file_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe Packages::Rpm::RepositoryFile, type: :model do +RSpec.describe Packages::Rpm::RepositoryFile, type: :model, feature_category: :package_registry do using RSpec::Parameterized::TableSyntax let_it_be(:repository_file) { create(:rpm_repository_file) } @@ -16,6 +16,32 @@ RSpec.describe Packages::Rpm::RepositoryFile, type: :model do it { is_expected.to validate_presence_of(:project) } end + describe '.has_oversized_filelists?' do + let_it_be(:filelists) { create(:rpm_repository_file, :filelists, size: 21.megabytes) } + + subject { described_class.has_oversized_filelists?(project_id: filelists.project_id) } + + context 'when has oversized filelists' do + it { expect(subject).to be true } + end + + context 'when filelists.xml is not oversized' do + before do + filelists.update!(size: 19.megabytes) + end + + it { expect(subject).to be_falsey } + end + + context 'when there is no filelists.xml' do + before do + filelists.destroy! + end + + it { expect(subject).to be_falsey } + end + end + context 'when updating project statistics' do context 'when the package file has an explicit size' do it_behaves_like 'UpdateProjectStatistics' do diff --git a/spec/requests/api/rpm_project_packages_spec.rb b/spec/requests/api/rpm_project_packages_spec.rb index f367ad8c59f..19247c30333 100644 --- a/spec/requests/api/rpm_project_packages_spec.rb +++ b/spec/requests/api/rpm_project_packages_spec.rb @@ -216,6 +216,19 @@ RSpec.describe API::RpmProjectPackages do expect(response.body).to match(/File is too large/) end end + + context 'when filelists.xml file size too large' do + before do + create(:rpm_repository_file, :filelists, size: 21.megabytes, project: project) + end + + it 'returns an error' do + upload_file(params: { file: file_upload }, request_headers: headers) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(response.body).to match(/Repository packages limit exceeded/) + end + end end def upload_file(params: {}, request_headers: headers) |