diff options
39 files changed, 419 insertions, 48 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index 91b802766f8..392de1c9f23 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -301,7 +301,7 @@ export default { class="view-file js-view-file-button" :title="viewFileButtonText" > - <icon name="external-link" /> + <icon name="doc-text" /> </gl-button> <a 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..45708d78886 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; @@ -112,7 +117,7 @@ export default class MergeRequestStore { this.ciStatus = data.ci_status; this.isPipelineFailed = this.ciStatus === 'failed' || this.ciStatus === 'canceled'; this.isPipelinePassing = - this.ciStatus === 'success' || this.ciStatus === 'success_with_warnings'; + this.ciStatus === 'success' || this.ciStatus === 'success-with-warnings'; this.isPipelineSkipped = this.ciStatus === 'skipped'; this.pipelineDetailedStatus = pipelineStatus; this.isPipelineActive = data.pipeline ? data.pipeline.active : false; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index be0073559b6..dffd5e70edb 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -200,12 +200,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 { @@ -213,6 +213,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/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 3ab61cc5c47..1be5ef276fd 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -20,8 +20,8 @@ } .ci-status-icon-pending, -.ci-status-icon-failed_with_warnings, -.ci-status-icon-success_with_warnings { +.ci-status-icon-failed-with-warnings, +.ci-status-icon-success-with-warnings { svg { fill: $orange-500; } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index fbd291f095a..f8620eec46d 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -107,6 +107,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 7084e8715e0..e3d0d0b0fa2 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 { @@ -444,3 +453,13 @@ } } } + +// Don't hide the overflow in system messages +.system-note-message, +.issuable-detail { + .scoped-label-wrapper { + .badge { + overflow: initial; + } + } +} diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 3e6aa43175e..093fc89a56f 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -785,7 +785,7 @@ } &.ci-status-icon-pending, - &.ci-status-icon-success_with_warnings { + &.ci-status-icon-success-with-warnings { @include mini-pipeline-graph-color($white, $orange-100, $orange-200, $orange-500, $orange-600, $orange-700); } diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index a59bb31bdcb..613f643af3a 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -38,8 +38,8 @@ } &.ci-pending, - &.ci-failed_with_warnings, - &.ci-success_with_warnings { + &.ci-failed-with-warnings, + &.ci-success-with-warnings { @include status-color($orange-100, $orange-500, $orange-700); } diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 355b91a8661..3122d8b5163 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -16,7 +16,7 @@ module CiStatusHelper label = case status when 'success' 'passed' - when 'success_with_warnings' + when 'success-with-warnings' 'passed with warnings' when 'manual' 'waiting for manual action' @@ -37,7 +37,7 @@ module CiStatusHelper case status when 'success' s_('CiStatusText|passed') - when 'success_with_warnings' + when 'success-with-warnings' s_('CiStatusText|passed') when 'manual' s_('CiStatusText|blocked') @@ -71,7 +71,7 @@ module CiStatusHelper case status when 'success' 'status_success' - when 'success_with_warnings' + when 'success-with-warnings' 'status_warning' when 'failed' 'status_failed' 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/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb index d8bba58dcbf..c5e5f4f6400 100644 --- a/app/models/project_services/mock_ci_service.rb +++ b/app/models/project_services/mock_ci_service.rb @@ -2,7 +2,7 @@ # For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service class MockCiService < CiService - ALLOWED_STATES = %w[failed canceled running pending success success_with_warnings skipped not_found].freeze + ALLOWED_STATES = %w[failed canceled running pending success success-with-warnings skipped not_found].freeze prop_accessor :mock_service_url validates :mock_service_url, presence: true, public_url: true, if: :activated? diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index e4a62deda7a..3f7b5bebb74 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -13,7 +13,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated def ci_status if pipeline status = pipeline.status - status = "success_with_warnings" if pipeline.success? && pipeline.has_warnings? + status = "success-with-warnings" if pipeline.success? && pipeline.has_warnings? status || "preparing" else @@ -212,6 +212,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/changelogs/unreleased/59034-external-link-button.yml b/changelogs/unreleased/59034-external-link-button.yml new file mode 100644 index 00000000000..3d8e9e82836 --- /dev/null +++ b/changelogs/unreleased/59034-external-link-button.yml @@ -0,0 +1,5 @@ +--- +title: Replaced icon for external URL with doc-text icon +merge_request: 27365 +author: +type: fixed diff --git a/changelogs/unreleased/nfriend-update-merge-request-widget-for-post-merge-pipelines.yml b/changelogs/unreleased/nfriend-update-merge-request-widget-for-post-merge-pipelines.yml new file mode 100644 index 00000000000..420c8f2923c --- /dev/null +++ b/changelogs/unreleased/nfriend-update-merge-request-widget-for-post-merge-pipelines.yml @@ -0,0 +1,5 @@ +--- +title: Add two new warning messages to the MR widget about merge request pipelines +merge_request: 25983 +author: +type: added diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md index 15276d364a0..8d81b32e721 100644 --- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md +++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md @@ -145,7 +145,7 @@ Securing LDAP (enabling LDAPS) on Windows Server 2012 involves installing a vali > By default a LDAP service listens for connections on TCP and UDP port 389. LDAPS (LDAP over SSL) listens on port 636 -### Testing you AD server +### Testing your AD server #### Using **AdFind** (Windows) diff --git a/doc/api/discussions.md b/doc/api/discussions.md index 3f575f432d0..67bbd4cc1ac 100644 --- a/doc/api/discussions.md +++ b/doc/api/discussions.md @@ -2,6 +2,8 @@ Discussions are set of related notes on snippets, issues, merge requests or commits. +This includes system notes, which are notes about changes to the object (for example, when a milestone changes, there will be a corresponding system note). Label notes are not part of this API, but recorded as separate events in [resource label events](resource_label_events.md). + ## Discussions pagination By default, `GET` requests return 20 results at a time because the API results diff --git a/doc/api/notes.md b/doc/api/notes.md index c351ee13d9a..dfde80c6441 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -2,6 +2,8 @@ Notes are comments on snippets, issues or merge requests. +This includes system notes, which are notes about changes to the object (for example, when a milestone changes, there will be a corresponding system note). Label notes are not part of this API, but recorded as separate events in [resource label events](resource_label_events.md). + ## Notes pagination By default, `GET` requests return 20 results at a time because the API results diff --git a/doc/ci/introduction/img/gitlab_workflow_example_extended_11_11.png b/doc/ci/introduction/img/gitlab_workflow_example_extended_11_11.png Binary files differnew file mode 100644 index 00000000000..5089a1088c5 --- /dev/null +++ b/doc/ci/introduction/img/gitlab_workflow_example_extended_11_11.png diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md index 6055d8c282a..14ea648c00b 100644 --- a/doc/ci/introduction/index.md +++ b/doc/ci/introduction/index.md @@ -133,8 +133,8 @@ At the end, if anything goes wrong, you can easily ### Basic CI/CD workflow -This is a very simple example for how GitLab CI/CD fits in a common -development workflow. +Consider the following example for how GitLab CI/CD fits in a +common development workflow. Assume that you have discussed a code implementation in an issue and worked locally on your proposed changes. Once you push your @@ -154,7 +154,7 @@ Once you're happy with your implementation: - GitLab CI/CD deploys your changes automatically to a production environment. - And finally, you and your team can easily roll it back if something goes wrong. -<img src="img/gitlab_workflow_example_11_9.png" alt="GitLab workflow example" class="image-noshadow"> +![GitLab workflow example](img/gitlab_workflow_example_11_9.png) GitLab CI/CD is capable of a doing a lot more, but this workflow exemplifies GitLab's ability to track the entire process, @@ -162,6 +162,48 @@ without the need of any external tool to deliver your software. And, most usefully, you can visualize all the steps through the GitLab UI. +#### A deeper look into the CI/CD basic workflow + +If we take a deeper look into the basic workflow, we can see +the features available in GitLab at each stage of the DevOps +lifecycle, as shown on the illustration below. + +![Deeper look into the basic CI/CD workflow](img/gitlab_workflow_example_extended_11_11.png) + +If you look at the image from the left to the right, +you'll see some of the features available in GitLab +according to each stage (Verify, Package, Release). + +1. **Verify**: + - Automatically build and test your application with Continuous Integration. + - Analyze your source code quality with [GitLab Code Quality](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html). **[STARTER]** + - Determine the performance impact of code changes with [Browser Performance Testing](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html). **[PREMIUM]** + - Perform a series of tests, such as [Container Scanning](https://docs.gitlab.com/ee/ci/examples/container_scanning.html) **[ULTIMATE]**, [Dependency Scanning](https://docs.gitlab.com/ee/ci/examples/dependency_scanning.html) **[ULTIMATE]**, and [JUnit tests](../junit_test_reports.md). + - Deploy your changes with [Review Apps](../review_apps/index.md) to preview the app changes on every branch. +1. **Package**: + - Store Docker images with [Container Registry](../../user/project/container_registry.md). + - Store NPM packages with [NPM Registry](https://docs.gitlab.com/ee/user/project/packages/npm_registry.html). **[PREMIUM]** + - Store Maven artifacts with [Maven Repository](https://docs.gitlab.com/ee/user/project/packages/maven_repository.html). **[PREMIUM]** +1. **Release**: + - Continuous Deployment, automatically deploying your app to production. + - Continuous Delivery, manually click to deploy your app to production. + - Deploy static websites with [GitLab Pages](../../user/project/pages/index.md). + - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html). **[PREMIUM]** + - Deploy your features behind [Feature Flags](https://docs.gitlab.com/ee/user/project/operations/feature_flags.html). **[PREMIUM]** + - Add release notes to any Git tag with [GitLab Releases](../../user/project/releases/index.md). + - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html). **[PREMIUM]** + - Deploy your application to a production environment in a Kubernetes cluster with [Auto Deploy](../../topics/autodevops/index.md#auto-deploy). + +With GitLab CI/CD you can also: + +- Easily set up your app's entire lifecycle with [Auto DevOps](../../topics/autodevops/index.md). +- Deploy your app to different [environments](../environments.md). +- Install your own [GitLab Runner](https://docs.gitlab.com/runner/). +- [Schedule pipelines](../../user/project/pipelines/schedules.md). +- Check for app vulnerabilities with [Security Test reports](https://docs.gitlab.com/ee/user/project/merge_requests/#security-reports-ultimate). **[ULTIMATE]** + +To see all CI/CD features, navigate back to the [CI/CD index](../README.md). + ### Setting up GitLab CI/CD for the first time To get started with GitLab CI/CD, you need to familiarize yourself @@ -178,16 +220,3 @@ existing one) for any application. For a deep view of GitLab's CI/CD configuration options, check the [`.gitlab-ci.yml` full reference](../yaml/README.md). - -### GitLab CI/CD feature set - -- Easily set up your app's entire lifecycle with [Auto DevOps](../../topics/autodevops/index.md). -- Deploy static websites with [GitLab Pages](../../user/project/pages/index.md). -- Deploy your app to different [environments](../environments.md). -- Preview changes per merge request with [Review Apps](../review_apps/index.md). -- Develop secure and private Docker images with [Container Registry](../../user/project/container_registry.md). -- Install your own [GitLab Runner](https://docs.gitlab.com/runner/). -- [Schedule pipelines](../../user/project/pipelines/schedules.md). -- Check for app vulnerabilities with [Security Test reports](https://docs.gitlab.com/ee/user/project/merge_requests/#security-reports-ultimate). **[ULTIMATE]** - -To see all CI/CD features, navigate back to the [CI/CD index](../README.md). diff --git a/doc/development/fe_guide/frontend_faq.md b/doc/development/fe_guide/frontend_faq.md new file mode 100644 index 00000000000..77f064a21a9 --- /dev/null +++ b/doc/development/fe_guide/frontend_faq.md @@ -0,0 +1,27 @@ +# Frontend FAQ + +## Rules of Frontend FAQ + +1. **You talk about Frontend FAQ.** + Please share links to it whenever applicable, so more eyes catch when content + gets outdated. +2. **Keep it short and simple.** + Whenever an answer needs more than two sentences it does not belong here. +3. **Provide background when possible.** + Linking to relevant source code, issue / epic, or other documentation helps + to understand the answer. +4. **If you see something, do something.** + Please remove or update any content that is outdated as soon as you see it. + +## FAQ + +### How do I find the Rails route for a page? + +The easiest way is to type the following in the browser while on the page in +question: + +```javascript +document.body.dataset.page +``` + +Find here the [source code setting the attribute](https://gitlab.com/gitlab-org/gitlab-ce/blob/cc5095edfce2b4d4083a4fb1cdc7c0a1898b9921/app/views/layouts/application.html.haml#L4). diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md index d5cc28dc00c..36d5e4ab96b 100644 --- a/doc/development/fe_guide/index.md +++ b/doc/development/fe_guide/index.md @@ -73,6 +73,10 @@ How we use UI components. How we use Snowplow to track custom events. +## Frontend FAQ + +Read the [frontend's FAQ](frontend_faq.md) for common small pieces of helpful information. + --- ## Style Guides diff --git a/doc/user/project/integrations/mock_ci.md b/doc/user/project/integrations/mock_ci.md index 8b1908c46fe..1c64b275d6e 100644 --- a/doc/user/project/integrations/mock_ci.md +++ b/doc/user/project/integrations/mock_ci.md @@ -5,7 +5,7 @@ To set up the mock CI service server, respond to the following endpoints - `commit_status`: `#{project.namespace.path}/#{project.path}/status/#{sha}.json` - - Have your service return `200 { status: ['failed'|'canceled'|'running'|'pending'|'success'|'success_with_warnings'|'skipped'|'not_found'] }` + - Have your service return `200 { status: ['failed'|'canceled'|'running'|'pending'|'success'|'success-with-warnings'|'skipped'|'not_found'] }` - If the service returns a 404, it is interpreted as `pending` - `build_page`: `#{project.namespace.path}/#{project.path}/status/#{sha}` - Just where the build is linked to, doesn't matter if implemented diff --git a/lib/gitlab/ci/status/build/failed_allowed.rb b/lib/gitlab/ci/status/build/failed_allowed.rb index d7570fdd3e2..8972498a069 100644 --- a/lib/gitlab/ci/status/build/failed_allowed.rb +++ b/lib/gitlab/ci/status/build/failed_allowed.rb @@ -14,7 +14,7 @@ module Gitlab end def group - 'failed_with_warnings' + 'failed-with-warnings' end def status_tooltip diff --git a/lib/gitlab/ci/status/success_warning.rb b/lib/gitlab/ci/status/success_warning.rb index 6632cd9b143..47623ad945f 100644 --- a/lib/gitlab/ci/status/success_warning.rb +++ b/lib/gitlab/ci/status/success_warning.rb @@ -21,7 +21,7 @@ module Gitlab end def group - 'success_with_warnings' + 'success-with-warnings' end def self.matches?(subject, user) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 435f2408a9e..e6e161a2b27 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10947,6 +10947,9 @@ msgstr "" msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally." msgstr "" +msgid "mrWidget|Fork merge requests do not create merge request pipelines which validate a post merge result" +msgstr "" + msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the" msgstr "" @@ -11040,6 +11043,9 @@ msgstr "" msgid "mrWidget|The source branch will not be deleted" msgstr "" +msgid "mrWidget|The target branch has advanced, which invalidates the merge request pipeline. Please update the source branch and retry merging" +msgstr "" + msgid "mrWidget|There are merge conflicts" msgstr "" diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json index 6b1cd60c25d..7018cb9a305 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json @@ -125,6 +125,7 @@ "test_reports_path": { "type": ["string", "null"] }, "can_receive_suggestion": { "type": "boolean" }, "source_branch_protected": { "type": "boolean" }, - "conflicts_docs_path": { "type": ["string", "null"] } + "conflicts_docs_path": { "type": ["string", "null"] }, + "merge_request_pipelines_docs_path": { "type": ["string", "null"] } } } diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js index 0398f184c0a..1a7f338c5fa 100644 --- a/spec/javascripts/jobs/mock_data.js +++ b/spec/javascripts/jobs/mock_data.js @@ -678,7 +678,7 @@ export const stages = [ icon: 'status_warning', text: 'failed', label: 'failed (allowed to fail)', - group: 'failed_with_warnings', + group: 'failed-with-warnings', tooltip: 'failed - (unknown failure) (allowed to fail)', has_details: true, details_path: '/gitlab-org/gitlab-shell/-/jobs/454', @@ -710,7 +710,7 @@ export const stages = [ icon: 'status_warning', text: 'failed', label: 'failed (allowed to fail)', - group: 'failed_with_warnings', + group: 'failed-with-warnings', tooltip: 'failed - (unknown failure) (allowed to fail)', has_details: true, details_path: '/gitlab-org/gitlab-shell/-/jobs/454', @@ -738,7 +738,7 @@ export const stages = [ icon: 'status_warning', text: 'passed', label: 'passed with warnings', - group: 'success_with_warnings', + group: 'success-with-warnings', tooltip: 'passed', has_details: true, details_path: '/gitlab-org/gitlab-shell/pipelines/27#test', diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js new file mode 100644 index 00000000000..8ec17efffb9 --- /dev/null +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js @@ -0,0 +1,77 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import MrWidgetAlertMessage from '~/vue_merge_request_widget/components/mr_widget_alert_message.vue'; +import { GlLink } from '@gitlab/ui'; + +describe('MrWidgetAlertMessage', () => { + let wrapper; + + beforeEach(() => { + const localVue = createLocalVue(); + + wrapper = shallowMount(localVue.extend(MrWidgetAlertMessage), { + propsData: {}, + localVue, + sync: false, + }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when type is not provided', () => { + it('should render a red message', done => { + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('danger_message'); + expect(wrapper.classes()).not.toContain('warning_message'); + done(); + }); + }); + }); + + describe('when type === "danger"', () => { + it('should render a red message', done => { + wrapper.setProps({ type: 'danger' }); + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('danger_message'); + expect(wrapper.classes()).not.toContain('warning_message'); + done(); + }); + }); + }); + + describe('when type === "warning"', () => { + it('should render a red message', done => { + wrapper.setProps({ type: 'warning' }); + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('warning_message'); + expect(wrapper.classes()).not.toContain('danger_message'); + done(); + }); + }); + }); + + describe('when helpPath is not provided', () => { + it('should not render a help icon/link', done => { + wrapper.vm.$nextTick(() => { + const link = wrapper.find(GlLink); + + expect(link.exists()).toBe(false); + done(); + }); + }); + }); + + describe('when helpPath is provided', () => { + it('should render a help icon/link', done => { + wrapper.setProps({ helpPath: '/path/to/help/docs' }); + wrapper.vm.$nextTick(() => { + const link = wrapper.find(GlLink); + + expect(link.exists()).toBe(true); + expect(link.attributes().href).toBe('/path/to/help/docs'); + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js index 7ab203a6011..dda16375103 100644 --- a/spec/javascripts/vue_mr_widget/mock_data.js +++ b/spec/javascripts/vue_mr_widget/mock_data.js @@ -233,6 +233,7 @@ export default { merge_commit_path: 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775', troubleshooting_docs_path: 'help', + merge_request_pipelines_docs_path: '/help/ci/merge_request_pipelines/index.md', squash: true, }; diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index 3e8f73646c8..690fcd3e224 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -183,6 +183,85 @@ describe('mrWidgetOptions', () => { }); }); }); + + describe('showMergePipelineForkWarning', () => { + describe('when the source project and target project are the same', () => { + beforeEach(done => { + Vue.set(vm.mr, 'mergePipelinesEnabled', true); + Vue.set(vm.mr, 'sourceProjectId', 1); + Vue.set(vm.mr, 'targetProjectId', 1); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showMergePipelineForkWarning).toEqual(false); + }); + }); + + describe('when merge pipelines are not enabled', () => { + beforeEach(done => { + Vue.set(vm.mr, 'mergePipelinesEnabled', false); + Vue.set(vm.mr, 'sourceProjectId', 1); + Vue.set(vm.mr, 'targetProjectId', 2); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showMergePipelineForkWarning).toEqual(false); + }); + }); + + describe('when merge pipelines are enabled _and_ the source project and target project are different', () => { + beforeEach(done => { + Vue.set(vm.mr, 'mergePipelinesEnabled', true); + Vue.set(vm.mr, 'sourceProjectId', 1); + Vue.set(vm.mr, 'targetProjectId', 2); + vm.$nextTick(done); + }); + + it('should be true', () => { + expect(vm.showMergePipelineForkWarning).toEqual(true); + }); + }); + }); + + describe('showTargetBranchAdvancedError', () => { + describe(`when the pipeline's target_sha property doesn't exist`, () => { + beforeEach(done => { + Vue.set(vm.mr.pipeline, 'target_sha', undefined); + Vue.set(vm.mr, 'targetBranchSha', 'abcd'); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showTargetBranchAdvancedError).toEqual(false); + }); + }); + + describe(`when the pipeline's target_sha matches the target branch's sha`, () => { + beforeEach(done => { + Vue.set(vm.mr.pipeline, 'target_sha', 'abcd'); + Vue.set(vm.mr, 'targetBranchSha', 'abcd'); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showTargetBranchAdvancedError).toEqual(false); + }); + }); + + describe(`when the pipeline's target_sha does not match the target branch's sha`, () => { + beforeEach(done => { + Vue.set(vm.mr.pipeline, 'target_sha', 'abcd'); + Vue.set(vm.mr, 'targetBranchSha', 'bcde'); + vm.$nextTick(done); + }); + + it('should be true', () => { + expect(vm.showTargetBranchAdvancedError).toEqual(true); + }); + }); + }); }); describe('methods', () => { diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js index c226704694c..e2cd0f084fd 100644 --- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js +++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js @@ -36,8 +36,8 @@ describe('MergeRequestStore', () => { expect(store.isPipelinePassing).toBe(true); }); - it('is true when the CI status is `success_with_warnings`', () => { - store.setData({ ...mockData, ci_status: 'success_with_warnings' }); + it('is true when the CI status is `success-with-warnings`', () => { + store.setData({ ...mockData, ci_status: 'success-with-warnings' }); expect(store.isPipelinePassing).toBe(true); }); diff --git a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js index 4b0b7ba66e5..42481f8c334 100644 --- a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js +++ b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js @@ -59,7 +59,7 @@ describe('CI Badge Link Component', () => { success_warining: { text: 'passed', label: 'passed', - group: 'success_with_warnings', + group: 'success-with-warnings', icon: 'status_warning', details_path: 'status/warning', }, diff --git a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb index af03d5a1308..2a5915d75d0 100644 --- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb @@ -31,7 +31,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do describe '#group' do it 'returns status failed with warnings status group' do - expect(subject.group).to eq 'failed_with_warnings' + expect(subject.group).to eq 'failed-with-warnings' end end diff --git a/spec/lib/gitlab/ci/status/success_warning_spec.rb b/spec/lib/gitlab/ci/status/success_warning_spec.rb index 6d05545d1d8..9493b1d89f2 100644 --- a/spec/lib/gitlab/ci/status/success_warning_spec.rb +++ b/spec/lib/gitlab/ci/status/success_warning_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::Ci::Status::SuccessWarning do end describe '#group' do - it { expect(subject.group).to eq 'success_with_warnings' } + it { expect(subject.group).to eq 'success-with-warnings' } end describe '.matches?' do diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb index 4a0f91c4c7a..e5f08aeb1fa 100644 --- a/spec/presenters/merge_request_presenter_spec.rb +++ b/spec/presenters/merge_request_presenter_spec.rb @@ -40,8 +40,8 @@ describe MergeRequestPresenter do allow(pipeline).to receive(:has_warnings?) { true } end - it 'returns "success_with_warnings"' do - is_expected.to eq('success_with_warnings') + it 'returns "success-with-warnings"' do + is_expected.to eq('success-with-warnings') end end |