summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_alert_message.vue46
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/constants.js5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue38
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js7
-rw-r--r--app/assets/stylesheets/framework/common.scss16
-rw-r--r--app/assets/stylesheets/framework/icons.scss4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss4
-rw-r--r--app/assets/stylesheets/pages/labels.scss19
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss2
-rw-r--r--app/assets/stylesheets/pages/status.scss4
-rw-r--r--app/helpers/ci_status_helper.rb6
-rw-r--r--app/helpers/labels_helper.rb2
-rw-r--r--app/models/project_services/mock_ci_service.rb2
-rw-r--r--app/presenters/merge_request_presenter.rb6
-rw-r--r--app/serializers/merge_request_widget_entity.rb4
-rw-r--r--changelogs/unreleased/59034-external-link-button.yml5
-rw-r--r--changelogs/unreleased/nfriend-update-merge-request-widget-for-post-merge-pipelines.yml5
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md2
-rw-r--r--doc/api/discussions.md2
-rw-r--r--doc/api/notes.md2
-rw-r--r--doc/ci/introduction/img/gitlab_workflow_example_extended_11_11.pngbin0 -> 139184 bytes
-rw-r--r--doc/ci/introduction/index.md61
-rw-r--r--doc/development/fe_guide/frontend_faq.md27
-rw-r--r--doc/development/fe_guide/index.md4
-rw-r--r--doc/user/project/integrations/mock_ci.md2
-rw-r--r--lib/gitlab/ci/status/build/failed_allowed.rb2
-rw-r--r--lib/gitlab/ci/status/success_warning.rb2
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json3
-rw-r--r--spec/javascripts/jobs/mock_data.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js77
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js1
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js79
-rw-r--r--spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/ci_badge_link_spec.js2
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/success_warning_spec.rb2
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb4
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
new file mode 100644
index 00000000000..5089a1088c5
--- /dev/null
+++ b/doc/ci/introduction/img/gitlab_workflow_example_extended_11_11.png
Binary files differ
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