diff options
Diffstat (limited to 'app')
18 files changed, 176 insertions, 34 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index 32e5fa5bf8b..91b802766f8 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -112,9 +112,7 @@ export default { const truncatedContentSha = _.escape(truncateSha(this.diffFile.content_sha)); return sprintf( s__('MergeRequests|View file @ %{commitId}'), - { - commitId: `<span class="commit-sha">${truncatedContentSha}</span>`, - }, + { commitId: truncatedContentSha }, false, ); }, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_alert_message.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_alert_message.vue new file mode 100644 index 00000000000..040315b3c66 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_alert_message.vue @@ -0,0 +1,46 @@ +<script> +import { GlLink } from '@gitlab/ui'; +import Icon from '~/vue_shared/components/icon.vue'; +import { WARNING, DANGER, WARNING_MESSAGE_CLASS, DANGER_MESSAGE_CLASS } from '../constants'; + +export default { + name: 'MrWidgetAlertMessage', + components: { + GlLink, + Icon, + }, + props: { + type: { + type: String, + required: false, + default: DANGER, + validator: value => [WARNING, DANGER].includes(value), + }, + helpPath: { + type: String, + required: false, + default: undefined, + }, + }, + computed: { + messageClass() { + if (this.type === WARNING) { + return WARNING_MESSAGE_CLASS; + } else if (this.type === DANGER) { + return DANGER_MESSAGE_CLASS; + } + + return ''; + }, + }, +}; +</script> + +<template> + <div class="m-3 ml-5" :class="messageClass"> + <slot></slot> + <gl-link v-if="helpPath" :href="helpPath" target="_blank"> + <icon :size="16" name="question-o" class="align-middle" /> + </gl-link> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/constants.js b/app/assets/javascripts/vue_merge_request_widget/constants.js new file mode 100644 index 00000000000..0a29d55fbd6 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/constants.js @@ -0,0 +1,5 @@ +export const WARNING = 'warning'; +export const DANGER = 'danger'; + +export const WARNING_MESSAGE_CLASS = 'warning_message'; +export const DANGER_MESSAGE_CLASS = 'danger_message'; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 57c4dfbe3b7..aa4ecb0aac3 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -12,6 +12,7 @@ import WidgetMergeHelp from './components/mr_widget_merge_help.vue'; import MrWidgetPipelineContainer from './components/mr_widget_pipeline_container.vue'; import Deployment from './components/deployment.vue'; import WidgetRelatedLinks from './components/mr_widget_related_links.vue'; +import MrWidgetAlertMessage from './components/mr_widget_alert_message.vue'; import MergedState from './components/states/mr_widget_merged.vue'; import ClosedState from './components/states/mr_widget_closed.vue'; import MergingState from './components/states/mr_widget_merging.vue'; @@ -46,6 +47,7 @@ export default { MrWidgetPipelineContainer, Deployment, 'mr-widget-related-links': WidgetRelatedLinks, + MrWidgetAlertMessage, 'mr-widget-merged': MergedState, 'mr-widget-closed': ClosedState, 'mr-widget-merging': MergingState, @@ -110,6 +112,18 @@ export default { shouldRenderMergedPipeline() { return this.mr.state === 'merged' && !_.isEmpty(this.mr.mergePipeline); }, + showMergePipelineForkWarning() { + return Boolean( + this.mr.mergePipelinesEnabled && this.mr.sourceProjectId !== this.mr.targetProjectId, + ); + }, + showTargetBranchAdvancedError() { + return Boolean( + this.mr.pipeline && + this.mr.pipeline.target_sha && + this.mr.pipeline.target_sha !== this.mr.targetBranchSha, + ); + }, }, watch: { state(newVal, oldVal) { @@ -328,6 +342,30 @@ export default { :related-links="mr.relatedLinks" /> + <mr-widget-alert-message + v-if="showMergePipelineForkWarning" + type="warning" + :help-path="mr.mergeRequestPipelinesHelpPath" + > + {{ + s__( + 'mrWidget|Fork merge requests do not create merge request pipelines which validate a post merge result', + ) + }} + </mr-widget-alert-message> + + <mr-widget-alert-message + v-if="showTargetBranchAdvancedError" + type="danger" + :help-path="mr.mergeRequestPipelinesHelpPath" + > + {{ + s__( + 'mrWidget|The target branch has advanced, which invalidates the merge request pipeline. Please update the source branch and retry merging', + ) + }} + </mr-widget-alert-message> + <source-branch-removal-status v-if="shouldRenderSourceBranchRemovalStatus" /> </div> <div v-if="shouldRenderMergeHelp" class="mr-widget-footer"><mr-widget-merge-help /></div> diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 58363f632a9..5fa6ec9328c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -28,9 +28,11 @@ export default class MergeRequestStore { this.iid = data.iid; this.title = data.title; this.targetBranch = data.target_branch; + this.targetBranchSha = data.target_branch_sha; this.sourceBranch = data.source_branch; this.sourceBranchProtected = data.source_branch_protected; this.conflictsDocsPath = data.conflicts_docs_path; + this.mergeRequestPipelinesHelpPath = data.merge_request_pipelines_docs_path; this.mergeStatus = data.merge_status; this.commitMessage = data.default_merge_commit_message; this.shortMergeCommitSha = data.short_merge_commit_sha; @@ -99,6 +101,9 @@ export default class MergeRequestStore { this.allowCollaboration = data.allow_collaboration; this.targetProjectFullPath = data.target_project_full_path; this.sourceProjectFullPath = data.source_project_full_path; + this.sourceProjectId = data.source_project_id; + this.targetProjectId = data.target_project_id; + this.mergePipelinesEnabled = data.merge_pipelines_enabled; // Cherry-pick and Revert actions related this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index db6c107210e..71f471df437 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -205,12 +205,12 @@ li.note { } } -.warning_message { - border-left: 4px solid $orange-200; - color: $orange-700; +@mixin message($background-color, $border-color, $text-color) { + border-left: 4px solid $border-color; + color: $text-color; padding: 10px; margin-bottom: 10px; - background: $orange-100; + background: $background-color; padding-left: 20px; &.centered { @@ -218,6 +218,14 @@ li.note { } } +.warning_message { + @include message($orange-100, $orange-200, $orange-700); +} + +.danger_message { + @include message($red-100, $red-200, $red-900); +} + .gitlab-promo { a { color: $gl-gray-350; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9be3f8138a0..8a11667d248 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -112,6 +112,10 @@ } .scoped-label-wrapper { + > a { + max-width: 100%; + } + .color-label { padding-right: $gl-padding-24; } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index e7fd7fab32b..d17d20bb683 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -408,12 +408,21 @@ } .scoped-label-wrapper { + max-width: 100%; + vertical-align: top; + + .badge { + text-overflow: ellipsis; + overflow-x: hidden; + } + &.label-link .color-label a { color: inherit; } .color-label { padding-right: $gl-padding-24; + max-width: 100%; } .scoped-label { @@ -438,3 +447,13 @@ } } } + +// Don't hide the overflow in system messages +.system-note-message, +.issuable-detail { + .scoped-label-wrapper { + .badge { + overflow: initial; + } + } +} diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 39ba2a651d4..8f177895b08 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -98,20 +98,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def test_reports - result = @merge_request.compare_test_reports - - case result[:status] - when :parsing - Gitlab::PollingInterval.set_header(response, interval: 3000) - - render json: '', status: :no_content - when :parsed - render json: result[:data].to_json, status: :ok - when :error - render json: { status_reason: result[:status_reason] }, status: :bad_request - else - render json: { status_reason: 'Unknown error' }, status: :internal_server_error - end + reports_response(@merge_request.compare_test_reports) end def edit @@ -353,4 +340,19 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # Also see https://gitlab.com/gitlab-org/gitlab-ce/issues/42441 Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42438') end + + def reports_response(report_comparison) + case report_comparison[:status] + when :parsing + ::Gitlab::PollingInterval.set_header(response, interval: 3000) + + render json: '', status: :no_content + when :parsed + render json: report_comparison[:data].to_json, status: :ok + when :error + render json: { status_reason: report_comparison[:status_reason] }, status: :bad_request + else + render json: { status_reason: 'Unknown error' }, status: :internal_server_error + end + end end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index e91e8f85515..a07c3f90a91 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -89,7 +89,7 @@ module LabelsHelper def render_colored_label(label, label_suffix: '', tooltip: true, title: nil) text_color = text_color_for_bg(label.color) - title ||= tooltip ? label_tooltip_title(label) : '' + title ||= tooltip ? label_tooltip_title(label) : label.name # Intentionally not using content_tag here so that this method can be called # by LabelReferenceFilter diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index b8a76e662b0..5cf9bb4979a 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -104,8 +104,8 @@ module Ci where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace) end - scope :with_test_reports, ->() do - with_existing_job_artifacts(Ci::JobArtifact.test_reports) + scope :with_reports, ->(reports_scope) do + with_existing_job_artifacts(reports_scope) .eager_load_job_artifacts end diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 99512a7c1dd..9695d49d18b 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -21,7 +21,8 @@ module Ci container_scanning: 'gl-container-scanning-report.json', dast: 'gl-dast-report.json', license_management: 'gl-license-management-report.json', - performance: 'performance.json' + performance: 'performance.json', + metrics: 'metrics.txt' }.freeze TYPE_AND_FORMAT_PAIRS = { @@ -29,6 +30,7 @@ module Ci metadata: :gzip, trace: :raw, junit: :gzip, + metrics: :gzip, # All these file formats use `raw` as we need to store them uncompressed # for Frontend to fetch the files and do analysis @@ -88,7 +90,8 @@ module Ci dast: 8, ## EE-specific codequality: 9, ## EE-specific license_management: 10, ## EE-specific - performance: 11 ## EE-specific + performance: 11, ## EE-specific + metrics: 12 ## EE-specific } enum file_format: { diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 01d96754518..fd65b2c733a 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -210,6 +210,10 @@ module Ci where(source: branch_pipeline_sources).where(ref: ref, tag: false) end + scope :with_reports, -> (reports_scope) do + where('EXISTS (?)', ::Ci::Build.latest.with_reports(reports_scope).where('ci_pipelines.id=ci_builds.commit_id').select(1)) + end + # Returns the pipelines in descending order (= newest first), optionally # limited to a number of references. # @@ -689,13 +693,13 @@ module Ci @latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a end - def has_test_reports? - complete? && builds.latest.with_test_reports.any? + def has_reports?(reports_scope) + complete? && builds.latest.with_reports(reports_scope).exists? end def test_reports Gitlab::Ci::Reports::TestReports.new.tap do |test_reports| - builds.latest.with_test_reports.each do |build| + builds.latest.with_reports(Ci::JobArtifact.test_reports).each do |build| build.collect_test_reports!(test_reports) end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 458c57c1dc6..d91dbe4bf49 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1195,7 +1195,7 @@ class MergeRequest < ApplicationRecord end def has_test_reports? - actual_head_pipeline&.has_test_reports? + actual_head_pipeline&.has_reports?(Ci::JobArtifact.test_reports) end def predefined_variables diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index 284b1ad9b55..55103c0a95c 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -209,6 +209,10 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated help_page_path('user/project/merge_requests/resolve_conflicts.md') end + def merge_request_pipelines_docs_path + help_page_path('ci/merge_request_pipelines/index.md') + end + private def cached_can_be_reverted? diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index 4831eb32c96..b130f447cce 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -256,6 +256,10 @@ class MergeRequestWidgetEntity < IssuableEntity presenter(merge_request).conflicts_docs_path end + expose :merge_request_pipelines_docs_path do |merge_request| + presenter(merge_request).merge_request_pipelines_docs_path + end + private delegate :current_user, to: :request diff --git a/app/services/ci/prepare_build_service.rb b/app/services/ci/prepare_build_service.rb index 32f11438b79..3722faeb020 100644 --- a/app/services/ci/prepare_build_service.rb +++ b/app/services/ci/prepare_build_service.rb @@ -11,9 +11,11 @@ module Ci def execute prerequisites.each(&:complete!) - unless build.enqueue - build.drop!(:unmet_prerequisites) - end + build.enqueue! + rescue => e + Gitlab::Sentry.track_acceptable_exception(e, extra: { build_id: build.id }) + + build.drop(:unmet_prerequisites) end private diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 8112c2a4299..e042c0a6434 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -16,7 +16,7 @@ module MergeRequests params.delete(:force_remove_source_branch) end - if params[:force_remove_source_branch].present? + if params.has_key?(:force_remove_source_branch) merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) end |