diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-16 03:09:09 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-16 03:09:09 +0300 |
commit | 72179bac11e9c18ea623e30594d6d427ef63db36 (patch) | |
tree | 601bc4fbb0a6c30ac866f11f278d80f920440c11 | |
parent | 9922389a501dfde79037426fa6d09144ee5f7e1a (diff) |
Add latest changes from gitlab-org/gitlab@master
53 files changed, 515 insertions, 731 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index dc21c6a62d5..32fe4c954cd 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -930,9 +930,6 @@ RSpec/EmptyLineAfterFinalLetItBe: - spec/services/clusters/applications/prometheus_health_check_service_spec.rb - spec/services/container_expiration_policy_service_spec.rb - spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb - - spec/services/design_management/copy_design_collection/copy_service_spec.rb - - spec/services/design_management/delete_designs_service_spec.rb - - spec/services/design_management/save_designs_service_spec.rb - spec/services/discussions/resolve_service_spec.rb - spec/services/discussions/unresolve_service_spec.rb - spec/services/feature_flags/create_service_spec.rb diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue index 7cd63ac152c..ddca5bc7d4f 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue @@ -90,9 +90,6 @@ export default { canRenderPipelineButton() { return this.latestPipelineDetachedFlag; }, - pipelineButtonClass() { - return !this.glFeatures.newPipelinesTable ? 'gl-md-display-none' : 'gl-lg-display-none'; - }, isForkMergeRequest() { return this.sourceProjectFullPath !== this.targetProjectFullPath; }, @@ -195,8 +192,7 @@ export default { <gl-button v-if="canRenderPipelineButton" block - class="gl-mt-3 gl-mb-3" - :class="pipelineButtonClass" + class="gl-mt-3 gl-mb-3 gl-lg-display-none" variant="confirm" data-testid="run_pipeline_button_mobile" :loading="state.isRunningMergeRequestPipeline" diff --git a/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue b/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue index b20d58b6ffe..98badb96ed7 100644 --- a/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue +++ b/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue @@ -51,7 +51,9 @@ export default { }); }, runnerId() { - return `${this.job.runner.description} (#${this.job.runner.id})`; + const { id, short_sha: token, description } = this.job?.runner; + + return `#${id} (${token}) ${description}`; }, shouldRenderBlock() { return Boolean(this.hasAnyDetail || this.hasTimeout || this.hasTags); diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_triggerer.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_triggerer.vue index c707b395192..0528e4c147c 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_triggerer.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_triggerer.vue @@ -17,19 +17,11 @@ export default { user() { return this.pipeline.user; }, - classes() { - const triggererClass = 'pipeline-triggerer'; - - if (this.glFeatures.newPipelinesTable) { - return triggererClass; - } - return `table-section section-10 d-none d-md-block ${triggererClass}`; - }, }, }; </script> <template> - <div :class="classes" data-testid="pipeline-triggerer"> + <div class="pipeline-triggerer" data-testid="pipeline-triggerer"> <user-avatar-link v-if="user" :link-href="user.path" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue index 0de520a2ca7..d39e120dc6c 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue @@ -49,19 +49,11 @@ export default { autoDevopsHelpPath() { return helpPagePath('topics/autodevops/index.md'); }, - classes() { - const tagsClass = 'pipeline-tags'; - - if (this.glFeatures.newPipelinesTable) { - return tagsClass; - } - return `table-section section-10 d-none d-md-block ${tagsClass}`; - }, }, }; </script> <template> - <div :class="classes" data-testid="pipeline-url-table-cell"> + <div class="pipeline-tags" data-testid="pipeline-url-table-cell"> <gl-link :href="pipeline.path" data-testid="pipeline-url-link" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue index aa27aa7e50d..47fc7023222 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue @@ -1,7 +1,6 @@ <script> import { GlTable, GlTooltipDirective } from '@gitlab/ui'; import { s__ } from '~/locale'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import eventHub from '../../event_hub'; import PipelineMiniGraph from './pipeline_mini_graph.vue'; import PipelineOperations from './pipeline_operations.vue'; @@ -10,7 +9,6 @@ import PipelineTriggerer from './pipeline_triggerer.vue'; import PipelineUrl from './pipeline_url.vue'; import PipelinesCommit from './pipelines_commit.vue'; import PipelinesStatusBadge from './pipelines_status_badge.vue'; -import PipelinesTableRowComponent from './pipelines_table_row.vue'; import PipelinesTimeago from './time_ago.vue'; const DEFAULT_TD_CLASS = 'gl-p-5!'; @@ -83,7 +81,6 @@ export default { PipelineOperations, PipelinesStatusBadge, PipelineStopModal, - PipelinesTableRowComponent, PipelinesTimeago, PipelineTriggerer, PipelineUrl, @@ -91,7 +88,6 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - mixins: [glFeatureFlagMixin()], props: { pipelines: { type: Array, @@ -149,41 +145,7 @@ export default { </script> <template> <div class="ci-table"> - <div v-if="!glFeatures.newPipelinesTable" data-testid="legacy-ci-table"> - <div class="gl-responsive-table-row table-row-header" role="row"> - <div class="table-section section-10 js-pipeline-status" role="rowheader"> - {{ s__('Pipeline|Status') }} - </div> - <div class="table-section section-10 js-pipeline-info pipeline-info" role="rowheader"> - {{ s__('Pipeline|Pipeline') }} - </div> - <div class="table-section section-10 js-triggerer-info triggerer-info" role="rowheader"> - {{ s__('Pipeline|Triggerer') }} - </div> - <div class="table-section section-20 js-pipeline-commit pipeline-commit" role="rowheader"> - {{ s__('Pipeline|Commit') }} - </div> - <div class="table-section section-15 js-pipeline-stages pipeline-stages" role="rowheader"> - {{ s__('Pipeline|Stages') }} - </div> - <div class="table-section section-15" role="rowheader"></div> - <div class="table-section section-20" role="rowheader"> - <slot name="table-header-actions"></slot> - </div> - </div> - <pipelines-table-row-component - v-for="model in pipelines" - :key="model.id" - :pipeline="model" - :pipeline-schedule-url="pipelineScheduleUrl" - :update-graph-dropdown="updateGraphDropdown" - :view-type="viewType" - :canceling-pipeline="cancelingPipeline" - /> - </div> - <gl-table - v-else :fields="$options.fields" :items="pipelines" tbody-tr-class="commit" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue deleted file mode 100644 index f684a0b0fcd..00000000000 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue +++ /dev/null @@ -1,269 +0,0 @@ -<script> -import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui'; -import { __ } from '~/locale'; -import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; -import CommitComponent from '~/vue_shared/components/commit.vue'; -import eventHub from '../../event_hub'; -import PipelineMiniGraph from './pipeline_mini_graph.vue'; -import PipelineTriggerer from './pipeline_triggerer.vue'; -import PipelineUrl from './pipeline_url.vue'; -import PipelinesArtifactsComponent from './pipelines_artifacts.vue'; -import PipelinesManualActionsComponent from './pipelines_manual_actions.vue'; -import PipelinesTimeago from './time_ago.vue'; - -export default { - i18n: { - cancelTitle: __('Cancel'), - redeployTitle: __('Retry'), - }, - directives: { - GlTooltip: GlTooltipDirective, - GlModalDirective, - }, - components: { - PipelinesManualActionsComponent, - PipelinesArtifactsComponent, - CommitComponent, - PipelineMiniGraph, - PipelineUrl, - PipelineTriggerer, - CiBadge, - PipelinesTimeago, - GlButton, - }, - props: { - pipeline: { - type: Object, - required: true, - }, - pipelineScheduleUrl: { - type: String, - required: false, - default: '', - }, - updateGraphDropdown: { - type: Boolean, - required: false, - default: false, - }, - viewType: { - type: String, - required: true, - }, - cancelingPipeline: { - type: Number, - required: false, - default: null, - }, - }, - data() { - return { - isRetrying: false, - }; - }, - computed: { - actions() { - if (!this.pipeline || !this.pipeline.details) { - return []; - } - const { details } = this.pipeline; - return [...(details.manual_actions || []), ...(details.scheduled_actions || [])]; - }, - /** - * If provided, returns the commit tag. - * Needed to render the commit component column. - * - * This field needs a lot of verification, because of different possible cases: - * - * 1. person who is an author of a commit might be a GitLab user - * 2. if person who is an author of a commit is a GitLab user, they can have a GitLab avatar - * 3. If GitLab user does not have avatar they might have a Gravatar - * 4. If committer is not a GitLab User they can have a Gravatar - * 5. We do not have consistent API object in this case - * 6. We should improve API and the code - * - * @returns {Object|Undefined} - */ - commitAuthor() { - let commitAuthorInformation; - - if (!this.pipeline || !this.pipeline.commit) { - return null; - } - - // 1. person who is an author of a commit might be a GitLab user - if (this.pipeline.commit.author) { - // 2. if person who is an author of a commit is a GitLab user - // they can have a GitLab avatar - if (this.pipeline.commit.author.avatar_url) { - commitAuthorInformation = this.pipeline.commit.author; - - // 3. If GitLab user does not have avatar, they might have a Gravatar - } else if (this.pipeline.commit.author_gravatar_url) { - commitAuthorInformation = { - ...this.pipeline.commit.author, - avatar_url: this.pipeline.commit.author_gravatar_url, - }; - } - // 4. If committer is not a GitLab User, they can have a Gravatar - } else { - commitAuthorInformation = { - avatar_url: this.pipeline.commit.author_gravatar_url, - path: `mailto:${this.pipeline.commit.author_email}`, - username: this.pipeline.commit.author_name, - }; - } - - return commitAuthorInformation; - }, - commitTag() { - return this.pipeline?.ref?.tag; - }, - commitRef() { - return this.pipeline?.ref; - }, - commitUrl() { - return this.pipeline?.commit?.commit_path; - }, - commitShortSha() { - return this.pipeline?.commit?.short_id; - }, - commitTitle() { - return this.pipeline?.commit?.title; - }, - pipelineStatus() { - return this.pipeline?.details?.status ?? {}; - }, - hasStages() { - return this.pipeline?.details?.stages?.length > 0; - }, - displayPipelineActions() { - return ( - this.pipeline.flags.retryable || - this.pipeline.flags.cancelable || - this.pipeline.details.manual_actions.length || - this.pipeline.details.artifacts.length - ); - }, - isChildView() { - return this.viewType === 'child'; - }, - isCancelling() { - return this.cancelingPipeline === this.pipeline.id; - }, - }, - watch: { - pipeline() { - this.isRetrying = false; - }, - }, - methods: { - handleCancelClick() { - eventHub.$emit('openConfirmationModal', { - pipeline: this.pipeline, - endpoint: this.pipeline.cancel_path, - }); - }, - handleRetryClick() { - this.isRetrying = true; - eventHub.$emit('retryPipeline', this.pipeline.retry_path); - }, - handlePipelineActionRequestComplete() { - // warn the pipelines table to update - eventHub.$emit('refreshPipelinesTable'); - }, - }, -}; -</script> -<template> - <div class="commit gl-responsive-table-row"> - <div class="table-section section-10 commit-link"> - <div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Status') }}</div> - <div class="table-mobile-content"> - <ci-badge - :status="pipelineStatus" - :show-text="!isChildView" - :icon-classes="'gl-vertical-align-middle!'" - data-qa-selector="pipeline_commit_status" - /> - </div> - </div> - - <pipeline-url :pipeline="pipeline" :pipeline-schedule-url="pipelineScheduleUrl" /> - <pipeline-triggerer :pipeline="pipeline" /> - - <div class="table-section section-wrap section-20"> - <div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Commit') }}</div> - <div class="table-mobile-content"> - <commit-component - :tag="commitTag" - :commit-ref="commitRef" - :commit-url="commitUrl" - :merge-request-ref="pipeline.merge_request" - :short-sha="commitShortSha" - :title="commitTitle" - :author="commitAuthor" - :show-ref-info="!isChildView" - /> - </div> - </div> - - <div class="table-section section-wrap section-15 stage-cell"> - <div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Stages') }}</div> - <div class="table-mobile-content"> - <pipeline-mini-graph - v-if="hasStages" - :stages="pipeline.details.stages" - :update-dropdown="updateGraphDropdown" - @pipelineActionRequestComplete="handlePipelineActionRequestComplete" - /> - </div> - </div> - - <pipelines-timeago class="gl-text-right" :pipeline="pipeline" /> - - <div - v-if="displayPipelineActions" - class="table-section section-20 table-button-footer pipeline-actions" - > - <div class="btn-group table-action-buttons"> - <pipelines-manual-actions-component v-if="actions.length > 0" :actions="actions" /> - - <pipelines-artifacts-component - v-if="pipeline.details.artifacts.length" - :artifacts="pipeline.details.artifacts" - /> - - <gl-button - v-if="pipeline.flags.retryable" - v-gl-tooltip.hover - :aria-label="$options.i18n.redeployTitle" - :title="$options.i18n.redeployTitle" - :disabled="isRetrying" - :loading="isRetrying" - class="js-pipelines-retry-button" - data-qa-selector="pipeline_retry_button" - icon="repeat" - variant="default" - category="secondary" - @click="handleRetryClick" - /> - - <gl-button - v-if="pipeline.flags.cancelable" - v-gl-tooltip.hover - v-gl-modal-directive="'confirmation-modal'" - :aria-label="$options.i18n.cancelTitle" - :title="$options.i18n.cancelTitle" - :loading="isCancelling" - :disabled="isCancelling" - icon="close" - variant="danger" - category="primary" - class="js-pipelines-cancel-button" - @click="handleCancelClick" - /> - </div> - </div> - </div> -</template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue index 7082446d60e..e6b03751350 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue @@ -48,12 +48,6 @@ export default { return `${hh}:${mm}:${ss}`; }, - legacySectionClass() { - return !this.glFeatures.newPipelinesTable ? 'table-section section-15' : ''; - }, - legacyTableMobileClass() { - return !this.glFeatures.newPipelinesTable ? 'table-mobile-content' : ''; - }, showInProgress() { return !this.duration && !this.finishedTime && !this.skipped; }, @@ -64,51 +58,40 @@ export default { }; </script> <template> - <div :class="legacySectionClass"> - <div v-if="!glFeatures.newPipelinesTable" class="table-mobile-header" role="rowheader"> - {{ s__('Pipeline|Duration') }} - </div> - <div :class="legacyTableMobileClass"> - <span v-if="showInProgress" data-testid="pipeline-in-progress"> - <gl-icon - v-if="stuck" - name="warning" - class="gl-mr-2" - :size="12" - data-testid="warning-icon" - /> - <gl-icon - v-else - name="hourglass" - class="gl-vertical-align-baseline! gl-mr-2" - :size="12" - data-testid="hourglass-icon" - /> - {{ s__('Pipeline|In progress') }} - </span> + <div> + <span v-if="showInProgress" data-testid="pipeline-in-progress"> + <gl-icon v-if="stuck" name="warning" class="gl-mr-2" :size="12" data-testid="warning-icon" /> + <gl-icon + v-else + name="hourglass" + class="gl-vertical-align-baseline! gl-mr-2" + :size="12" + data-testid="hourglass-icon" + /> + {{ s__('Pipeline|In progress') }} + </span> - <span v-if="showSkipped" data-testid="pipeline-skipped"> - <gl-icon name="status_skipped_borderless" class="gl-mr-2" :size="16" /> - {{ s__('Pipeline|Skipped') }} - </span> + <span v-if="showSkipped" data-testid="pipeline-skipped"> + <gl-icon name="status_skipped_borderless" class="gl-mr-2" :size="16" /> + {{ s__('Pipeline|Skipped') }} + </span> - <p v-if="duration" class="duration"> - <gl-icon name="timer" class="gl-vertical-align-baseline!" :size="12" /> - {{ durationFormatted }} - </p> + <p v-if="duration" class="duration"> + <gl-icon name="timer" class="gl-vertical-align-baseline!" :size="12" /> + {{ durationFormatted }} + </p> - <p v-if="finishedTime" class="finished-at d-none d-md-block"> - <gl-icon name="calendar" class="gl-vertical-align-baseline!" :size="12" /> + <p v-if="finishedTime" class="finished-at d-none d-md-block"> + <gl-icon name="calendar" class="gl-vertical-align-baseline!" :size="12" /> - <time - v-gl-tooltip - :title="tooltipTitle(finishedTime)" - data-placement="top" - data-container="body" - > - {{ timeFormatted(finishedTime) }} - </time> - </p> - </div> + <time + v-gl-tooltip + :title="tooltipTitle(finishedTime)" + data-placement="top" + data-container="body" + > + {{ timeFormatted(finishedTime) }} + </time> + </p> </div> </template> diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 3853797e0bf..0c3ff07bc76 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -54,8 +54,6 @@ class Projects::CommitController < Projects::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def pipelines - set_pipeline_feature_flag - @pipelines = @commit.pipelines.order(id: :desc) @pipelines = @pipelines.where(ref: params[:ref]).page(params[:page]).per(30) if params[:ref] @@ -135,10 +133,6 @@ class Projects::CommitController < Projects::ApplicationController private - def set_pipeline_feature_flag - push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml) - end - def create_new_branch? params[:create_merge_request].present? || !can?(current_user, :push_code, @project) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 4d612cd45d0..1f370fce48d 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -39,7 +39,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true) push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml) push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml) - push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml) push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml) push_frontend_feature_flag(:usage_data_i_testing_summary_widget_total, @project, default_enabled: :yaml) push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml) diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 1828c6466e6..ee1e10221ec 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -19,7 +19,6 @@ class Projects::PipelinesController < Projects::ApplicationController push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:jira_for_vulnerabilities, project, type: :development, default_enabled: :yaml) - push_frontend_feature_flag(:new_pipelines_table, project, default_enabled: :yaml) end before_action :ensure_pipeline, only: [:show] diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 6b65b1c0bff..504ebb5606e 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -229,6 +229,9 @@ module ApplicationSettingsHelper :email_author_in_body, :enabled_git_access_protocol, :enforce_terms, + :external_pipeline_validation_service_timeout, + :external_pipeline_validation_service_token, + :external_pipeline_validation_service_url, :first_day_of_week, :force_pages_access_control, :gitaly_timeout_default, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index dbc09a3c9b2..f405f5ca5d3 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -468,6 +468,13 @@ class ApplicationSetting < ApplicationRecord validates :admin_mode, inclusion: { in: [true, false], message: _('must be a boolean value') } + validates :external_pipeline_validation_service_url, + addressable_url: true, allow_blank: true + + validates :external_pipeline_validation_service_timeout, + allow_nil: true, + numericality: { only_integer: true, greater_than: 0 } + attr_encrypted :asset_proxy_secret_key, mode: :per_attribute_iv, key: Settings.attr_encrypted_db_key_base_truncated, @@ -496,6 +503,7 @@ class ApplicationSetting < ApplicationRecord attr_encrypted :ci_jwt_signing_key, encryption_options_base_truncated_aes_256_gcm attr_encrypted :secret_detection_token_revocation_token, encryption_options_base_truncated_aes_256_gcm attr_encrypted :cloud_license_auth_token, encryption_options_base_truncated_aes_256_gcm + attr_encrypted :external_pipeline_validation_service_token, encryption_options_base_truncated_aes_256_gcm validates :disable_feed_token, inclusion: { in: [true, false], message: _('must be a boolean value') } diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index dba72f88986..e67d261d3b4 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -72,6 +72,9 @@ module ApplicationSettingImplementation eks_secret_access_key: nil, email_restrictions_enabled: false, email_restrictions: nil, + external_pipeline_validation_service_timeout: nil, + external_pipeline_validation_service_token: nil, + external_pipeline_validation_service_url: nil, first_day_of_week: 0, gitaly_timeout_default: 55, gitaly_timeout_fast: 10, diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb index 97e5b336a35..6d6ba920a3b 100644 --- a/app/serializers/runner_entity.rb +++ b/app/serializers/runner_entity.rb @@ -3,7 +3,7 @@ class RunnerEntity < Grape::Entity include RequestAwareEntity - expose :id, :description + expose :id, :description, :short_sha expose :edit_path, if: -> (*) { can_edit_runner? } do |runner| edit_project_runner_path(project, runner) diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml index 8882bab2e57..c3e4626c14e 100644 --- a/app/views/admin/runners/_runner.html.haml +++ b/app/views/admin/runners/_runner.html.haml @@ -1,3 +1,6 @@ +-# Note: This file should stay aligned with: +-# `app/views/groups/runners/_runner.html.haml` + .gl-responsive-table-row{ id: dom_id(runner) } .table-section.section-10.section-wrap .table-mobile-header{ role: 'rowheader' }= _('Type') @@ -13,15 +16,13 @@ - unless runner.active? %span.badge.badge-pill.gl-badge.sm.badge-danger= _("paused") - .table-section.section-10 - .table-mobile-header{ role: 'rowheader' }= _('Runner token') + .table-section.section-30 + .table-mobile-header{ role: 'rowheader' }= s_('Runners|Runner') .table-mobile-content - = link_to runner.short_sha, admin_runner_path(runner) - - .table-section.section-20 - .table-mobile-header{ role: 'rowheader' }= _('Description') - .table-mobile-content.str-truncated.has-tooltip{ title: runner.description } - = runner.description + = link_to("##{runner.id} (#{runner.short_sha})", admin_runner_path(runner)) + .gl-text-truncate + %span{ title: runner.description, data: { toggle: 'tooltip', container: 'body' } } + = runner.description .table-section.section-10 .table-mobile-header{ role: 'rowheader' }= _('Version') diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 564d6000bc1..a38615d9b1b 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -122,8 +122,7 @@ .table-holder .gl-responsive-table-row.table-row-header{ role: 'row' } .table-section.section-10{ role: 'rowheader' }= _('Type/State') - .table-section.section-10{ role: 'rowheader' }= _('Runner token') - .table-section.section-20{ role: 'rowheader' }= _('Description') + .table-section.section-30{ role: 'rowheader' }= s_('Runners|Runner') .table-section.section-10{ role: 'rowheader' }= _('Version') .table-section.section-10{ role: 'rowheader' }= _('IP Address') .table-section.section-5{ role: 'rowheader' }= _('Projects') diff --git a/app/views/groups/runners/_index.html.haml b/app/views/groups/runners/_index.html.haml index 724a41b84e6..187588f5f11 100644 --- a/app/views/groups/runners/_index.html.haml +++ b/app/views/groups/runners/_index.html.haml @@ -86,8 +86,7 @@ .table-holder .gl-responsive-table-row.table-row-header{ role: 'row' } .table-section.section-10{ role: 'rowheader' }= _('Type/State') - .table-section.section-10{ role: 'rowheader' }= _('Runner token') - .table-section.section-20{ role: 'rowheader' }= _('Description') + .table-section.section-30{ role: 'rowheader' }= s_('Runners|Runner') .table-section.section-10{ role: 'rowheader' }= _('Version') .table-section.section-10{ role: 'rowheader' }= _('IP Address') .table-section.section-5{ role: 'rowheader' }= _('Projects') diff --git a/app/views/groups/runners/_runner.html.haml b/app/views/groups/runners/_runner.html.haml index c687167e519..89e32c0999c 100644 --- a/app/views/groups/runners/_runner.html.haml +++ b/app/views/groups/runners/_runner.html.haml @@ -1,3 +1,6 @@ +-# Note: This file should stay aligned with: +-# `app/views/admin/runners/_runner.html.haml` + .gl-responsive-table-row{ id: dom_id(runner) } .table-section.section-10.section-wrap .table-mobile-header{ role: 'rowheader' }= _('Type') @@ -15,15 +18,13 @@ %span.badge.badge-pill.gl-badge.sm.badge-danger = _('paused') - .table-section.section-10 - .table-mobile-header{ role: 'rowheader' }= _('Runner token') + .table-section.section-30 + .table-mobile-header{ role: 'rowheader' }= s_('Runners|Runner') .table-mobile-content - = link_to runner.short_sha, group_runner_path(@group, runner) - - .table-section.section-20 - .table-mobile-header{ role: 'rowheader' }= _('Description') - .table-mobile-content.str-truncated.has-tooltip{ title: runner.description } - = runner.description + = link_to("##{runner.id} (#{runner.short_sha})", group_runner_path(@group, runner)) + .gl-text-truncate + %span{ title: runner.description, data: { toggle: 'tooltip', container: 'body' } } + = runner.description .table-section.section-10 .table-mobile-header{ role: 'rowheader' }= _('Version') diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml index 6a87e052272..4eb321050ad 100644 --- a/app/views/profiles/keys/_key.html.haml +++ b/app/views/profiles/keys/_key.html.haml @@ -23,7 +23,7 @@ = s_('Profiles|Last used:') = key.last_used_at ? time_ago_with_tooltip(key.last_used_at) : _('Never') %span.expires.gl-mr-3 - = s_('Profiles|Expires:') + = key.expired? ? s_('Profiles|Expired:') : s_('Profiles|Expires:') = key.expires_at ? key.expires_at.to_date : _('Never') %span.key-created-at.gl-display-flex.gl-align-items-center - if key.can_delete? diff --git a/changelogs/unreleased/326197-change-runner-identifiers-job-sidebar-admin-list.yml b/changelogs/unreleased/326197-change-runner-identifiers-job-sidebar-admin-list.yml new file mode 100644 index 00000000000..79991023375 --- /dev/null +++ b/changelogs/unreleased/326197-change-runner-identifiers-job-sidebar-admin-list.yml @@ -0,0 +1,6 @@ +--- +title: Display runner token and description consistently in the job sidebar and admin + list +merge_request: 58904 +author: +type: changed diff --git a/changelogs/unreleased/326995-refer-to-expired-ssh-keys-in-the-past-tense.yml b/changelogs/unreleased/326995-refer-to-expired-ssh-keys-in-the-past-tense.yml new file mode 100644 index 00000000000..59caecce8f3 --- /dev/null +++ b/changelogs/unreleased/326995-refer-to-expired-ssh-keys-in-the-past-tense.yml @@ -0,0 +1,5 @@ +--- +title: Update profile SSH key labels to refer to expired keys as "Expired" +merge_request: 59381 +author: +type: changed diff --git a/changelogs/unreleased/issue-325836-fix-empty-line-after-let-it-be-services-design-management.yml b/changelogs/unreleased/issue-325836-fix-empty-line-after-let-it-be-services-design-management.yml new file mode 100644 index 00000000000..4a2c45c7117 --- /dev/null +++ b/changelogs/unreleased/issue-325836-fix-empty-line-after-let-it-be-services-design-management.yml @@ -0,0 +1,5 @@ +--- +title: Fix EmptyLineAfterFinalLetItBe in spec/services/design_management +merge_request: 58416 +author: Huzaifa Iftikhar @huzaifaiftikhar +type: fixed diff --git a/changelogs/unreleased/mobsf_version.yml b/changelogs/unreleased/mobsf_version.yml new file mode 100644 index 00000000000..d26a3d6726d --- /dev/null +++ b/changelogs/unreleased/mobsf_version.yml @@ -0,0 +1,5 @@ +--- +title: Update MobSF to version 3.4.0 in the SAST template +merge_request: 58594 +author: +type: changed diff --git a/changelogs/unreleased/pb-flip-on-new-pipelines-table-ff.yml b/changelogs/unreleased/pb-flip-on-new-pipelines-table-ff.yml new file mode 100644 index 00000000000..6759962cd59 --- /dev/null +++ b/changelogs/unreleased/pb-flip-on-new-pipelines-table-ff.yml @@ -0,0 +1,5 @@ +--- +title: Use GlTable design system component for pipelines table +merge_request: 58581 +author: +type: changed diff --git a/changelogs/unreleased/validation-service-token-to-config.yml b/changelogs/unreleased/validation-service-token-to-config.yml new file mode 100644 index 00000000000..1242e3f02ba --- /dev/null +++ b/changelogs/unreleased/validation-service-token-to-config.yml @@ -0,0 +1,5 @@ +--- +title: Obtain pipeline validation service token from config not ENV. +merge_request: 59101 +author: +type: other diff --git a/config/feature_flags/development/new_pipelines_table.yml b/config/feature_flags/development/new_pipelines_table.yml deleted file mode 100644 index 33a7226dd34..00000000000 --- a/config/feature_flags/development/new_pipelines_table.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: new_pipelines_table -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54958 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/322599 -milestone: '13.10' -type: development -group: group::continuous integration -default_enabled: true diff --git a/db/migrate/20210414131600_add_external_pipeline_validation_to_application_setting.rb b/db/migrate/20210414131600_add_external_pipeline_validation_to_application_setting.rb new file mode 100644 index 00000000000..537f7727691 --- /dev/null +++ b/db/migrate/20210414131600_add_external_pipeline_validation_to_application_setting.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddExternalPipelineValidationToApplicationSetting < ActiveRecord::Migration[6.0] + def up + add_column :application_settings, :external_pipeline_validation_service_timeout, :integer + # rubocop:disable Migration/AddLimitToTextColumns + add_column :application_settings, :encrypted_external_pipeline_validation_service_token, :text + add_column :application_settings, :encrypted_external_pipeline_validation_service_token_iv, :text + add_column :application_settings, :external_pipeline_validation_service_url, :text + # rubocop:enable Migration/AddLimitToTextColumns + end + + def down + remove_column :application_settings, :external_pipeline_validation_service_timeout + remove_column :application_settings, :encrypted_external_pipeline_validation_service_token + remove_column :application_settings, :encrypted_external_pipeline_validation_service_token_iv + remove_column :application_settings, :external_pipeline_validation_service_url + end +end diff --git a/db/migrate/20210415142700_add_url_limit_to_pipeline_validation.rb b/db/migrate/20210415142700_add_url_limit_to_pipeline_validation.rb new file mode 100644 index 00000000000..9c38e04a96b --- /dev/null +++ b/db/migrate/20210415142700_add_url_limit_to_pipeline_validation.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddUrlLimitToPipelineValidation < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + CONSTRAINT_NAME = 'app_settings_ext_pipeline_validation_service_url_text_limit' + + def up + add_text_limit :application_settings, :external_pipeline_validation_service_url, 255, constraint_name: CONSTRAINT_NAME + end + + def down + remove_check_constraint(:application_settings, CONSTRAINT_NAME) + end +end diff --git a/db/schema_migrations/20210414131600 b/db/schema_migrations/20210414131600 new file mode 100644 index 00000000000..2ed1c9856ae --- /dev/null +++ b/db/schema_migrations/20210414131600 @@ -0,0 +1 @@ +199c8a540cb4a0dd30a86a81f993798afb3e7384f1176b71a780d5950a52eb5f
\ No newline at end of file diff --git a/db/schema_migrations/20210415142700 b/db/schema_migrations/20210415142700 new file mode 100644 index 00000000000..22b10173911 --- /dev/null +++ b/db/schema_migrations/20210415142700 @@ -0,0 +1 @@ +2d6d62b036c937136dfbb11becfd3c2c705f0db1e3a38fdcefe676106168ab29
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 6df80a14d0f..710a112d521 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9441,7 +9441,12 @@ CREATE TABLE application_settings ( admin_mode boolean DEFAULT false NOT NULL, delayed_project_removal boolean DEFAULT false NOT NULL, lock_delayed_project_removal boolean DEFAULT false NOT NULL, + external_pipeline_validation_service_timeout integer, + encrypted_external_pipeline_validation_service_token text, + encrypted_external_pipeline_validation_service_token_iv text, + external_pipeline_validation_service_url text, CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), + CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)), CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)), CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)), diff --git a/doc/api/settings.md b/doc/api/settings.md index 913a3699fe4..6322f14442c 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -87,7 +87,10 @@ Example response: "personal_access_token_prefix": "GL-", "rate_limiting_response_text": null, "keep_latest_artifact": true, - "admin_mode": false + "admin_mode": false, + "external_pipeline_validation_service_timeout": null, + "external_pipeline_validation_service_token": null, + "external_pipeline_validation_service_url": null } ``` @@ -183,7 +186,10 @@ Example response: "personal_access_token_prefix": "GL-", "rate_limiting_response_text": null, "keep_latest_artifact": true, - "admin_mode": false + "admin_mode": false, + "external_pipeline_validation_service_timeout": null, + "external_pipeline_validation_service_token": null, + "external_pipeline_validation_service_url": null } ``` @@ -283,6 +289,9 @@ listed in the descriptions of the relevant settings. | `external_authorization_service_enabled` | boolean | no | (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url`) Enable using an external authorization service for accessing projects | | `external_authorization_service_timeout` | float | required by:<br>`external_authorization_service_enabled` | The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001). | | `external_authorization_service_url` | string | required by:<br>`external_authorization_service_enabled` | URL to which authorization requests are directed. | +| `external_pipeline_validation_service_url` | string | no | URL to which pipeline validation requests are directed. | +| `external_pipeline_validation_service_token` | string | no | An optional token to include as the `X-Gitlab-Token` header in requests to the URL in external_pipeline_validation_service_url. | +| `external_pipeline_validation_service_timeout` | integer | no | How long to wait for a response from the pipeline validation service before giving up and assuming 'OK'. | | `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from | | `first_day_of_week` | integer | no | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday, `1` for Monday, and `6` for Saturday. | | `geo_node_allowed_ips` | string | yes | **(PREMIUM)** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. | diff --git a/doc/user/application_security/dependency_list/img/dependency_list_v12_10.png b/doc/user/application_security/dependency_list/img/dependency_list_v12_10.png Binary files differdeleted file mode 100644 index 2755b42f1e4..00000000000 --- a/doc/user/application_security/dependency_list/img/dependency_list_v12_10.png +++ /dev/null diff --git a/doc/user/application_security/dependency_list/img/dependency_list_v13_11.png b/doc/user/application_security/dependency_list/img/dependency_list_v13_11.png Binary files differnew file mode 100644 index 00000000000..5b2bd985ce4 --- /dev/null +++ b/doc/user/application_security/dependency_list/img/dependency_list_v13_11.png diff --git a/doc/user/application_security/dependency_list/index.md b/doc/user/application_security/dependency_list/index.md index 6ed3b15d829..25b7615a8ae 100644 --- a/doc/user/application_security/dependency_list/index.md +++ b/doc/user/application_security/dependency_list/index.md @@ -26,7 +26,7 @@ To view your project's dependencies, ensure you meet the following requirements: ## View a project's dependencies -![Dependency list](img/dependency_list_v12_10.png) +![Dependency list](img/dependency_list_v13_11.png) GitLab displays dependencies with the following information: @@ -44,7 +44,8 @@ can also be sorted by name or by the packager that installed them. If a dependency has known vulnerabilities, view them by clicking the arrow next to the dependency's name or the badge that indicates how many known vulnerabilities exist. For each -vulnerability, its severity and description appears below it. +vulnerability, its severity and description appears below it. To view more details of a vulnerability, +select the vulnerability’s description. The [vulnerability's details](../vulnerabilities) page is opened. ### Dependency paths diff --git a/doc/user/packages/conan_repository/index.md b/doc/user/packages/conan_repository/index.md index a519f07da77..9df4aeb404a 100644 --- a/doc/user/packages/conan_repository/index.md +++ b/doc/user/packages/conan_repository/index.md @@ -128,7 +128,7 @@ To add the remote: For example: ```shell - conan search Hello* --all --remote=gitlab + conan search Hello* --remote=gitlab ``` ### Add a remote for your instance diff --git a/lib/gitlab/ci/pipeline/chain/validate/external.rb b/lib/gitlab/ci/pipeline/chain/validate/external.rb index 9d1aeb75ccc..93f4bebd41d 100644 --- a/lib/gitlab/ci/pipeline/chain/validate/external.rb +++ b/lib/gitlab/ci/pipeline/chain/validate/external.rb @@ -82,18 +82,18 @@ module Gitlab end def validation_service_timeout - timeout = ENV['EXTERNAL_VALIDATION_SERVICE_TIMEOUT'].to_i + timeout = Gitlab::CurrentSettings.external_pipeline_validation_service_timeout || ENV['EXTERNAL_VALIDATION_SERVICE_TIMEOUT'].to_i return timeout if timeout > 0 DEFAULT_VALIDATION_REQUEST_TIMEOUT end def validation_service_url - ENV['EXTERNAL_VALIDATION_SERVICE_URL'] + Gitlab::CurrentSettings.external_pipeline_validation_service_url || ENV['EXTERNAL_VALIDATION_SERVICE_URL'] end def validation_service_token - ENV['EXTERNAL_VALIDATION_SERVICE_TOKEN'] + Gitlab::CurrentSettings.external_pipeline_validation_service_token || ENV['EXTERNAL_VALIDATION_SERVICE_TOKEN'] end def validation_service_payload diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml index 654a03ced5f..bf42cd52605 100644 --- a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml @@ -12,7 +12,7 @@ stages: variables: FUZZAPI_PROFILE: Quick - FUZZAPI_VERSION: latest + FUZZAPI_VERSION: "1.6" FUZZAPI_CONFIG: .gitlab-api-fuzzing.yml FUZZAPI_TIMEOUT: 30 FUZZAPI_REPORT: gl-api-fuzzing-report.json @@ -45,7 +45,7 @@ apifuzzer_fuzz: entrypoint: ["/bin/bash", "-l", "-c"] variables: FUZZAPI_PROJECT: $CI_PROJECT_PATH - FUZZAPI_API: http://localhost:80 + FUZZAPI_API: http://localhost:5000 FUZZAPI_NEW_REPORT: 1 FUZZAPI_LOG_SCANNER: gl-apifuzzing-api-scanner.log TZ: America/Los_Angeles @@ -107,7 +107,7 @@ apifuzzer_fuzz_dnd: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" FUZZAPI_PROJECT: $CI_PROJECT_PATH - FUZZAPI_API: http://apifuzzer:80 + FUZZAPI_API: http://apifuzzer:5000 allow_failure: true rules: - if: $FUZZAPI_D_TARGET_IMAGE == null && $FUZZAPI_D_WORKER_IMAGE == null @@ -142,6 +142,7 @@ apifuzzer_fuzz_dnd: -e TZ=America/Los_Angeles \ -e GITLAB_FEATURES \ -p 80:80 \ + -p 5000:5000 \ -p 8000:8000 \ -p 514:514 \ --restart=no \ @@ -168,7 +169,7 @@ apifuzzer_fuzz_dnd: docker run \ --name worker \ --network $FUZZAPI_D_NETWORK \ - -e FUZZAPI_API=http://apifuzzer:80 \ + -e FUZZAPI_API=http://apifuzzer:5000 \ -e FUZZAPI_PROJECT \ -e FUZZAPI_PROFILE \ -e FUZZAPI_CONFIG \ @@ -211,7 +212,7 @@ apifuzzer_fuzz_dnd: --name worker \ --network $FUZZAPI_D_NETWORK \ -e TZ=America/Los_Angeles \ - -e FUZZAPI_API=http://apifuzzer:80 \ + -e FUZZAPI_API=http://apifuzzer:5000 \ -e FUZZAPI_PROJECT \ -e FUZZAPI_PROFILE \ -e FUZZAPI_CONFIG \ @@ -237,6 +238,7 @@ apifuzzer_fuzz_dnd: -v $CI_PROJECT_DIR:/app \ -v `pwd`/$FUZZAPI_REPORT_ASSET_PATH:/app/$FUZZAPI_REPORT_ASSET_PATH:rw \ -p 81:80 \ + -p 5001:5000 \ -p 8001:8000 \ -p 515:514 \ --restart=no \ diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml new file mode 100644 index 00000000000..215029dc952 --- /dev/null +++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml @@ -0,0 +1,270 @@ +# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/api_fuzzing/ + +# Configure the scanning tool through the environment variables. +# List of the variables: https://docs.gitlab.com/ee/user/application_security/api_fuzzing/#available-variables +# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables + +variables: + FUZZAPI_PROFILE: Quick + FUZZAPI_VERSION: latest + FUZZAPI_CONFIG: .gitlab-api-fuzzing.yml + FUZZAPI_TIMEOUT: 30 + FUZZAPI_REPORT: gl-api-fuzzing-report.json + FUZZAPI_REPORT_ASSET_PATH: assets + # + FUZZAPI_D_NETWORK: testing-net + # + # Wait up to 5 minutes for API Fuzzer and target url to become + # available (non 500 response to HTTP(s)) + FUZZAPI_SERVICE_START_TIMEOUT: "300" + # + FUZZAPI_IMAGE: registry.gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing:${FUZZAPI_VERSION}-engine + # + +apifuzzer_fuzz_unlicensed: + stage: fuzz + allow_failure: true + rules: + - if: '$GITLAB_FEATURES !~ /\bapi_fuzzing\b/ && $API_FUZZING_DISABLED == null' + - when: never + script: + - | + echo "Error: Your GitLab project is not licensed for API Fuzzing." + - exit 1 + +apifuzzer_fuzz: + stage: fuzz + image: + name: $FUZZAPI_IMAGE + entrypoint: ["/bin/bash", "-l", "-c"] + variables: + FUZZAPI_PROJECT: $CI_PROJECT_PATH + FUZZAPI_API: http://localhost:80 + FUZZAPI_NEW_REPORT: 1 + FUZZAPI_LOG_SCANNER: gl-apifuzzing-api-scanner.log + TZ: America/Los_Angeles + allow_failure: true + rules: + - if: $FUZZAPI_D_TARGET_IMAGE + when: never + - if: $FUZZAPI_D_WORKER_IMAGE + when: never + - if: $API_FUZZING_DISABLED + when: never + - if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH && + $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME + when: never + - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bapi_fuzzing\b/ + script: + # + # Validate options + - | + if [ "$FUZZAPI_HAR$FUZZAPI_OPENAPI$FUZZAPI_POSTMAN_COLLECTION" == "" ]; then \ + echo "Error: One of FUZZAPI_HAR, FUZZAPI_OPENAPI, or FUZZAPI_POSTMAN_COLLECTION must be provided."; \ + echo "See https://docs.gitlab.com/ee/user/application_security/api_fuzzing/ for information on how to configure API Fuzzing."; \ + exit 1; \ + fi + # + # Run user provided pre-script + - sh -c "$FUZZAPI_PRE_SCRIPT" + # + # Make sure asset path exists + - mkdir -p $FUZZAPI_REPORT_ASSET_PATH + # + # Start API Security background process + - dotnet /peach/Peach.Web.dll &> $FUZZAPI_LOG_SCANNER & + - APISEC_PID=$! + # + # Start scanning + - worker-entry + # + # Run user provided post-script + - sh -c "$FUZZAPI_POST_SCRIPT" + # + # Shutdown API Security + - kill $APISEC_PID + - wait $APISEC_PID + # + artifacts: + when: always + paths: + - $FUZZAPI_REPORT_ASSET_PATH + - $FUZZAPI_REPORT + - $FUZZAPI_LOG_SCANNER + reports: + api_fuzzing: $FUZZAPI_REPORT + +apifuzzer_fuzz_dnd: + stage: fuzz + image: docker:19.03.12 + variables: + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: "" + FUZZAPI_PROJECT: $CI_PROJECT_PATH + FUZZAPI_API: http://apifuzzer:80 + allow_failure: true + rules: + - if: $FUZZAPI_D_TARGET_IMAGE == null && $FUZZAPI_D_WORKER_IMAGE == null + when: never + - if: $API_FUZZING_DISABLED + when: never + - if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH && + $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME + when: never + - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bapi_fuzzing\b/ + services: + - docker:19.03.12-dind + script: + # + # + - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY + # + - docker network create --driver bridge $FUZZAPI_D_NETWORK + # + # Run user provided pre-script + - sh -c "$FUZZAPI_PRE_SCRIPT" + # + # Make sure asset path exists + - mkdir -p $FUZZAPI_REPORT_ASSET_PATH + # + # Start peach testing engine container + - | + docker run -d \ + --name apifuzzer \ + --network $FUZZAPI_D_NETWORK \ + -e Proxy:Port=8000 \ + -e TZ=America/Los_Angeles \ + -e GITLAB_FEATURES \ + -p 80:80 \ + -p 8000:8000 \ + -p 514:514 \ + --restart=no \ + $FUZZAPI_IMAGE \ + dotnet /peach/Peach.Web.dll + # + # Start target container + - | + if [ "$FUZZAPI_D_TARGET_IMAGE" != "" ]; then \ + docker run -d \ + --name target \ + --network $FUZZAPI_D_NETWORK \ + $FUZZAPI_D_TARGET_ENV \ + $FUZZAPI_D_TARGET_PORTS \ + $FUZZAPI_D_TARGET_VOLUME \ + --restart=no \ + $FUZZAPI_D_TARGET_IMAGE \ + ; fi + # + # Start worker container if provided + - | + if [ "$FUZZAPI_D_WORKER_IMAGE" != "" ]; then \ + echo "Starting worker image $FUZZAPI_D_WORKER_IMAGE"; \ + docker run \ + --name worker \ + --network $FUZZAPI_D_NETWORK \ + -e FUZZAPI_API=http://apifuzzer:80 \ + -e FUZZAPI_PROJECT \ + -e FUZZAPI_PROFILE \ + -e FUZZAPI_CONFIG \ + -e FUZZAPI_REPORT \ + -e FUZZAPI_REPORT_ASSET_PATH \ + -e FUZZAPI_NEW_REPORT=1 \ + -e FUZZAPI_HAR \ + -e FUZZAPI_OPENAPI \ + -e FUZZAPI_POSTMAN_COLLECTION \ + -e FUZZAPI_POSTMAN_COLLECTION_VARIABLES \ + -e FUZZAPI_TARGET_URL \ + -e FUZZAPI_OVERRIDES_FILE \ + -e FUZZAPI_OVERRIDES_ENV \ + -e FUZZAPI_OVERRIDES_CMD \ + -e FUZZAPI_OVERRIDES_INTERVAL \ + -e FUZZAPI_TIMEOUT \ + -e FUZZAPI_VERBOSE \ + -e FUZZAPI_SERVICE_START_TIMEOUT \ + -e FUZZAPI_HTTP_USERNAME \ + -e FUZZAPI_HTTP_PASSWORD \ + -e CI_PROJECT_URL \ + -e CI_JOB_ID \ + -e CI_COMMIT_BRANCH=${CI_COMMIT_BRANCH} \ + $FUZZAPI_D_WORKER_ENV \ + $FUZZAPI_D_WORKER_PORTS \ + $FUZZAPI_D_WORKER_VOLUME \ + --restart=no \ + $FUZZAPI_D_WORKER_IMAGE \ + ; fi + # + # Start API Fuzzing provided worker if no other worker present + - | + if [ "$FUZZAPI_D_WORKER_IMAGE" == "" ]; then \ + if [ "$FUZZAPI_HAR$FUZZAPI_OPENAPI$FUZZAPI_POSTMAN_COLLECTION" == "" ]; then \ + echo "Error: One of FUZZAPI_HAR, FUZZAPI_OPENAPI, or FUZZAPI_POSTMAN_COLLECTION must be provided."; \ + echo "See https://docs.gitlab.com/ee/user/application_security/api_fuzzing/ for information on how to configure API Fuzzing."; \ + exit 1; \ + fi; \ + docker run \ + --name worker \ + --network $FUZZAPI_D_NETWORK \ + -e TZ=America/Los_Angeles \ + -e FUZZAPI_API=http://apifuzzer:80 \ + -e FUZZAPI_PROJECT \ + -e FUZZAPI_PROFILE \ + -e FUZZAPI_CONFIG \ + -e FUZZAPI_REPORT \ + -e FUZZAPI_REPORT_ASSET_PATH \ + -e FUZZAPI_NEW_REPORT=1 \ + -e FUZZAPI_HAR \ + -e FUZZAPI_OPENAPI \ + -e FUZZAPI_POSTMAN_COLLECTION \ + -e FUZZAPI_POSTMAN_COLLECTION_VARIABLES \ + -e FUZZAPI_TARGET_URL \ + -e FUZZAPI_OVERRIDES_FILE \ + -e FUZZAPI_OVERRIDES_ENV \ + -e FUZZAPI_OVERRIDES_CMD \ + -e FUZZAPI_OVERRIDES_INTERVAL \ + -e FUZZAPI_TIMEOUT \ + -e FUZZAPI_VERBOSE \ + -e FUZZAPI_SERVICE_START_TIMEOUT \ + -e FUZZAPI_HTTP_USERNAME \ + -e FUZZAPI_HTTP_PASSWORD \ + -e CI_PROJECT_URL \ + -e CI_JOB_ID \ + -v $CI_PROJECT_DIR:/app \ + -v `pwd`/$FUZZAPI_REPORT_ASSET_PATH:/app/$FUZZAPI_REPORT_ASSET_PATH:rw \ + -p 81:80 \ + -p 8001:8000 \ + -p 515:514 \ + --restart=no \ + $FUZZAPI_IMAGE \ + worker-entry \ + ; fi + # + # Propagate exit code from api fuzzing scanner (if any) + - if [[ $(docker inspect apifuzzer --format='{{.State.ExitCode}}') != "0" ]]; then echo "API Fuzzing scanner exited with an error. Logs are available as job artifacts."; exit 1; fi + # + # Run user provided post-script + - sh -c "$FUZZAPI_POST_SCRIPT" + # + after_script: + # + # Shutdown all containers + - echo "Stopping all containers" + - if [ "$FUZZAPI_D_TARGET_IMAGE" != "" ]; then docker stop target; fi + - docker stop worker + - docker stop apifuzzer + # + # Save docker logs + - docker logs apifuzzer &> gl-api_fuzzing-logs.log + - if [ "$FUZZAPI_D_TARGET_IMAGE" != "" ]; then docker logs target &> gl-api_fuzzing-target-logs.log; fi + - docker logs worker &> gl-api_fuzzing-worker-logs.log + # + artifacts: + when: always + paths: + - ./gl-api_fuzzing*.log + - ./gl-api_fuzzing*.zip + - $FUZZAPI_REPORT_ASSET_PATH + - $FUZZAPI_REPORT + reports: + api_fuzzing: $FUZZAPI_REPORT + +# end diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml index 90dc80a3fc0..a1f0d2e5a48 100644 --- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml @@ -160,7 +160,7 @@ mobsf-android-sast: services: # this version must match with analyzer version mentioned in: https://gitlab.com/gitlab-org/security-products/analyzers/mobsf/-/blob/master/Dockerfile # Unfortunately, we need to keep track of mobsf version in 2 different places for now. - - name: opensecurity/mobile-security-framework-mobsf:v3.3.3 + - name: opensecurity/mobile-security-framework-mobsf:v3.4.0 alias: mobsf image: name: "$SAST_ANALYZER_IMAGE" @@ -186,7 +186,7 @@ mobsf-ios-sast: services: # this version must match with analyzer version mentioned in: https://gitlab.com/gitlab-org/security-products/analyzers/mobsf/-/blob/master/Dockerfile # Unfortunately, we need to keep track of mobsf version in 2 different places for now. - - name: opensecurity/mobile-security-framework-mobsf:v3.3.3 + - name: opensecurity/mobile-security-framework-mobsf:v3.4.0 alias: mobsf image: name: "$SAST_ANALYZER_IMAGE" diff --git a/locale/gitlab.pot b/locale/gitlab.pot index de282b4ca5f..f611b8a6a86 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -24167,6 +24167,9 @@ msgstr "" msgid "Profiles|Expired key is not valid." msgstr "" +msgid "Profiles|Expired:" +msgstr "" + msgid "Profiles|Expires at" msgstr "" @@ -27178,9 +27181,6 @@ msgstr "" msgid "Runner API" msgstr "" -msgid "Runner token" -msgstr "" - msgid "Runner tokens" msgstr "" @@ -27268,6 +27268,9 @@ msgstr "" msgid "Runners|Revision" msgstr "" +msgid "Runners|Runner" +msgstr "" + msgid "Runners|Runner #%{runner_id}" msgstr "" diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb index 98c12a4086e..3cb466abce9 100644 --- a/qa/qa/page/project/pipeline/index.rb +++ b/qa/qa/page/project/pipeline/index.rb @@ -9,8 +9,11 @@ module QA element :pipeline_url_link end - view 'app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table_row.vue' do + view 'app/assets/javascripts/pipelines/components/pipelines_list/pipelines_status_badge.vue' do element :pipeline_commit_status + end + + view 'app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue' do element :pipeline_retry_button end diff --git a/spec/frontend/jobs/components/job_sidebar_details_container_spec.js b/spec/frontend/jobs/components/job_sidebar_details_container_spec.js index 2b56bd2d558..ad0368555fa 100644 --- a/spec/frontend/jobs/components/job_sidebar_details_container_spec.js +++ b/spec/frontend/jobs/components/job_sidebar_details_container_spec.js @@ -34,11 +34,22 @@ describe('Job Sidebar Details Container', () => { }); describe('when no details are available', () => { - it('should render an empty container', () => { + beforeEach(() => { createWrapper(); + }); + it('should render an empty container', () => { expect(wrapper.html()).toBe(''); }); + + it.each(['duration', 'erased_at', 'finished_at', 'queued', 'runner', 'coverage'])( + 'should not render %s details when missing', + async (detail) => { + await store.dispatch('receiveJobSuccess', { [detail]: undefined }); + + expect(findAllDetailsRow()).toHaveLength(0); + }, + ); }); describe('when some of the details are available', () => { @@ -49,7 +60,7 @@ describe('Job Sidebar Details Container', () => { ['erased_at', 'Erased: 3 weeks ago'], ['finished_at', 'Finished: 3 weeks ago'], ['queued', 'Queued: 9 seconds'], - ['runner', 'Runner: local ci runner (#1)'], + ['runner', 'Runner: #1 (ABCDEFGH) local ci runner'], ['coverage', 'Coverage: 20%'], ])('uses %s to render job-%s', async (detail, value) => { await store.dispatch('receiveJobSuccess', { [detail]: job[detail] }); diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js index 3db3584c8fd..c3115dd78e2 100644 --- a/spec/frontend/jobs/mock_data.js +++ b/spec/frontend/jobs/mock_data.js @@ -958,6 +958,7 @@ export default { artifacts: [null], runner: { id: 1, + short_sha: 'ABCDEFGH', description: 'local ci runner', edit_path: '/root/ci-mock/runners/1/edit', }, diff --git a/spec/frontend/pipelines/pipelines_table_row_spec.js b/spec/frontend/pipelines/pipelines_table_row_spec.js deleted file mode 100644 index 68d46575081..00000000000 --- a/spec/frontend/pipelines/pipelines_table_row_spec.js +++ /dev/null @@ -1,239 +0,0 @@ -import { mount } from '@vue/test-utils'; -import waitForPromises from 'helpers/wait_for_promises'; -import PipelinesTableRowComponent from '~/pipelines/components/pipelines_list/pipelines_table_row.vue'; -import eventHub from '~/pipelines/event_hub'; - -describe('Pipelines Table Row', () => { - const jsonFixtureName = 'pipelines/pipelines.json'; - - const createWrapper = (pipeline) => - mount(PipelinesTableRowComponent, { - propsData: { - pipeline, - viewType: 'root', - }, - }); - - let wrapper; - let pipeline; - let pipelineWithoutAuthor; - let pipelineWithoutCommit; - - beforeEach(() => { - const { pipelines } = getJSONFixture(jsonFixtureName); - - pipeline = pipelines.find((p) => p.user !== null && p.commit !== null); - pipelineWithoutAuthor = pipelines.find((p) => p.user === null && p.commit !== null); - pipelineWithoutCommit = pipelines.find((p) => p.user === null && p.commit === null); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('should render a table row', () => { - wrapper = createWrapper(pipeline); - - expect(wrapper.attributes('class')).toContain('gl-responsive-table-row'); - }); - - describe('status column', () => { - beforeEach(() => { - wrapper = createWrapper(pipeline); - }); - - it('should render a pipeline link', () => { - expect(wrapper.find('.table-section.commit-link a').attributes('href')).toEqual( - pipeline.path, - ); - }); - - it('should render status text', () => { - expect(wrapper.find('.table-section.commit-link a').text()).toContain( - pipeline.details.status.text, - ); - }); - }); - - describe('information column', () => { - beforeEach(() => { - wrapper = createWrapper(pipeline); - }); - - it('should render a pipeline link', () => { - expect(wrapper.find('.table-section:nth-child(2) a').attributes('href')).toEqual( - pipeline.path, - ); - }); - - it('should render pipeline ID', () => { - expect(wrapper.find('.table-section:nth-child(2) a > span').text()).toEqual( - `#${pipeline.id}`, - ); - }); - - describe('when a user is provided', () => { - it('should render user information', () => { - expect( - wrapper.find('.table-section:nth-child(3) .js-pipeline-url-user').attributes('href'), - ).toEqual(pipeline.user.path); - - expect( - wrapper.find('.table-section:nth-child(3) .js-user-avatar-image-tooltip').text().trim(), - ).toEqual(pipeline.user.name); - }); - }); - }); - - describe('commit column', () => { - it('should render link to commit', () => { - wrapper = createWrapper(pipeline); - - const commitLink = wrapper.find('.branch-commit .commit-sha'); - - expect(commitLink.attributes('href')).toEqual(pipeline.commit.commit_path); - }); - - const findElements = () => { - const commitTitleElement = wrapper.find('.branch-commit .commit-title'); - const commitAuthorElement = commitTitleElement.find('a.avatar-image-container'); - - if (!commitAuthorElement.exists()) { - return { - commitAuthorElement, - }; - } - - const commitAuthorLink = commitAuthorElement.attributes('href'); - const commitAuthorName = commitAuthorElement - .find('.js-user-avatar-image-tooltip') - .text() - .trim(); - - return { - commitAuthorElement, - commitAuthorLink, - commitAuthorName, - }; - }; - - it('renders nothing without commit', () => { - expect(pipelineWithoutCommit.commit).toBe(null); - - wrapper = createWrapper(pipelineWithoutCommit); - const { commitAuthorElement } = findElements(); - - expect(commitAuthorElement.exists()).toBe(false); - }); - - it('renders commit author', () => { - wrapper = createWrapper(pipeline); - const { commitAuthorLink, commitAuthorName } = findElements(); - - expect(commitAuthorLink).toEqual(pipeline.commit.author.path); - expect(commitAuthorName).toEqual(pipeline.commit.author.username); - }); - - it('renders commit with unregistered author', () => { - expect(pipelineWithoutAuthor.commit.author).toBe(null); - - wrapper = createWrapper(pipelineWithoutAuthor); - const { commitAuthorLink, commitAuthorName } = findElements(); - - expect(commitAuthorLink).toEqual(`mailto:${pipelineWithoutAuthor.commit.author_email}`); - expect(commitAuthorName).toEqual(pipelineWithoutAuthor.commit.author_name); - }); - }); - - describe('stages column', () => { - const findAllMiniPipelineStages = () => - wrapper.findAll('.table-section:nth-child(5) [data-testid="mini-pipeline-graph-dropdown"]'); - - it('should render an icon for each stage', () => { - wrapper = createWrapper(pipeline); - - expect(findAllMiniPipelineStages()).toHaveLength(pipeline.details.stages.length); - }); - - it('should not render stages when stages are empty', () => { - const withoutStages = { ...pipeline }; - withoutStages.details = { ...withoutStages.details, stages: null }; - - wrapper = createWrapper(withoutStages); - - expect(findAllMiniPipelineStages()).toHaveLength(0); - }); - }); - - describe('actions column', () => { - const scheduledJobAction = { - name: 'some scheduled job', - }; - - beforeEach(() => { - const withActions = { ...pipeline }; - withActions.details.scheduled_actions = [scheduledJobAction]; - withActions.flags.cancelable = true; - withActions.flags.retryable = true; - withActions.cancel_path = '/cancel'; - withActions.retry_path = '/retry'; - - wrapper = createWrapper(withActions); - }); - - it('should render the provided actions', () => { - expect(wrapper.find('.js-pipelines-retry-button').exists()).toBe(true); - expect(wrapper.find('.js-pipelines-retry-button').attributes('title')).toMatch('Retry'); - expect(wrapper.find('.js-pipelines-cancel-button').exists()).toBe(true); - expect(wrapper.find('.js-pipelines-cancel-button').attributes('title')).toMatch('Cancel'); - }); - - it('should render the manual actions', async () => { - const manualActions = wrapper.find('[data-testid="pipelines-manual-actions-dropdown"]'); - - // Click on the dropdown and wait for `lazy` dropdown items - manualActions.find('.dropdown-toggle').trigger('click'); - await waitForPromises(); - - expect(manualActions.text()).toContain(scheduledJobAction.name); - }); - - it('emits `retryPipeline` event when retry button is clicked and toggles loading', () => { - eventHub.$on('retryPipeline', (endpoint) => { - expect(endpoint).toBe('/retry'); - }); - - wrapper.find('.js-pipelines-retry-button').trigger('click'); - expect(wrapper.vm.isRetrying).toBe(true); - }); - - it('emits `openConfirmationModal` event when cancel button is clicked and toggles loading', () => { - eventHub.$once('openConfirmationModal', (data) => { - const { id, ref, commit } = pipeline; - - expect(data.endpoint).toBe('/cancel'); - expect(data.pipeline).toEqual( - expect.objectContaining({ - id, - ref, - commit, - }), - ); - }); - - wrapper.find('.js-pipelines-cancel-button').trigger('click'); - }); - - it('renders a loading icon when `cancelingPipeline` matches pipeline id', (done) => { - wrapper.setProps({ cancelingPipeline: pipeline.id }); - wrapper.vm - .$nextTick() - .then(() => { - expect(wrapper.vm.isCancelling).toBe(true); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js index 952bea81052..70e47b98575 100644 --- a/spec/frontend/pipelines/pipelines_table_spec.js +++ b/spec/frontend/pipelines/pipelines_table_spec.js @@ -30,23 +30,17 @@ describe('Pipelines Table', () => { return pipelines.find((p) => p.user !== null && p.commit !== null); }; - const createComponent = (props = {}, flagState = false) => { + const createComponent = (props = {}) => { wrapper = extendedWrapper( mount(PipelinesTable, { propsData: { ...defaultProps, ...props, }, - provide: { - glFeatures: { - newPipelinesTable: flagState, - }, - }, }), ); }; - const findRows = () => wrapper.findAll('.commit.gl-responsive-table-row'); const findGlTable = () => wrapper.findComponent(GlTable); const findStatusBadge = () => wrapper.findComponent(PipelinesStatusBadge); const findPipelineInfo = () => wrapper.findComponent(PipelineUrl); @@ -56,8 +50,7 @@ describe('Pipelines Table', () => { const findTimeAgo = () => wrapper.findComponent(PipelinesTimeago); const findActions = () => wrapper.findComponent(PipelineOperations); - const findLegacyTable = () => wrapper.findByTestId('legacy-ci-table'); - const findTableRows = () => wrapper.findAll('[data-testid="pipeline-table-row"]'); + const findTableRows = () => wrapper.findAllByTestId('pipeline-table-row'); const findStatusTh = () => wrapper.findByTestId('status-th'); const findPipelineTh = () => wrapper.findByTestId('pipeline-th'); const findTriggererTh = () => wrapper.findByTestId('triggerer-th'); @@ -75,52 +68,13 @@ describe('Pipelines Table', () => { wrapper = null; }); - describe('table with feature flag off', () => { - describe('renders the table correctly', () => { - beforeEach(() => { - createComponent(); - }); - - it('should render a table', () => { - expect(wrapper.classes()).toContain('ci-table'); - }); - - it('should render table head with correct columns', () => { - expect(wrapper.find('.table-section.js-pipeline-status').text()).toEqual('Status'); - - expect(wrapper.find('.table-section.js-pipeline-info').text()).toEqual('Pipeline'); - - expect(wrapper.find('.table-section.js-pipeline-commit').text()).toEqual('Commit'); - - expect(wrapper.find('.table-section.js-pipeline-stages').text()).toEqual('Stages'); - }); - }); - - describe('without data', () => { - it('should render an empty table', () => { - createComponent(); - - expect(findRows()).toHaveLength(0); - }); - }); - - describe('with data', () => { - it('should render rows', () => { - createComponent({ pipelines: [pipeline], viewType: 'root' }); - - expect(findRows()).toHaveLength(1); - }); - }); - }); - - describe('table with feature flag on', () => { + describe('Pipelines Table', () => { beforeEach(() => { - createComponent({ pipelines: [pipeline], viewType: 'root' }, true); + createComponent({ pipelines: [pipeline], viewType: 'root' }); }); - it('displays new table', () => { + it('displays table', () => { expect(findGlTable().exists()).toBe(true); - expect(findLegacyTable().exists()).toBe(false); }); it('should render table head with correct columns', () => { diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb index 980efed3c04..2bf4ee1a9fd 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb @@ -60,6 +60,30 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('correlation-id') end + context 'with configuration values in ApplicationSetting' do + let(:alternate_validation_service_url) { 'https://alternate-validation-service.external/' } + let(:validation_service_token) { 'SECURE_TOKEN' } + let(:shorter_timeout) { described_class::DEFAULT_VALIDATION_REQUEST_TIMEOUT - 1 } + + before do + stub_env('EXTERNAL_VALIDATION_SERVICE_TOKEN', 'TOKEN_IN_ENV') + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:external_pipeline_validation_service_timeout).and_return(shorter_timeout) + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:external_pipeline_validation_service_token).and_return(validation_service_token) + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:external_pipeline_validation_service_url).and_return(alternate_validation_service_url) + end + + it 'uses those values rather than env vars or defaults' do + expect(::Gitlab::HTTP).to receive(:post) do |url, params| + expect(url).to eq(alternate_validation_service_url) + expect(params[:timeout]).to eq(shorter_timeout) + expect(params[:headers]).to include('X-Gitlab-Token' => validation_service_token) + expect(params[:timeout]).to eq(shorter_timeout) + end + + perform! + end + end + it 'respects the defined payload schema' do expect(::Gitlab::HTTP).to receive(:post) do |_url, params| expect(params[:body]).to match_schema('/external_validation') diff --git a/spec/serializers/runner_entity_spec.rb b/spec/serializers/runner_entity_spec.rb index e864b52c0f2..39cac65c5ac 100644 --- a/spec/serializers/runner_entity_spec.rb +++ b/spec/serializers/runner_entity_spec.rb @@ -20,6 +20,7 @@ RSpec.describe RunnerEntity do it 'contains required fields' do expect(subject).to include(:id, :description) expect(subject).to include(:edit_path) + expect(subject).to include(:short_sha) end end end diff --git a/spec/services/design_management/copy_design_collection/copy_service_spec.rb b/spec/services/design_management/copy_design_collection/copy_service_spec.rb index ddbed91815f..03242487b53 100644 --- a/spec/services/design_management/copy_design_collection/copy_service_spec.rb +++ b/spec/services/design_management/copy_design_collection/copy_service_spec.rb @@ -7,6 +7,7 @@ RSpec.describe DesignManagement::CopyDesignCollection::CopyService, :clean_gitla let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:issue, refind: true) { create(:issue, project: project) } + let(:target_issue) { create(:issue) } subject { described_class.new(project, user, issue: issue, target_issue: target_issue).execute } diff --git a/spec/services/design_management/delete_designs_service_spec.rb b/spec/services/design_management/delete_designs_service_spec.rb index ed161b4c8ff..341f71fa62c 100644 --- a/spec/services/design_management/delete_designs_service_spec.rb +++ b/spec/services/design_management/delete_designs_service_spec.rb @@ -7,6 +7,7 @@ RSpec.describe DesignManagement::DeleteDesignsService do let_it_be(:project) { create(:project) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:user) { create(:user) } + let(:designs) { create_designs } subject(:service) { described_class.new(project, user, issue: issue, designs: designs) } diff --git a/spec/services/design_management/save_designs_service_spec.rb b/spec/services/design_management/save_designs_service_spec.rb index 5348c0908b2..5bc763cc95e 100644 --- a/spec/services/design_management/save_designs_service_spec.rb +++ b/spec/services/design_management/save_designs_service_spec.rb @@ -7,6 +7,7 @@ RSpec.describe DesignManagement::SaveDesignsService do let_it_be_with_reload(:issue) { create(:issue) } let_it_be(:developer) { create(:user, developer_projects: [issue.project]) } + let(:project) { issue.project } let(:user) { developer } let(:files) { [rails_sample] } diff --git a/spec/views/profiles/keys/_key.html.haml_spec.rb b/spec/views/profiles/keys/_key.html.haml_spec.rb index a29b8ecc3d5..bb101198ac3 100644 --- a/spec/views/profiles/keys/_key.html.haml_spec.rb +++ b/spec/views/profiles/keys/_key.html.haml_spec.rb @@ -58,6 +58,20 @@ RSpec.describe 'profiles/keys/_key.html.haml' do end end + context 'when the key has expired' do + let_it_be(:key) do + create(:personal_key, + user: user, + expires_at: 2.days.ago) + end + + it 'renders "Expired:" as the expiration date label' do + render + + expect(rendered).to have_text('Expired:') + end + end + context 'when the key is not deletable' do # Turns out key.can_delete? is only false for LDAP keys # but LDAP keys don't exist outside EE |