Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-25 00:09:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-25 00:09:08 +0300
commit7671216b60e2796a050358ff808b4a0c2de3d22f (patch)
tree605dfc1339a3cd7dc7353ac6d725191086a9acca /app
parentc2367afbf57ebc65d5b78a743b5d6a91f0aece9f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/blob/pipeline_tour_success_modal.vue78
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js22
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue68
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue143
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js1
-rw-r--r--app/assets/stylesheets/framework/images.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/projects/blob_controller.rb1
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/helpers/blob_helper.rb12
-rw-r--r--app/serializers/merge_request_widget_entity.rb3
-rw-r--r--app/uploaders/import_export_uploader.rb4
-rw-r--r--app/views/ci/variables/_index.html.haml2
-rw-r--r--app/views/projects/blob/_pipeline_tour_success.html.haml1
-rw-r--r--app/views/projects/blob/show.html.haml2
-rw-r--r--app/views/projects/merge_requests/_widget.html.haml1
18 files changed, 326 insertions, 24 deletions
diff --git a/app/assets/javascripts/blob/pipeline_tour_success_modal.vue b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue
new file mode 100644
index 00000000000..0739b4d5e39
--- /dev/null
+++ b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue
@@ -0,0 +1,78 @@
+<script>
+import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
+import { sprintf, s__, __ } from '~/locale';
+import Cookies from 'js-cookie';
+import { glEmojiTag } from '~/emoji';
+
+export default {
+ beginnerLink:
+ 'https://about.gitlab.com/blog/2018/01/22/a-beginners-guide-to-continuous-integration/',
+ exampleLink: 'https://docs.gitlab.com/ee/ci/examples/',
+ bodyMessage: s__(
+ 'MR widget|The pipeline will now run automatically every time you commit code. Pipelines are useful for deploying static web pages, detecting vulnerabilities in dependencies, static or dynamic application security testing (SAST and DAST), and so much more!',
+ ),
+ modalTitle: sprintf(
+ __("That's it, well done!%{celebrate}"),
+ {
+ celebrate: glEmojiTag('tada'),
+ },
+ false,
+ ),
+ components: {
+ GlModal,
+ GlSprintf,
+ GlLink,
+ },
+ props: {
+ goToPipelinesPath: {
+ type: String,
+ required: true,
+ },
+ commitCookie: {
+ type: String,
+ required: true,
+ },
+ },
+ mounted() {
+ this.disableModalFromRenderingAgain();
+ },
+ methods: {
+ disableModalFromRenderingAgain() {
+ Cookies.remove(this.commitCookie);
+ },
+ },
+};
+</script>
+<template>
+ <gl-modal
+ visible
+ size="sm"
+ :title="$options.modalTitle"
+ modal-id="success-pipeline-modal-id-not-used"
+ >
+ <p>
+ {{ $options.bodyMessage }}
+ </p>
+ <gl-sprintf
+ :message="
+ s__(`MR widget|Take a look at our %{beginnerLinkStart}Beginner's Guide to Continuous Integration%{beginnerLinkEnd}
+ and our %{exampleLinkStart}examples of GitLab CI/CD%{exampleLinkEnd}
+ to see all the cool stuff you can do with it.`)
+ "
+ >
+ <template #beginnerLink="{content}">
+ <gl-link :href="$options.beginnerLink" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ <template #exampleLink="{content}">
+ <gl-link :href="$options.exampleLink" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ <template #modal-footer>
+ <a :href="goToPipelinesPath" class="btn btn-success">{{ __('Go to Pipelines') }}</a>
+ </template>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index caf9a8c0b64..4d308d6b07a 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -4,6 +4,7 @@ import BlobViewer from '~/blob/viewer/index';
import initBlob from '~/pages/projects/init_blob';
import GpgBadges from '~/gpg_badges';
import '~/sourcegraph/load';
+import PipelineTourSuccessModal from '~/blob/pipeline_tour_success_modal.vue';
document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
@@ -35,4 +36,25 @@ document.addEventListener('DOMContentLoaded', () => {
// eslint-disable-next-line promise/catch-or-return
import('~/code_navigation').then(m => m.default());
}
+
+ if (gon.features?.suggestPipeline) {
+ const successPipelineEl = document.querySelector('.js-success-pipeline-modal');
+
+ if (successPipelineEl) {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: successPipelineEl,
+ render(createElement) {
+ const { commitCookie, pipelinesPath: goToPipelinesPath } = this.$el.dataset;
+
+ return createElement(PipelineTourSuccessModal, {
+ props: {
+ goToPipelinesPath,
+ commitCookie,
+ },
+ });
+ },
+ });
+ }
+ }
});
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
index f08bfb3a90f..9942861d9e4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
@@ -1,45 +1,75 @@
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import MrWidgetIcon from './mr_widget_icon.vue';
+import PipelineTourState from './states/mr_widget_pipeline_tour.vue';
export default {
name: 'MRWidgetSuggestPipeline',
iconName: 'status_notfound',
+ popoverTarget: 'suggest-popover',
+ popoverContainer: 'suggest-pipeline',
+ trackLabel: 'no_pipeline_noticed',
+ linkTrackValue: 30,
+ linkTrackEvent: 'click_link',
components: {
GlLink,
GlSprintf,
MrWidgetIcon,
+ PipelineTourState,
},
props: {
pipelinePath: {
type: String,
required: true,
},
+ pipelineSvgPath: {
+ type: String,
+ required: true,
+ },
+ humanAccess: {
+ type: String,
+ required: true,
+ },
},
};
</script>
<template>
- <div class="d-flex mr-pipeline-suggest append-bottom-default">
+ <div :id="$options.popoverContainer" class="d-flex mr-pipeline-suggest append-bottom-default">
<mr-widget-icon :name="$options.iconName" />
- <gl-sprintf
- class="js-no-pipeline-message"
- :message="
- s__(`mrWidget|%{prefixToLinkStart}No pipeline%{prefixToLinkEnd}
+ <div :id="$options.popoverTarget">
+ <gl-sprintf
+ :message="
+ s__(`mrWidget|%{prefixToLinkStart}No pipeline%{prefixToLinkEnd}
%{addPipelineLinkStart}Add the .gitlab-ci.yml file%{addPipelineLinkEnd}
to create one.`)
- "
- >
- <template #prefixToLink="{content}">
- <strong>
- {{ content }}
- </strong>
- </template>
- <template #addPipelineLink="{content}">
- <gl-link :href="pipelinePath" class="ml-2">
- {{ content }}
- </gl-link>
- &nbsp;
- </template>
- </gl-sprintf>
+ "
+ >
+ <template #prefixToLink="{content}">
+ <strong>
+ {{ content }}
+ </strong>
+ </template>
+ <template #addPipelineLink="{content}">
+ <gl-link
+ :href="pipelinePath"
+ class="ml-2 js-add-pipeline-path"
+ :data-track-property="humanAccess"
+ :data-track-value="$options.linkTrackValue"
+ :data-track-event="$options.linkTrackEvent"
+ :data-track-label="$options.trackLabel"
+ >
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ <pipeline-tour-state
+ :pipeline-path="pipelinePath"
+ :pipeline-svg-path="pipelineSvgPath"
+ :human-access="humanAccess"
+ :popover-target="$options.popoverTarget"
+ :popover-container="$options.popoverContainer"
+ :track-label="$options.trackLabel"
+ />
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue
new file mode 100644
index 00000000000..f2d7e86a85e
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue
@@ -0,0 +1,143 @@
+<script>
+import { s__, sprintf } from '~/locale';
+import { GlPopover, GlButton } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+import Cookies from 'js-cookie';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import Tracking from '~/tracking';
+
+const trackingMixin = Tracking.mixin();
+
+const cookieKey = 'suggest_pipeline_dismissed';
+
+export default {
+ name: 'MRWidgetPipelineTour',
+ dismissTrackValue: 20,
+ showTrackValue: 10,
+ trackEvent: 'click_button',
+ popoverContent: sprintf(
+ '%{messageText1}%{lineBreak}%{messageText2}%{lineBreak}%{messageText3}%{lineBreak}%{messageText4}%{lineBreak}%{messageText5}',
+ {
+ messageText1: s__('mrWidget|Detect issues before deployment with a CI pipeline'),
+ messageText2: s__('mrWidget|that continuously tests your code. We created'),
+ messageText3: s__("mrWidget|a quick guide that'll show you how to create"),
+ messageText4: s__('mrWidget|one. Make your code more secure and more'),
+ messageText5: s__('mrWidget|robust in just a minute.'),
+ lineBreak: '<br/>',
+ },
+ false,
+ ),
+ components: {
+ GlPopover,
+ GlButton,
+ Icon,
+ },
+ mixins: [trackingMixin],
+ props: {
+ pipelinePath: {
+ type: String,
+ required: true,
+ },
+ pipelineSvgPath: {
+ type: String,
+ required: true,
+ },
+ humanAccess: {
+ type: String,
+ required: true,
+ },
+ popoverTarget: {
+ type: String,
+ required: true,
+ },
+ popoverContainer: {
+ type: String,
+ required: true,
+ },
+ trackLabel: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ popoverDismissed: parseBoolean(Cookies.get(cookieKey)),
+ tracking: {
+ label: this.trackLabel,
+ property: this.humanAccess,
+ },
+ };
+ },
+ mounted() {
+ this.trackOnShow();
+ },
+ methods: {
+ trackOnShow() {
+ if (!this.popoverDismissed) {
+ this.track();
+ }
+ },
+ dismissPopover() {
+ this.popoverDismissed = true;
+ Cookies.set(cookieKey, this.popoverDismissed, { expires: 365 });
+ },
+ },
+};
+</script>
+<template>
+ <gl-popover
+ v-if="!popoverDismissed"
+ show
+ :target="popoverTarget"
+ :container="popoverContainer"
+ placement="rightbottom"
+ >
+ <template #title>
+ <button
+ class="btn-blank float-right mt-1"
+ type="button"
+ :aria-label="__('Close')"
+ :data-track-property="humanAccess"
+ :data-track-value="$options.dismissTrackValue"
+ :data-track-event="$options.trackEvent"
+ :data-track-label="trackLabel"
+ @click="dismissPopover"
+ >
+ <icon name="close" aria-hidden="true" />
+ </button>
+ {{ s__('mrWidget|Are you adding technical debt or code vulnerabilities?') }}
+ </template>
+ <div class="svg-content svg-150 pt-1">
+ <img :src="pipelineSvgPath" />
+ </div>
+ <p v-html="$options.popoverContent"></p>
+ <gl-button
+ ref="ok"
+ category="primary"
+ class="mt-2 mb-0"
+ variant="info"
+ block
+ :href="pipelinePath"
+ :data-track-property="humanAccess"
+ :data-track-value="$options.showTrackValue"
+ :data-track-event="$options.trackEvent"
+ :data-track-label="trackLabel"
+ >
+ {{ __('Show me how') }}
+ </gl-button>
+ <gl-button
+ ref="no-thanks"
+ category="secondary"
+ class="mt-2 mb-0"
+ variant="info"
+ block
+ :data-track-property="humanAccess"
+ :data-track-value="$options.dismissTrackValue"
+ :data-track-event="$options.trackEvent"
+ :data-track-label="trackLabel"
+ @click="dismissPopover"
+ >
+ {{ __("No thanks, don't show this again") }}
+ </gl-button>
+ </gl-popover>
+</template>
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 27f13ace779..c8d69143f8d 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
@@ -362,6 +362,8 @@ export default {
v-if="shouldSuggestPipelines"
class="mr-widget-workflow"
:pipeline-path="mr.mergeRequestAddCiConfigPath"
+ :pipeline-svg-path="mr.pipelinesEmptySvgPath"
+ :human-access="mr.humanAccess.toLowerCase()"
/>
<mr-widget-pipeline-container
v-if="shouldRenderPipelines"
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 73a0b3cb673..ea83c61e275 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
@@ -176,6 +176,7 @@ export default class MergeRequestStore {
this.eligibleApproversDocsPath = data.eligible_approvers_docs_path;
this.mergeImmediatelyDocsPath = data.merge_immediately_docs_path;
this.mergeRequestAddCiConfigPath = data.merge_request_add_ci_config_path;
+ this.pipelinesEmptySvgPath = data.pipelines_empty_svg_path;
this.humanAccess = data.human_access;
}
diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss
index d78c707192f..2c9397d363c 100644
--- a/app/assets/stylesheets/framework/images.scss
+++ b/app/assets/stylesheets/framework/images.scss
@@ -20,7 +20,7 @@
width: 100%;
}
- $image-widths: 80 130 250 306 394 430;
+ $image-widths: 80 130 150 250 306 394 430;
@each $width in $image-widths {
&.svg-#{$width} {
img,
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 5ca75c28ac3..ad8b251d3e4 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -614,6 +614,10 @@ $mr-widget-min-height: 69px;
.circle-icon-container {
color: $gl-text-color-quaternary;
}
+
+ .popover {
+ z-index: 240;
+ }
}
.card-new-merge-request {
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 3555528b2ef..3c1f020702f 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -7,7 +7,7 @@ module Groups
before_action :authorize_admin_group!
before_action :authorize_update_max_artifacts_size!, only: [:update]
before_action do
- push_frontend_feature_flag(:new_variables_ui, @group)
+ push_frontend_feature_flag(:new_variables_ui, @group, default_enabled: true)
end
def show
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 01e5103198b..5788fc17a9b 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -31,6 +31,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action only: :show do
push_frontend_feature_flag(:code_navigation, @project)
+ push_frontend_feature_flag(:suggest_pipeline) if experiment_enabled?(:suggest_pipeline)
end
def new
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 37f97785778..5097b6b8c8c 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -6,7 +6,7 @@ module Projects
before_action :authorize_admin_pipeline!
before_action :define_variables
before_action do
- push_frontend_feature_flag(:new_variables_ui, @project)
+ push_frontend_feature_flag(:new_variables_ui, @project, default_enabled: true)
end
def show
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 77a320f8925..9c09ddafbf1 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -341,4 +341,16 @@ module BlobHelper
edit_fork_button_tag(common_classes, project, text, edit_blob_fork_params(edit_path))
end
end
+
+ def show_suggest_pipeline_creation_celebration?
+ experiment_enabled?(:suggest_pipeline) &&
+ @blob.auxiliary_viewer.valid?(project: @project, sha: @commit.sha, user: current_user) &&
+ @blob.path == Gitlab::FileDetector::PATTERNS[:gitlab_ci] &&
+ @project.uses_default_ci_config? &&
+ cookies[suggest_pipeline_commit_cookie_name].present?
+ end
+
+ def suggest_pipeline_commit_cookie_name
+ "suggest_gitlab_ci_yml_commit_#{@project.id}"
+ end
end
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index 7d67a35c94c..c48e60064ed 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -55,7 +55,8 @@ class MergeRequestWidgetEntity < Grape::Entity
merge_request.source_project,
merge_request.source_branch,
file_name: '.gitlab-ci.yml',
- commit_message: s_("CommitMessage|Add %{file_name}") % { file_name: Gitlab::FileDetector::PATTERNS[:gitlab_ci] }
+ commit_message: s_("CommitMessage|Add %{file_name}") % { file_name: Gitlab::FileDetector::PATTERNS[:gitlab_ci] },
+ suggest_gitlab_ci_yml: true
)
end
diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb
index 104d5d3b3dd..b0e6464f5b1 100644
--- a/app/uploaders/import_export_uploader.rb
+++ b/app/uploaders/import_export_uploader.rb
@@ -3,6 +3,10 @@
class ImportExportUploader < AttachmentUploader
EXTENSION_WHITELIST = %w[tar.gz gz].freeze
+ def self.workhorse_local_upload_path
+ File.join(options.storage_path, 'uploads', TMP_UPLOAD_PATH)
+ end
+
def extension_whitelist
EXTENSION_WHITELIST
end
diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml
index f11c730eba6..aadb2c62d83 100644
--- a/app/views/ci/variables/_index.html.haml
+++ b/app/views/ci/variables/_index.html.haml
@@ -5,7 +5,7 @@
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('ci/variables/README', anchor: 'protected-variables') }
= s_('Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
-- if Feature.enabled?(:new_variables_ui, @project || @group)
+- if Feature.enabled?(:new_variables_ui, @project || @group, default_enabled: true)
- is_group = !@group.nil?
#js-ci-project-variables{ data: { endpoint: save_endpoint, project_id: @project&.id || '', group: is_group.to_s, maskable_regex: ci_variable_maskable_regex} }
diff --git a/app/views/projects/blob/_pipeline_tour_success.html.haml b/app/views/projects/blob/_pipeline_tour_success.html.haml
new file mode 100644
index 00000000000..7ecbc1974ec
--- /dev/null
+++ b/app/views/projects/blob/_pipeline_tour_success.html.haml
@@ -0,0 +1 @@
+.js-success-pipeline-modal{ 'data-commit-cookie': suggest_pipeline_commit_cookie_name, 'data-pipelines-path': project_pipelines_path(@project) }
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 7c73bbc7479..c66300aa947 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -14,3 +14,5 @@
- title = "Replace #{@blob.name}"
= render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put
+
+= render partial: 'pipeline_tour_success' if show_suggest_pipeline_creation_celebration?
diff --git a/app/views/projects/merge_requests/_widget.html.haml b/app/views/projects/merge_requests/_widget.html.haml
index 3fe6f0a6640..1853d40c2e4 100644
--- a/app/views/projects/merge_requests/_widget.html.haml
+++ b/app/views/projects/merge_requests/_widget.html.haml
@@ -10,5 +10,6 @@
window.gl.mrWidgetData.troubleshooting_docs_path = '#{help_page_path('user/project/merge_requests/reviewing_and_managing_merge_requests.md', anchor: 'troubleshooting')}';
window.gl.mrWidgetData.security_approvals_help_page_path = '#{help_page_path('user/application_security/index.html', anchor: 'security-approvals-in-merge-requests-ultimate')}';
window.gl.mrWidgetData.eligible_approvers_docs_path = '#{help_page_path('user/project/merge_requests/merge_request_approvals', anchor: 'eligible-approvers')}';
+ window.gl.mrWidgetData.pipelines_empty_svg_path = '#{image_path('illustrations/pipelines_empty.svg')}';
#js-vue-mr-widget.mr-widget