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
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 18:44:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 18:44:42 +0300
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /app/assets/javascripts/pipeline_editor
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'app/assets/javascripts/pipeline_editor')
-rw-r--r--app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue6
-rw-r--r--app/assets/javascripts/pipeline_editor/components/drawer/cards/first_pipeline_card.vue67
-rw-r--r--app/assets/javascripts/pipeline_editor/components/drawer/cards/getting_started_card.vue35
-rw-r--r--app/assets/javascripts/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue75
-rw-r--r--app/assets/javascripts/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue24
-rw-r--r--app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue105
-rw-r--r--app/assets/javascripts/pipeline_editor/components/drawer/ui/demo_job_pill.vue17
-rw-r--r--app/assets/javascripts/pipeline_editor/components/drawer/ui/pipeline_visual_reference.vue43
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue154
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue4
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue12
-rw-r--r--app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue44
-rw-r--r--app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_messages.vue155
-rw-r--r--app/assets/javascripts/pipeline_editor/constants.js6
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql1
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql13
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline_etag.graphql3
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/resolvers.js17
-rw-r--r--app/assets/javascripts/pipeline_editor/index.js27
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue146
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue10
22 files changed, 795 insertions, 171 deletions
diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue b/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue
index 14a4a9d5710..567164cb0ee 100644
--- a/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue
+++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue
@@ -11,6 +11,7 @@ import commitCIFile from '../../graphql/mutations/commit_ci_file.mutation.graphq
import getCommitSha from '../../graphql/queries/client/commit_sha.graphql';
import getCurrentBranch from '../../graphql/queries/client/current_branch.graphql';
import getIsNewCiConfigFile from '../../graphql/queries/client/is_new_ci_config_file.graphql';
+import getPipelineEtag from '../../graphql/queries/client/pipeline_etag.graphql';
import CommitForm from './commit_form.vue';
@@ -94,10 +95,15 @@ export default {
},
update(store, { data }) {
const commitSha = data?.commitCreate?.commit?.sha;
+ const pipelineEtag = data?.commitCreate?.commit?.commitPipelinePath;
if (commitSha) {
store.writeQuery({ query: getCommitSha, data: { commitSha } });
}
+
+ if (pipelineEtag) {
+ store.writeQuery({ query: getPipelineEtag, data: { pipelineEtag } });
+ }
},
});
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/first_pipeline_card.vue b/app/assets/javascripts/pipeline_editor/components/drawer/cards/first_pipeline_card.vue
new file mode 100644
index 00000000000..22c1563350d
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/drawer/cards/first_pipeline_card.vue
@@ -0,0 +1,67 @@
+<script>
+import { GlCard, GlLink, GlSprintf } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import PipelineVisualReference from '../ui/pipeline_visual_reference.vue';
+
+export default {
+ i18n: {
+ title: s__('PipelineEditorTutorial|🚀 Run your first pipeline'),
+ firstParagraph: s__(
+ 'PipelineEditorTutorial|A typical GitLab pipeline consists of three stages: build, test and deploy. Each stage can have one or more jobs.',
+ ),
+ secondParagraph: s__(
+ 'PipelineEditorTutorial|In the example below, %{codeStart}build%{codeEnd} and %{codeStart}deploy%{codeEnd} each contain one job, and %{codeStart}test%{codeEnd} contains two jobs. Your scripts run in jobs like these.',
+ ),
+ thirdParagraph: s__(
+ 'PipelineEditorTutorial|You can use %{linkStart}CI/CD examples and templates%{linkEnd} to get your first %{codeStart}.gitlab-ci.yml%{codeEnd} configuration file started. Your first pipeline runs when you commit the changes.',
+ ),
+ note: s__(
+ 'PipelineEditorTutorial|If you’re using a self-managed GitLab instance, %{linkStart}make sure your instance has runners available.%{linkEnd}',
+ ),
+ },
+ components: {
+ GlCard,
+ GlLink,
+ GlSprintf,
+ PipelineVisualReference,
+ },
+ inject: ['ciExamplesHelpPagePath', 'runnerHelpPagePath'],
+};
+</script>
+<template>
+ <gl-card>
+ <template #default>
+ <h4 class="gl-font-lg gl-mt-0">{{ $options.i18n.title }}</h4>
+ <p class="gl-mb-3">{{ $options.i18n.firstParagraph }}</p>
+ <p class="gl-mb-3">
+ <gl-sprintf :message="$options.i18n.secondParagraph">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ <pipeline-visual-reference />
+ <p class="gl-my-3">
+ <gl-sprintf :message="$options.i18n.thirdParagraph">
+ <template #link="{ content }">
+ <gl-link :href="ciExamplesHelpPagePath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ <p class="gl-mb-0">
+ <gl-sprintf :message="$options.i18n.note">
+ <template #link="{ content }">
+ <gl-link :href="runnerHelpPagePath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ </template>
+ </gl-card>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/getting_started_card.vue b/app/assets/javascripts/pipeline_editor/components/drawer/cards/getting_started_card.vue
new file mode 100644
index 00000000000..3da535f5f94
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/drawer/cards/getting_started_card.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlCard, GlSprintf } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export default {
+ i18n: {
+ title: s__('PipelineEditorTutorial|Get started with GitLab CI/CD'),
+ firstParagraph: s__(
+ 'PipelineEditorTutorial|GitLab CI/CD can automatically build, test, and deploy your application.',
+ ),
+ secondParagraph: s__(
+ 'PipelineEditorTutorial|The pipeline stages and jobs are defined in a %{codeStart}.gitlab-ci.yml%{codeEnd} file. You can edit, visualize and validate the syntax in this file by using the Pipeline Editor.',
+ ),
+ },
+ components: {
+ GlCard,
+ GlSprintf,
+ },
+};
+</script>
+<template>
+ <gl-card>
+ <template #default>
+ <h4 class="gl-font-lg gl-mt-0">{{ $options.i18n.title }}</h4>
+ <p class="gl-mb-3">{{ $options.i18n.firstParagraph }}</p>
+ <p class="gl-mb-0">
+ <gl-sprintf :message="$options.i18n.secondParagraph">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ </template>
+ </gl-card>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue b/app/assets/javascripts/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue
new file mode 100644
index 00000000000..f714f6411f1
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue
@@ -0,0 +1,75 @@
+<script>
+import { GlCard, GlLink, GlSprintf } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export default {
+ i18n: {
+ title: s__('PipelineEditorTutorial|⚙️ Pipeline configuration reference'),
+ firstParagraph: s__('PipelineEditorTutorial|Resources to help with your CI/CD configuration:'),
+ browseExamples: s__(
+ 'PipelineEditorTutorial|Browse %{linkStart}CI/CD examples and templates%{linkEnd}',
+ ),
+ viewSyntaxRef: s__(
+ 'PipelineEditorTutorial|View %{linkStart}.gitlab-ci.yml syntax reference%{linkEnd}',
+ ),
+ learnMore: s__(
+ 'PipelineEditorTutorial|Learn more about %{linkStart}GitLab CI/CD concepts%{linkEnd}',
+ ),
+ needs: s__(
+ 'PipelineEditorTutorial|Make your pipeline more efficient with the %{linkStart}Needs keyword%{linkEnd}',
+ ),
+ },
+ components: {
+ GlCard,
+ GlLink,
+ GlSprintf,
+ },
+ inject: ['ciExamplesHelpPagePath', 'ciHelpPagePath', 'needsHelpPagePath', 'ymlHelpPagePath'],
+};
+</script>
+<template>
+ <gl-card>
+ <template #default>
+ <h4 class="gl-font-lg gl-mt-0">{{ $options.i18n.title }}</h4>
+ <p class="gl-mb-3">{{ $options.i18n.firstParagraph }}</p>
+ <ul>
+ <li>
+ <gl-sprintf :message="$options.i18n.browseExamples">
+ <template #link="{ content }">
+ <gl-link :href="ciExamplesHelpPagePath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </li>
+ <li>
+ <gl-sprintf :message="$options.i18n.viewSyntaxRef">
+ <template #link="{ content }">
+ <gl-link :href="ymlHelpPagePath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </li>
+ <li>
+ <gl-sprintf :message="$options.i18n.learnMore">
+ <template #link="{ content }">
+ <gl-link :href="ciHelpPagePath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </li>
+ <li>
+ <gl-sprintf :message="$options.i18n.needs">
+ <template #link="{ content }">
+ <gl-link :href="needsHelpPagePath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </li>
+ </ul>
+ </template>
+ </gl-card>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue b/app/assets/javascripts/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue
new file mode 100644
index 00000000000..512414f0246
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue
@@ -0,0 +1,24 @@
+<script>
+import { GlCard } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export default {
+ i18n: {
+ title: s__('PipelineEditorTutorial|💡 Tip: Visualize and validate your pipeline'),
+ firstParagraph: s__(
+ 'PipelineEditorTutorial|Use the Visualize and Lint tabs in the Pipeline Editor to visualize your pipeline and check for any errors or warnings before committing your changes.',
+ ),
+ },
+ components: {
+ GlCard,
+ },
+};
+</script>
+<template>
+ <gl-card>
+ <template #default>
+ <h4 class="gl-font-lg gl-mt-0">{{ $options.i18n.title }}</h4>
+ <p class="gl-mb-0">{{ $options.i18n.firstParagraph }}</p>
+ </template>
+ </gl-card>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue b/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue
new file mode 100644
index 00000000000..ff1e0b6388f
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/drawer/pipeline_editor_drawer.vue
@@ -0,0 +1,105 @@
+<script>
+import { GlButton, GlIcon } from '@gitlab/ui';
+import { __ } from '~/locale';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+import { DRAWER_EXPANDED_KEY } from '../../constants';
+import FirstPipelineCard from './cards/first_pipeline_card.vue';
+import GettingStartedCard from './cards/getting_started_card.vue';
+import PipelineConfigReferenceCard from './cards/pipeline_config_reference_card.vue';
+import VisualizeAndLintCard from './cards/visualize_and_lint_card.vue';
+
+export default {
+ width: {
+ expanded: '482px',
+ collapsed: '58px',
+ },
+ i18n: {
+ toggleTxt: __('Collapse'),
+ },
+ localDrawerKey: DRAWER_EXPANDED_KEY,
+ components: {
+ FirstPipelineCard,
+ GettingStartedCard,
+ GlButton,
+ GlIcon,
+ LocalStorageSync,
+ PipelineConfigReferenceCard,
+ VisualizeAndLintCard,
+ },
+ data() {
+ return {
+ isExpanded: false,
+ topPosition: 0,
+ };
+ },
+ computed: {
+ buttonIconName() {
+ return this.isExpanded ? 'chevron-double-lg-right' : 'chevron-double-lg-left';
+ },
+ buttonClass() {
+ return this.isExpanded ? 'gl-justify-content-end!' : '';
+ },
+ rootStyle() {
+ const { expanded, collapsed } = this.$options.width;
+ const top = this.topPosition;
+ const style = { top: `${top}px` };
+
+ return this.isExpanded ? { ...style, width: expanded } : { ...style, width: collapsed };
+ },
+ },
+ mounted() {
+ this.setTopPosition();
+ this.setInitialExpandState();
+ },
+ methods: {
+ setInitialExpandState() {
+ // We check in the local storage and if no value is defined, we want the default
+ // to be true. We want to explicitly set it to true here so that the drawer
+ // animates to open on load.
+ const localValue = localStorage.getItem(this.$options.localDrawerKey);
+ if (localValue === null) {
+ this.isExpanded = true;
+ }
+ },
+ setTopPosition() {
+ const navbarEl = document.querySelector('.js-navbar');
+
+ if (navbarEl) {
+ this.topPosition = navbarEl.getBoundingClientRect().bottom;
+ }
+ },
+ toggleDrawer() {
+ this.isExpanded = !this.isExpanded;
+ },
+ },
+};
+</script>
+<template>
+ <local-storage-sync v-model="isExpanded" :storage-key="$options.localDrawerKey" as-json>
+ <aside
+ aria-live="polite"
+ class="gl-fixed gl-right-0 gl-bg-gray-10 gl-shadow-drawer gl-transition-property-width gl-transition-duration-medium gl-border-l-solid gl-border-1 gl-border-gray-100 gl-h-full gl-z-index-3 gl-overflow-y-auto"
+ :style="rootStyle"
+ >
+ <gl-button
+ category="tertiary"
+ class="gl-w-full gl-h-9 gl-rounded-0! gl-border-none! gl-border-b-solid! gl-border-1! gl-border-gray-100 gl-text-decoration-none! gl-outline-0! gl-display-flex"
+ :class="buttonClass"
+ :title="__('Toggle sidebar')"
+ @click="toggleDrawer"
+ >
+ <span v-if="isExpanded" class="gl-text-gray-500 gl-mr-3" data-testid="collapse-text">
+ {{ __('Collapse') }}
+ </span>
+ <gl-icon data-testid="toggle-icon" :name="buttonIconName" />
+ </gl-button>
+ <div v-if="isExpanded" class="gl-h-full gl-p-5" data-testid="drawer-content">
+ <getting-started-card class="gl-mb-4" />
+ <first-pipeline-card class="gl-mb-4" />
+ <visualize-and-lint-card class="gl-mb-4" />
+ <pipeline-config-reference-card />
+ <div class="gl-h-13"></div>
+ </div>
+ </aside>
+ </local-storage-sync>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/ui/demo_job_pill.vue b/app/assets/javascripts/pipeline_editor/components/drawer/ui/demo_job_pill.vue
new file mode 100644
index 00000000000..049504181c4
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/drawer/ui/demo_job_pill.vue
@@ -0,0 +1,17 @@
+<script>
+export default {
+ props: {
+ jobName: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div
+ class="gl-w-13 gl-h-6 gl-font-sm gl-bg-white gl-inset-border-1-blue-500 gl-text-center gl-text-truncate gl-rounded-pill gl-px-4 gl-py-2 gl-relative gl-z-index-1 gl-transition-duration-slow gl-transition-timing-function-ease"
+ >
+ {{ jobName }}
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/drawer/ui/pipeline_visual_reference.vue b/app/assets/javascripts/pipeline_editor/components/drawer/ui/pipeline_visual_reference.vue
new file mode 100644
index 00000000000..1017237365b
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/drawer/ui/pipeline_visual_reference.vue
@@ -0,0 +1,43 @@
+<script>
+import { s__ } from '~/locale';
+import DemoJobPill from './demo_job_pill.vue';
+
+export default {
+ i18n: {
+ stageNames: {
+ build: s__('StageName|Build'),
+ test: s__('StageName|Test'),
+ deploy: s__('StageName|Deploy'),
+ },
+ jobNames: {
+ build: s__('JobName|build-job'),
+ test_1: s__('JobName|unit-test'),
+ test_2: s__('JobName|lint-test'),
+ deploy: s__('JobName|deploy-app'),
+ },
+ },
+ stageClasses:
+ 'gl-bg-blue-50 gl-display-flex gl-flex-direction-column gl-align-items-center gl-p-4 gl-rounded-base',
+ titleClasses: 'gl-text-blue-600 gl-mb-4',
+ components: {
+ DemoJobPill,
+ },
+};
+</script>
+<template>
+ <div class="gl-display-flex gl-justify-content-center">
+ <div :class="$options.stageClasses" class="gl-mr-5">
+ <div :class="$options.titleClasses">{{ $options.i18n.stageNames.build }}</div>
+ <demo-job-pill :job-name="$options.i18n.jobNames.build" />
+ </div>
+ <div :class="$options.stageClasses" class="gl-mr-5">
+ <div :class="$options.titleClasses">{{ $options.i18n.stageNames.test }}</div>
+ <demo-job-pill class="gl-mb-3" :job-name="$options.i18n.jobNames.test_1" />
+ <demo-job-pill :job-name="$options.i18n.jobNames.test_2" />
+ </div>
+ <div :class="$options.stageClasses">
+ <div :class="$options.titleClasses">{{ $options.i18n.stageNames.deploy }}</div>
+ <demo-job-pill :job-name="$options.i18n.jobNames.deploy" />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
index b3eba0fcc19..1acf3a03e73 100644
--- a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
+++ b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
@@ -1,32 +1,77 @@
<script>
-import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader, GlIcon } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlInfiniteScroll,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
+import { historyPushState } from '~/lib/utils/common_utils';
+import { setUrlParams } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
-import { DEFAULT_FAILURE } from '~/pipeline_editor/constants';
+import {
+ BRANCH_PAGINATION_LIMIT,
+ BRANCH_SEARCH_DEBOUNCE,
+ DEFAULT_FAILURE,
+} from '~/pipeline_editor/constants';
import getAvailableBranches from '~/pipeline_editor/graphql/queries/available_branches.graphql';
import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.graphql';
export default {
i18n: {
+ dropdownHeader: s__('Switch Branch'),
title: s__('Branches'),
fetchError: s__('Unable to fetch branch list for this project.'),
},
+ inputDebounce: BRANCH_SEARCH_DEBOUNCE,
components: {
GlDropdown,
GlDropdownItem,
GlDropdownSectionHeader,
- GlIcon,
+ GlInfiniteScroll,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ },
+ inject: ['projectFullPath', 'totalBranches'],
+ props: {
+ paginationLimit: {
+ type: Number,
+ required: false,
+ default: BRANCH_PAGINATION_LIMIT,
+ },
+ },
+ data() {
+ return {
+ branches: [],
+ page: {
+ limit: this.paginationLimit,
+ offset: 0,
+ searchTerm: '',
+ },
+ };
},
- inject: ['projectFullPath'],
apollo: {
- branches: {
+ availableBranches: {
query: getAvailableBranches,
variables() {
return {
+ limit: this.page.limit,
+ offset: this.page.offset,
projectFullPath: this.projectFullPath,
+ searchPattern: this.searchPattern,
};
},
update(data) {
- return data.project?.repository?.branches || [];
+ return data.project?.repository?.branchNames || [];
+ },
+ result({ data }) {
+ const newBranches = data.project?.repository?.branchNames || [];
+
+ // check that we're not re-concatenating existing fetch results
+ if (!this.branches.includes(newBranches[0])) {
+ this.branches = this.branches.concat(newBranches);
+ }
},
error() {
this.$emit('showError', {
@@ -40,26 +85,99 @@ export default {
},
},
computed: {
- hasBranchList() {
- return this.branches?.length > 0;
+ isBranchesLoading() {
+ return this.$apollo.queries.availableBranches.loading;
+ },
+ showBranchSwitcher() {
+ return this.branches.length > 0 || this.page.searchTerm.length > 0;
+ },
+ searchPattern() {
+ if (this.page.searchTerm === '') {
+ return '*';
+ }
+
+ return `*${this.page.searchTerm}*`;
+ },
+ },
+ methods: {
+ // if there is no searchPattern, paginate by {paginationLimit} branches
+ fetchNextBranches() {
+ if (
+ this.isBranchesLoading ||
+ this.page.searchTerm.length > 0 ||
+ this.branches.length === this.totalBranches
+ ) {
+ return;
+ }
+
+ this.page = {
+ ...this.page,
+ limit: this.paginationLimit,
+ offset: this.page.offset + this.paginationLimit,
+ };
+ },
+ async selectBranch(newBranch) {
+ if (newBranch === this.currentBranch) {
+ return;
+ }
+
+ await this.$apollo.getClient().writeQuery({
+ query: getCurrentBranch,
+ data: { currentBranch: newBranch },
+ });
+
+ const updatedPath = setUrlParams({ branch_name: newBranch });
+ historyPushState(updatedPath);
+
+ this.$emit('refetchContent');
+ },
+ setSearchTerm(newSearchTerm) {
+ this.branches = [];
+ this.page = {
+ limit: newSearchTerm.trim() === '' ? this.paginationLimit : this.totalBranches,
+ offset: 0,
+ searchTerm: newSearchTerm.trim(),
+ };
},
},
};
</script>
<template>
- <gl-dropdown v-if="hasBranchList" class="gl-ml-2" :text="currentBranch" icon="branch">
+ <gl-dropdown
+ v-if="showBranchSwitcher"
+ class="gl-ml-2"
+ :header-text="$options.i18n.dropdownHeader"
+ :text="currentBranch"
+ icon="branch"
+ >
+ <gl-search-box-by-type :debounce="$options.inputDebounce" @input="setSearchTerm" />
<gl-dropdown-section-header>
- {{ this.$options.i18n.title }}
+ {{ $options.i18n.title }}
</gl-dropdown-section-header>
- <gl-dropdown-item
- v-for="branch in branches"
- :key="branch.name"
- :is-checked="currentBranch === branch.name"
- :is-check-item="true"
+
+ <gl-infinite-scroll
+ :fetched-items="branches.length"
+ :total-items="totalBranches"
+ :max-list-height="250"
+ @bottomReached="fetchNextBranches"
>
- <gl-icon name="check" class="gl-visibility-hidden" />
- {{ branch.name }}
- </gl-dropdown-item>
+ <template #items>
+ <gl-dropdown-item
+ v-for="branch in branches"
+ :key="branch"
+ :is-checked="currentBranch === branch"
+ :is-check-item="true"
+ @click="selectBranch(branch)"
+ >
+ {{ branch }}
+ </gl-dropdown-item>
+ </template>
+ <template #default>
+ <gl-dropdown-item v-if="isBranchesLoading" key="loading">
+ <gl-loading-icon size="md" />
+ </gl-dropdown-item>
+ </template>
+ </gl-infinite-scroll>
</gl-dropdown>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue
index fefa784f060..24bca04e115 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue
@@ -1,5 +1,4 @@
<script>
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import PipelineStatus from './pipeline_status.vue';
import ValidationSegment from './validation_segment.vue';
@@ -29,7 +28,6 @@ export default {
PipelineStatus,
ValidationSegment,
},
- mixins: [glFeatureFlagsMixin()],
props: {
ciConfigData: {
type: Object,
@@ -42,7 +40,7 @@ export default {
},
computed: {
showPipelineStatus() {
- return this.glFeatures.pipelineStatusForPipelineEditor && !this.isNewCiConfigFile;
+ return !this.isNewCiConfigFile;
},
// make sure corners are rounded correctly depending on if
// pipeline status is rendered
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
index 4a92e106da1..368a026bdaa 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
@@ -5,7 +5,11 @@ import { truncateSha } from '~/lib/utils/text_utility';
import { s__ } from '~/locale';
import getCommitSha from '~/pipeline_editor/graphql/queries/client/commit_sha.graphql';
import getPipelineQuery from '~/pipeline_editor/graphql/queries/client/pipeline.graphql';
-import { toggleQueryPollingByVisibility } from '~/pipelines/components/graph/utils';
+import getPipelineEtag from '~/pipeline_editor/graphql/queries/client/pipeline_etag.graphql';
+import {
+ getQueryHeaders,
+ toggleQueryPollingByVisibility,
+} from '~/pipelines/components/graph/utils';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
const POLL_INTERVAL = 10000;
@@ -31,7 +35,13 @@ export default {
commitSha: {
query: getCommitSha,
},
+ pipelineEtag: {
+ query: getPipelineEtag,
+ },
pipeline: {
+ context() {
+ return getQueryHeaders(this.pipelineEtag);
+ },
query: getPipelineQuery,
variables() {
return {
diff --git a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue b/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
index 5acb3355b23..4e2f26af51d 100644
--- a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
+++ b/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
@@ -110,7 +110,6 @@ export default {
<text-editor :value="ciFileContent" v-on="$listeners" />
</editor-tab>
<editor-tab
- v-if="glFeatures.ciConfigVisualizationTab"
class="gl-mb-3"
:empty-message="$options.i18n.empty.visualization"
:is-empty="isEmpty"
@@ -135,7 +134,6 @@ export default {
<ci-lint v-else :is-valid="isValid" :ci-config="ciConfigData" />
</editor-tab>
<editor-tab
- v-if="glFeatures.ciConfigMergedTab"
class="gl-mb-3"
:empty-message="$options.i18n.empty.merge"
:keep-component-mounted="false"
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue b/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
index d4f04a0d055..0ac4a40ff4a 100644
--- a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
+++ b/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue
@@ -1,12 +1,14 @@
<script>
import { GlButton, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
+import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
GlButton,
GlSprintf,
+ PipelineEditorFileNav,
},
i18n: {
title: __('Optimize your workflow with CI/CD Pipelines'),
@@ -22,6 +24,9 @@ export default {
},
},
computed: {
+ showFileNav() {
+ return this.glFeatures.pipelineEditorBranchSwitcher;
+ },
showCTAButton() {
return this.glFeatures.pipelineEditorEmptyStateAction;
},
@@ -34,23 +39,26 @@ export default {
};
</script>
<template>
- <div class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-mt-11">
- <img :src="emptyStateIllustrationPath" />
- <h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1>
- <p class="gl-mt-3">
- <gl-sprintf :message="$options.i18n.body">
- <template #code="{ content }">
- <code>{{ content }}</code>
- </template>
- </gl-sprintf>
- </p>
- <gl-button
- v-if="showCTAButton"
- variant="confirm"
- class="gl-mt-3"
- @click="createEmptyConfigFile"
- >
- {{ $options.i18n.btnText }}
- </gl-button>
+ <div>
+ <pipeline-editor-file-nav v-if="showFileNav" v-on="$listeners" />
+ <div class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-mt-11">
+ <img :src="emptyStateIllustrationPath" />
+ <h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1>
+ <p class="gl-mt-3">
+ <gl-sprintf :message="$options.i18n.body">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+ <gl-button
+ v-if="showCTAButton"
+ variant="confirm"
+ class="gl-mt-3"
+ @click="createEmptyConfigFile"
+ >
+ {{ $options.i18n.btnText }}
+ </gl-button>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_messages.vue b/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_messages.vue
new file mode 100644
index 00000000000..091b202e10b
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_messages.vue
@@ -0,0 +1,155 @@
+<script>
+import { GlAlert } from '@gitlab/ui';
+import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
+import { __, s__ } from '~/locale';
+import {
+ COMMIT_FAILURE,
+ COMMIT_SUCCESS,
+ DEFAULT_FAILURE,
+ DEFAULT_SUCCESS,
+ LOAD_FAILURE_UNKNOWN,
+} from '../../constants';
+import CodeSnippetAlert from '../code_snippet_alert/code_snippet_alert.vue';
+import {
+ CODE_SNIPPET_SOURCE_URL_PARAM,
+ CODE_SNIPPET_SOURCES,
+} from '../code_snippet_alert/constants';
+
+export default {
+ components: {
+ GlAlert,
+ CodeSnippetAlert,
+ },
+ errorTexts: {
+ [COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
+ [DEFAULT_FAILURE]: __('Something went wrong on our end.'),
+ [LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'),
+ },
+ successTexts: {
+ [COMMIT_SUCCESS]: __('Your changes have been successfully committed.'),
+ [DEFAULT_SUCCESS]: __('Your action succeeded.'),
+ },
+ props: {
+ failureType: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ failureReasons: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ showFailure: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showSuccess: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ successType: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ codeSnippetCopiedFrom: '',
+ };
+ },
+ computed: {
+ failure() {
+ switch (this.failureType) {
+ case LOAD_FAILURE_UNKNOWN:
+ return {
+ text: this.$options.errorTexts[LOAD_FAILURE_UNKNOWN],
+ variant: 'danger',
+ };
+ case COMMIT_FAILURE:
+ return {
+ text: this.$options.errorTexts[COMMIT_FAILURE],
+ variant: 'danger',
+ };
+ default:
+ return {
+ text: this.$options.errorTexts[DEFAULT_FAILURE],
+ variant: 'danger',
+ };
+ }
+ },
+ success() {
+ switch (this.successType) {
+ case COMMIT_SUCCESS:
+ return {
+ text: this.$options.successTexts[COMMIT_SUCCESS],
+ variant: 'info',
+ };
+ default:
+ return {
+ text: this.$options.successTexts[DEFAULT_SUCCESS],
+ variant: 'info',
+ };
+ }
+ },
+ },
+ created() {
+ this.parseCodeSnippetSourceParam();
+ },
+ methods: {
+ dismissCodeSnippetAlert() {
+ this.codeSnippetCopiedFrom = '';
+ },
+ dismissFailure() {
+ this.$emit('hide-failure');
+ },
+ dismissSuccess() {
+ this.$emit('hide-success');
+ },
+ parseCodeSnippetSourceParam() {
+ const [codeSnippetCopiedFrom] = getParameterValues(CODE_SNIPPET_SOURCE_URL_PARAM);
+ if (codeSnippetCopiedFrom && CODE_SNIPPET_SOURCES.includes(codeSnippetCopiedFrom)) {
+ this.codeSnippetCopiedFrom = codeSnippetCopiedFrom;
+ window.history.replaceState(
+ {},
+ document.title,
+ removeParams([CODE_SNIPPET_SOURCE_URL_PARAM]),
+ );
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <code-snippet-alert
+ v-if="codeSnippetCopiedFrom"
+ :source="codeSnippetCopiedFrom"
+ class="gl-mb-5"
+ @dismiss="dismissCodeSnippetAlert"
+ />
+ <gl-alert
+ v-if="showSuccess"
+ :variant="success.variant"
+ class="gl-mb-5"
+ @dismiss="dismissSuccess"
+ >
+ {{ success.text }}
+ </gl-alert>
+ <gl-alert
+ v-if="showFailure"
+ :variant="failure.variant"
+ class="gl-mb-5"
+ @dismiss="dismissFailure"
+ >
+ {{ failure.text }}
+ <ul v-if="failureReasons.length" class="gl-mb-0">
+ <li v-for="reason in failureReasons" :key="reason">{{ reason }}</li>
+ </ul>
+ </gl-alert>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/constants.js b/app/assets/javascripts/pipeline_editor/constants.js
index 8d0ec6c3e2d..f0a24e0c061 100644
--- a/app/assets/javascripts/pipeline_editor/constants.js
+++ b/app/assets/javascripts/pipeline_editor/constants.js
@@ -14,6 +14,7 @@ export const COMMIT_FAILURE = 'COMMIT_FAILURE';
export const COMMIT_SUCCESS = 'COMMIT_SUCCESS';
export const DEFAULT_FAILURE = 'DEFAULT_FAILURE';
+export const DEFAULT_SUCCESS = 'DEFAULT_SUCCESS';
export const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN';
export const CREATE_TAB = 'CREATE_TAB';
@@ -25,3 +26,8 @@ export const TABS_WITH_COMMIT_FORM = [CREATE_TAB, LINT_TAB, VISUALIZE_TAB];
export const COMMIT_ACTION_CREATE = 'CREATE';
export const COMMIT_ACTION_UPDATE = 'UPDATE';
+
+export const DRAWER_EXPANDED_KEY = 'pipeline_editor_drawer_expanded';
+
+export const BRANCH_PAGINATION_LIMIT = 20;
+export const BRANCH_SEARCH_DEBOUNCE = '500';
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql b/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
index 3b2daa45a18..94e6facabfd 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
+++ b/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
@@ -22,6 +22,7 @@ mutation commitCIFile(
commit {
sha
}
+ commitPipelinePath
errors
}
}
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql
index f162bb11d47..46e9b108b41 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql
@@ -1,9 +1,12 @@
-query getAvailableBranches($projectFullPath: ID!) {
- project(fullPath: $projectFullPath) @client {
+query getAvailableBranches(
+ $limit: Int!
+ $offset: Int!
+ $projectFullPath: ID!
+ $searchPattern: String!
+) {
+ project(fullPath: $projectFullPath) {
repository {
- branches {
- name
- }
+ branchNames(limit: $limit, offset: $offset, searchPattern: $searchPattern)
}
}
}
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline_etag.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline_etag.graphql
new file mode 100644
index 00000000000..b9946a9e233
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline_etag.graphql
@@ -0,0 +1,3 @@
+query getPipelineEtag {
+ pipelineEtag @client
+}
diff --git a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
index caa2a65d424..81e75c32846 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
+++ b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
@@ -11,23 +11,6 @@ export const resolvers = {
}),
};
},
- /* eslint-disable @gitlab/require-i18n-strings */
- project() {
- return {
- __typename: 'Project',
- repository: {
- __typename: 'Repository',
- branches: [
- { __typename: 'Branch', name: 'master' },
- { __typename: 'Branch', name: 'main' },
- { __typename: 'Branch', name: 'develop' },
- { __typename: 'Branch', name: 'production' },
- { __typename: 'Branch', name: 'test' },
- ],
- },
- };
- },
- /* eslint-enable @gitlab/require-i18n-strings */
},
Mutation: {
lintCI: (_, { endpoint, content, dry_run }) => {
diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/pipeline_editor/index.js
index 8a1e26f9bff..66158bdba88 100644
--- a/app/assets/javascripts/pipeline_editor/index.js
+++ b/app/assets/javascripts/pipeline_editor/index.js
@@ -6,6 +6,7 @@ import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
import { CODE_SNIPPET_SOURCE_SETTINGS } from './components/code_snippet_alert/constants';
import getCommitSha from './graphql/queries/client/commit_sha.graphql';
import getCurrentBranch from './graphql/queries/client/current_branch.graphql';
+import getPipelineEtag from './graphql/queries/client/pipeline_etag.graphql';
import { resolvers } from './graphql/resolvers';
import typeDefs from './graphql/typedefs.graphql';
import PipelineEditorApp from './pipeline_editor_app.vue';
@@ -26,15 +27,23 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
// Add to apollo cache as it can be updated by future queries
commitSha,
initialBranchName,
+ pipelineEtag,
// Add to provide/inject API for static values
ciConfigPath,
+ ciExamplesHelpPagePath,
+ ciHelpPagePath,
defaultBranch,
emptyStateIllustrationPath,
+ helpPaths,
lintHelpPagePath,
+ needsHelpPagePath,
newMergeRequestPath,
+ pipelinePagePath,
projectFullPath,
projectPath,
projectNamespace,
+ runnerHelpPagePath,
+ totalBranches,
ymlHelpPagePath,
} = el?.dataset;
@@ -48,7 +57,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(resolvers, { typeDefs }),
+ defaultClient: createDefaultClient(resolvers, { typeDefs, useGet: true }),
});
const { cache } = apolloProvider.clients.defaultClient;
@@ -66,20 +75,34 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
},
});
+ cache.writeQuery({
+ query: getPipelineEtag,
+ data: {
+ pipelineEtag,
+ },
+ });
+
return new Vue({
el,
apolloProvider,
provide: {
ciConfigPath,
+ ciExamplesHelpPagePath,
+ ciHelpPagePath,
+ configurationPaths,
defaultBranch,
emptyStateIllustrationPath,
+ helpPaths,
lintHelpPagePath,
+ needsHelpPagePath,
newMergeRequestPath,
+ pipelinePagePath,
projectFullPath,
projectPath,
projectNamespace,
+ runnerHelpPagePath,
+ totalBranches: parseInt(totalBranches, 10),
ymlHelpPagePath,
- configurationPaths,
},
render(h) {
return h(PipelineEditorApp);
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
index e0fb38004ec..79a2a51cebc 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
@@ -1,21 +1,15 @@
<script>
-import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { fetchPolicies } from '~/lib/graphql';
import httpStatusCodes from '~/lib/utils/http_status';
-import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
-import { __, s__ } from '~/locale';
+import { s__ } from '~/locale';
import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
-import CodeSnippetAlert from './components/code_snippet_alert/code_snippet_alert.vue';
-import {
- CODE_SNIPPET_SOURCE_URL_PARAM,
- CODE_SNIPPET_SOURCES,
-} from './components/code_snippet_alert/constants';
+
import ConfirmUnsavedChangesDialog from './components/ui/confirm_unsaved_changes_dialog.vue';
import PipelineEditorEmptyState from './components/ui/pipeline_editor_empty_state.vue';
+import PipelineEditorMessages from './components/ui/pipeline_editor_messages.vue';
import {
- COMMIT_FAILURE,
- COMMIT_SUCCESS,
- DEFAULT_FAILURE,
EDITOR_APP_STATUS_EMPTY,
EDITOR_APP_STATUS_ERROR,
EDITOR_APP_STATUS_LOADING,
@@ -31,11 +25,10 @@ import PipelineEditorHome from './pipeline_editor_home.vue';
export default {
components: {
ConfirmUnsavedChangesDialog,
- GlAlert,
GlLoadingIcon,
PipelineEditorEmptyState,
PipelineEditorHome,
- CodeSnippetAlert,
+ PipelineEditorMessages,
},
inject: {
ciConfigPath: {
@@ -50,20 +43,20 @@ export default {
ciConfigData: {},
failureType: null,
failureReasons: [],
- showStartScreen: false,
- isNewCiConfigFile: false,
initialCiFileContent: '',
+ isNewCiConfigFile: false,
lastCommittedContent: '',
currentCiFileContent: '',
- showFailureAlert: false,
- showSuccessAlert: false,
successType: null,
- codeSnippetCopiedFrom: '',
+ showStartScreen: false,
+ showSuccess: false,
+ showFailure: false,
};
},
apollo: {
initialCiFileContent: {
+ fetchPolicy: fetchPolicies.NETWORK,
query: getBlobContent,
// If it's a brand new file, we don't want to fetch the content.
// Then when the user commits the first time, the query would run
@@ -87,10 +80,21 @@ export default {
this.lastCommittedContent = fileContent;
this.currentCiFileContent = fileContent;
+
+ // make sure to reset the start screen flag during a refetch
+ // e.g. when switching branches
+ if (fileContent.length) {
+ this.showStartScreen = false;
+ }
},
error(error) {
this.handleBlobContentError(error);
},
+ watchLoading(isLoading) {
+ if (isLoading) {
+ this.setAppStatus(EDITOR_APP_STATUS_LOADING);
+ }
+ },
},
ciConfigData: {
query: getCiConfigData,
@@ -145,50 +149,12 @@ export default {
isEmpty() {
return this.currentCiFileContent === '';
},
- failure() {
- switch (this.failureType) {
- case LOAD_FAILURE_UNKNOWN:
- return {
- text: this.$options.errorTexts[LOAD_FAILURE_UNKNOWN],
- variant: 'danger',
- };
- case COMMIT_FAILURE:
- return {
- text: this.$options.errorTexts[COMMIT_FAILURE],
- variant: 'danger',
- };
- default:
- return {
- text: this.$options.errorTexts[DEFAULT_FAILURE],
- variant: 'danger',
- };
- }
- },
- success() {
- switch (this.successType) {
- case COMMIT_SUCCESS:
- return {
- text: this.$options.successTexts[COMMIT_SUCCESS],
- variant: 'info',
- };
- default:
- return null;
- }
- },
},
i18n: {
tabEdit: s__('Pipelines|Write pipeline configuration'),
tabGraph: s__('Pipelines|Visualize'),
tabLint: s__('Pipelines|Lint'),
},
- errorTexts: {
- [COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
- [DEFAULT_FAILURE]: __('Something went wrong on our end.'),
- [LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'),
- },
- successTexts: {
- [COMMIT_SUCCESS]: __('Your changes have been successfully committed.'),
- },
watch: {
isEmpty(flag) {
if (flag) {
@@ -196,9 +162,6 @@ export default {
}
},
},
- created() {
- this.parseCodeSnippetSourceParam();
- },
methods: {
handleBlobContentError(error = {}) {
const { networkError } = error;
@@ -216,24 +179,27 @@ export default {
this.reportFailure(LOAD_FAILURE_UNKNOWN);
}
},
-
- dismissFailure() {
- this.showFailureAlert = false;
+ hideFailure() {
+ this.showFailure = false;
+ },
+ hideSuccess() {
+ this.showSuccess = false;
},
- dismissSuccess() {
- this.showSuccessAlert = false;
+ async refetchContent() {
+ this.$apollo.queries.initialCiFileContent.skip = false;
+ await this.$apollo.queries.initialCiFileContent.refetch();
},
reportFailure(type, reasons = []) {
this.setAppStatus(EDITOR_APP_STATUS_ERROR);
window.scrollTo({ top: 0, behavior: 'smooth' });
- this.showFailureAlert = true;
+ this.showFailure = true;
this.failureType = type;
this.failureReasons = reasons;
},
reportSuccess(type) {
window.scrollTo({ top: 0, behavior: 'smooth' });
- this.showSuccessAlert = true;
+ this.showSuccess = true;
this.successType = type;
},
resetContent() {
@@ -266,20 +232,6 @@ export default {
// if the user has made changes to the file that are unsaved.
this.lastCommittedContent = this.currentCiFileContent;
},
- parseCodeSnippetSourceParam() {
- const [codeSnippetCopiedFrom] = getParameterValues(CODE_SNIPPET_SOURCE_URL_PARAM);
- if (codeSnippetCopiedFrom && CODE_SNIPPET_SOURCES.includes(codeSnippetCopiedFrom)) {
- this.codeSnippetCopiedFrom = codeSnippetCopiedFrom;
- window.history.replaceState(
- {},
- document.title,
- removeParams([CODE_SNIPPET_SOURCE_URL_PARAM]),
- );
- }
- },
- dismissCodeSnippetAlert() {
- this.codeSnippetCopiedFrom = '';
- },
},
};
</script>
@@ -290,33 +242,18 @@ export default {
<pipeline-editor-empty-state
v-else-if="showStartScreen"
@createEmptyConfigFile="setNewEmptyCiConfigFile"
+ @refetchContent="refetchContent"
/>
<div v-else>
- <code-snippet-alert
- v-if="codeSnippetCopiedFrom"
- :source="codeSnippetCopiedFrom"
- class="gl-mb-5"
- @dismiss="dismissCodeSnippetAlert"
+ <pipeline-editor-messages
+ :failure-type="failureType"
+ :failure-reasons="failureReasons"
+ :show-failure="showFailure"
+ :show-success="showSuccess"
+ :success-type="successType"
+ @hide-success="hideSuccess"
+ @hide-failure="hideFailure"
/>
- <gl-alert
- v-if="showSuccessAlert"
- :variant="success.variant"
- class="gl-mb-5"
- @dismiss="dismissSuccess"
- >
- {{ success.text }}
- </gl-alert>
- <gl-alert
- v-if="showFailureAlert"
- :variant="failure.variant"
- class="gl-mb-5"
- @dismiss="dismissFailure"
- >
- {{ failure.text }}
- <ul v-if="failureReasons.length" class="gl-mb-0">
- <li v-for="reason in failureReasons" :key="reason">{{ reason }}</li>
- </ul>
- </gl-alert>
<pipeline-editor-home
:ci-config-data="ciConfigData"
:ci-file-content="currentCiFileContent"
@@ -324,6 +261,7 @@ export default {
@commit="updateOnCommit"
@resetContent="resetContent"
@showError="showErrorAlert"
+ @refetchContent="refetchContent"
@updateCiConfig="updateCiConfig"
/>
<confirm-unsaved-changes-dialog :has-unsaved-changes="hasUnsavedChanges" />
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
index adba55f9f4b..dfe9c82b912 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
@@ -1,5 +1,7 @@
<script>
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import CommitSection from './components/commit/commit_section.vue';
+import PipelineEditorDrawer from './components/drawer/pipeline_editor_drawer.vue';
import PipelineEditorFileNav from './components/file_nav/pipeline_editor_file_nav.vue';
import PipelineEditorHeader from './components/header/pipeline_editor_header.vue';
import PipelineEditorTabs from './components/pipeline_editor_tabs.vue';
@@ -8,10 +10,12 @@ import { TABS_WITH_COMMIT_FORM, CREATE_TAB } from './constants';
export default {
components: {
CommitSection,
+ PipelineEditorDrawer,
PipelineEditorFileNav,
PipelineEditorHeader,
PipelineEditorTabs,
},
+ mixins: [glFeatureFlagMixin()],
props: {
ciConfigData: {
type: Object,
@@ -35,6 +39,9 @@ export default {
showCommitForm() {
return TABS_WITH_COMMIT_FORM.includes(this.currentTab);
},
+ showPipelineDrawer() {
+ return this.glFeatures.pipelineEditorDrawer;
+ },
},
methods: {
setCurrentTab(tabName) {
@@ -45,7 +52,7 @@ export default {
</script>
<template>
- <div>
+ <div class="gl-pr-9 gl-transition-medium gl-w-full">
<pipeline-editor-file-nav v-on="$listeners" />
<pipeline-editor-header
:ci-config-data="ciConfigData"
@@ -58,5 +65,6 @@ export default {
@set-current-tab="setCurrentTab"
/>
<commit-section v-if="showCommitForm" :ci-file-content="ciFileContent" v-on="$listeners" />
+ <pipeline-editor-drawer v-if="showPipelineDrawer" />
</div>
</template>