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:
Diffstat (limited to 'app/assets/javascripts/pipeline_editor')
-rw-r--r--app/assets/javascripts/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue42
-rw-r--r--app/assets/javascripts/pipeline_editor/components/code_snippet_alert/constants.js11
-rw-r--r--app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue76
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue65
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue21
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue15
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue34
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue59
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue12
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue30
-rw-r--r--app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue75
-rw-r--r--app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue66
-rw-r--r--app/assets/javascripts/pipeline_editor/constants.js11
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql9
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/client/app_status.graphql3
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql3
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/resolvers.js24
-rw-r--r--app/assets/javascripts/pipeline_editor/index.js21
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue90
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue9
22 files changed, 489 insertions, 191 deletions
diff --git a/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue b/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue
new file mode 100644
index 00000000000..7b33d98bca0
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue
@@ -0,0 +1,42 @@
+<script>
+import { GlAlert } from '@gitlab/ui';
+import { CODE_SNIPPET_SOURCES, CODE_SNIPPET_SOURCE_SETTINGS } from './constants';
+
+export default {
+ name: 'CodeSnippetAlert',
+ components: {
+ GlAlert,
+ },
+ inject: ['configurationPaths'],
+ props: {
+ source: {
+ type: String,
+ required: true,
+ validator: (source) => CODE_SNIPPET_SOURCES.includes(source),
+ },
+ },
+ computed: {
+ settings() {
+ return CODE_SNIPPET_SOURCE_SETTINGS[this.source];
+ },
+ configurationPath() {
+ return this.configurationPaths[this.source];
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-alert
+ variant="tip"
+ :title="__('Code snippet copied. Insert it in the correct location in the YAML file.')"
+ :dismiss-label="__('Dismiss')"
+ :primary-button-link="settings.docsPath"
+ :primary-button-text="__('Read documentation')"
+ :secondary-button-link="configurationPath"
+ :secondary-button-text="__('Go back to configuration')"
+ v-on="$listeners"
+ >
+ {{ __('Before inserting code, be sure to read the comment that separated each code group.') }}
+ </gl-alert>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/constants.js b/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/constants.js
new file mode 100644
index 00000000000..582fdfea6c9
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/code_snippet_alert/constants.js
@@ -0,0 +1,11 @@
+import { helpPagePath } from '~/helpers/help_page_helper';
+
+export const CODE_SNIPPET_SOURCE_URL_PARAM = 'code_snippet_copied_from';
+export const CODE_SNIPPET_SOURCE_API_FUZZING = 'api_fuzzing';
+export const CODE_SNIPPET_SOURCES = [CODE_SNIPPET_SOURCE_API_FUZZING];
+export const CODE_SNIPPET_SOURCE_SETTINGS = {
+ [CODE_SNIPPET_SOURCE_API_FUZZING]: {
+ datasetKey: 'apiFuzzingConfigurationPath',
+ docsPath: helpPagePath('user/application_security/api_fuzzing/index'),
+ },
+};
diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
index b088678fee8..f6e88738002 100644
--- a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
+++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
@@ -124,7 +124,7 @@ export default {
type="submit"
class="js-no-auto-disable"
category="primary"
- variant="success"
+ variant="confirm"
:disabled="submitDisabled"
:loading="isSaving"
>
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue b/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
index f36b22f33c3..455990f2791 100644
--- a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
+++ b/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
@@ -1,22 +1,15 @@
<script>
-import { GlAlert, GlIcon } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
import { uniqueId } from 'lodash';
-import { __, s__ } from '~/locale';
-import { CI_CONFIG_STATUS_INVALID } from '~/pipeline_editor/constants';
-import { DEFAULT, INVALID_CI_CONFIG } from '~/pipelines/constants';
+import { s__ } from '~/locale';
import EditorLite from '~/vue_shared/components/editor_lite.vue';
export default {
i18n: {
viewOnlyMessage: s__('Pipelines|Merged YAML is view only'),
},
- errorTexts: {
- [INVALID_CI_CONFIG]: __('Your CI configuration file is invalid.'),
- [DEFAULT]: __('An unknown error occurred.'),
- },
components: {
EditorLite,
- GlAlert,
GlIcon,
},
inject: ['ciConfigPath'],
@@ -32,69 +25,30 @@ export default {
};
},
computed: {
- failure() {
- switch (this.failureType) {
- case INVALID_CI_CONFIG:
- return this.$options.errorTexts[INVALID_CI_CONFIG];
- default:
- return this.$options.errorTexts[DEFAULT];
- }
- },
fileGlobalId() {
return `${this.ciConfigPath}-${uniqueId()}`;
},
- hasError() {
- return this.failureType;
- },
- isInvalidConfiguration() {
- return this.ciConfigData.status === CI_CONFIG_STATUS_INVALID;
- },
mergedYaml() {
return this.ciConfigData.mergedYaml;
},
},
- watch: {
- ciConfigData: {
- immediate: true,
- handler() {
- if (this.isInvalidConfiguration) {
- this.reportFailure(INVALID_CI_CONFIG);
- } else if (this.hasError) {
- this.resetFailure();
- }
- },
- },
- },
- methods: {
- reportFailure(errorType) {
- this.failureType = errorType;
- },
- resetFailure() {
- this.failureType = null;
- },
- },
};
</script>
<template>
<div>
- <gl-alert v-if="hasError" variant="danger" :dismissible="false">
- {{ failure }}
- </gl-alert>
- <div v-else>
- <div class="gl-display-flex gl-align-items-center">
- <gl-icon :size="18" name="lock" use-deprecated-sizes class="gl-text-gray-500 gl-mr-3" />
- {{ $options.i18n.viewOnlyMessage }}
- </div>
- <div class="gl-mt-3 gl-border-solid gl-border-gray-100 gl-border-1">
- <editor-lite
- ref="editor"
- :value="mergedYaml"
- :file-name="ciConfigPath"
- :file-global-id="fileGlobalId"
- :editor-options="{ readOnly: true }"
- v-on="$listeners"
- />
- </div>
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-icon :size="16" name="lock" class="gl-text-gray-500 gl-mr-3" />
+ {{ $options.i18n.viewOnlyMessage }}
+ </div>
+ <div class="gl-mt-3 gl-border-solid gl-border-gray-100 gl-border-1">
+ <editor-lite
+ ref="editor"
+ :value="mergedYaml"
+ :file-name="ciConfigPath"
+ :file-global-id="fileGlobalId"
+ :editor-options="{ readOnly: true }"
+ v-on="$listeners"
+ />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue b/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
index 872da88d3e6..a3410d7b837 100644
--- a/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
+++ b/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
@@ -27,7 +27,7 @@ export default {
registerCiSchema() {
const editorInstance = this.$refs.editor.getEditor();
- editorInstance.use(new CiSchemaExtension());
+ editorInstance.use(new CiSchemaExtension({ instance: editorInstance }));
editorInstance.registerCiSchema({
projectPath: this.projectPath,
projectNamespace: this.projectNamespace,
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
new file mode 100644
index 00000000000..b3eba0fcc19
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
@@ -0,0 +1,65 @@
+<script>
+import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader, GlIcon } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { 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: {
+ title: s__('Branches'),
+ fetchError: s__('Unable to fetch branch list for this project.'),
+ },
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlIcon,
+ },
+ inject: ['projectFullPath'],
+ apollo: {
+ branches: {
+ query: getAvailableBranches,
+ variables() {
+ return {
+ projectFullPath: this.projectFullPath,
+ };
+ },
+ update(data) {
+ return data.project?.repository?.branches || [];
+ },
+ error() {
+ this.$emit('showError', {
+ type: DEFAULT_FAILURE,
+ reasons: [this.$options.i18n.fetchError],
+ });
+ },
+ },
+ currentBranch: {
+ query: getCurrentBranch,
+ },
+ },
+ computed: {
+ hasBranchList() {
+ return this.branches?.length > 0;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown v-if="hasBranchList" class="gl-ml-2" :text="currentBranch" icon="branch">
+ <gl-dropdown-section-header>
+ {{ this.$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-icon name="check" class="gl-visibility-hidden" />
+ {{ branch.name }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue b/app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue
new file mode 100644
index 00000000000..a945fc542a5
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue
@@ -0,0 +1,21 @@
+<script>
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import BranchSwitcher from './branch_switcher.vue';
+
+export default {
+ components: {
+ BranchSwitcher,
+ },
+ mixins: [glFeatureFlagsMixin()],
+ computed: {
+ showBranchSwitcher() {
+ return this.glFeatures.pipelineEditorBranchSwitcher;
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-mb-5">
+ <branch-switcher v-if="showBranchSwitcher" v-on="$listeners" />
+ </div>
+</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 7a35e31e9ce..fefa784f060 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
@@ -31,22 +31,18 @@ export default {
},
mixins: [glFeatureFlagsMixin()],
props: {
- ciFileContent: {
- type: String,
- required: true,
- },
ciConfigData: {
type: Object,
required: true,
},
- isCiConfigDataLoading: {
+ isNewCiConfigFile: {
type: Boolean,
required: true,
},
},
computed: {
showPipelineStatus() {
- return this.glFeatures.pipelineStatusForPipelineEditor;
+ return this.glFeatures.pipelineStatusForPipelineEditor && !this.isNewCiConfigFile;
},
// make sure corners are rounded correctly depending on if
// pipeline status is rendered
@@ -61,11 +57,6 @@ export default {
<template>
<div class="gl-mb-5">
<pipeline-status v-if="showPipelineStatus" :class="$options.pipelineStatusClasses" />
- <validation-segment
- :class="validationStyling"
- :loading="isCiConfigDataLoading"
- :ci-file-content="ciFileContent"
- :ci-config="ciConfigData"
- />
+ <validation-segment :class="validationStyling" :ci-config="ciConfigData" />
</div>
</template>
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 b1ea464be99..4a92e106da1 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
@@ -1,9 +1,11 @@
<script>
import { GlIcon, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+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 CiIcon from '~/vue_shared/components/ci_icon.vue';
const POLL_INTERVAL = 10000;
@@ -38,13 +40,11 @@ export default {
};
},
update: (data) => {
- const { id, commitPath = '', shortSha = '', detailedStatus = {} } =
- data.project?.pipeline || {};
+ const { id, commitPath = '', detailedStatus = {} } = data.project?.pipeline || {};
return {
id,
commitPath,
- shortSha,
detailedStatus,
};
},
@@ -61,24 +61,34 @@ export default {
},
computed: {
hasPipelineData() {
- return Boolean(this.$apollo.queries.pipeline?.id);
+ return Boolean(this.pipeline?.id);
},
- isQueryLoading() {
- return this.$apollo.queries.pipeline.loading && !this.hasPipelineData;
+ pipelineId() {
+ return getIdFromGraphQLId(this.pipeline.id);
+ },
+ showLoadingState() {
+ // the query is set to poll regularly, so if there is no pipeline data
+ // (e.g. pipeline is null during fetch when the pipeline hasn't been
+ // triggered yet), we can just show the loading state until the pipeline
+ // details are ready to be fetched
+ return this.$apollo.queries.pipeline.loading || (!this.hasPipelineData && !this.hasError);
+ },
+ shortSha() {
+ return truncateSha(this.commitSha);
},
status() {
return this.pipeline.detailedStatus;
},
- pipelineId() {
- return getIdFromGraphQLId(this.pipeline.id);
- },
+ },
+ mounted() {
+ toggleQueryPollingByVisibility(this.$apollo.queries.pipeline, POLL_INTERVAL);
},
};
</script>
<template>
<div class="gl-white-space-nowrap gl-max-w-full">
- <template v-if="isQueryLoading">
+ <template v-if="showLoadingState">
<gl-loading-icon class="gl-mr-auto gl-display-inline-block" size="sm" />
<span data-testid="pipeline-loading-msg">{{ $options.i18n.fetchLoading }}</span>
</template>
@@ -88,7 +98,7 @@ export default {
</template>
<template v-else>
<a :href="status.detailsPath" class="gl-mr-auto">
- <ci-icon :status="status" :size="18" />
+ <ci-icon :status="status" :size="16" />
</a>
<span class="gl-font-weight-bold">
<gl-sprintf :message="$options.i18n.pipelineInfo">
@@ -110,7 +120,7 @@ export default {
target="_blank"
data-testid="pipeline-commit"
>
- {{ pipeline.shortSha }}
+ {{ shortSha }}
</gl-link>
</template>
</gl-sprintf>
diff --git a/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue b/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue
index 541ab74b177..d1534655a00 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue
@@ -1,8 +1,13 @@
<script>
import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
+import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.graphql';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
-import { CI_CONFIG_STATUS_VALID } from '../../constants';
+import {
+ EDITOR_APP_STATUS_EMPTY,
+ EDITOR_APP_STATUS_LOADING,
+ EDITOR_APP_STATUS_VALID,
+} from '../../constants';
export const i18n = {
empty: __(
@@ -29,47 +34,51 @@ export default {
},
},
props: {
- ciFileContent: {
- type: String,
- required: true,
- },
ciConfig: {
type: Object,
required: false,
default: () => ({}),
},
- loading: {
- type: Boolean,
- required: false,
- default: false,
+ },
+ apollo: {
+ appStatus: {
+ query: getAppStatus,
},
},
computed: {
isEmpty() {
- return !this.ciFileContent;
+ return this.appStatus === EDITOR_APP_STATUS_EMPTY;
+ },
+ isLoading() {
+ return this.appStatus === EDITOR_APP_STATUS_LOADING;
},
isValid() {
- return this.ciConfig?.status === CI_CONFIG_STATUS_VALID;
+ return this.appStatus === EDITOR_APP_STATUS_VALID;
},
icon() {
- if (this.isValid || this.isEmpty) {
- return 'check';
+ switch (this.appStatus) {
+ case EDITOR_APP_STATUS_EMPTY:
+ return 'check';
+ case EDITOR_APP_STATUS_VALID:
+ return 'check';
+ default:
+ return 'warning-solid';
}
- return 'warning-solid';
},
message() {
- if (this.isEmpty) {
- return this.$options.i18n.empty;
- } else if (this.isValid) {
- return this.$options.i18n.valid;
- }
-
- // Only display first error as a reason
const [reason] = this.ciConfig?.errors || [];
- if (reason) {
- return sprintf(this.$options.i18n.invalidWithReason, { reason }, false);
+
+ switch (this.appStatus) {
+ case EDITOR_APP_STATUS_EMPTY:
+ return this.$options.i18n.empty;
+ case EDITOR_APP_STATUS_VALID:
+ return this.$options.i18n.valid;
+ default:
+ // Only display first error as a reason
+ return this.ciConfig?.errors.length > 0
+ ? sprintf(this.$options.i18n.invalidWithReason, { reason }, false)
+ : this.$options.i18n.invalid;
}
- return this.$options.i18n.invalid;
},
},
};
@@ -77,7 +86,7 @@ export default {
<template>
<div>
- <template v-if="loading">
+ <template v-if="isLoading">
<gl-loading-icon inline />
{{ $options.i18n.loading }}
</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue
index b27ab9a39d3..f1cf5630fbf 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue
@@ -1,6 +1,5 @@
<script>
import { flatten } from 'lodash';
-import { CI_CONFIG_STATUS_VALID } from '../../constants';
import CiLintResults from './ci_lint_results.vue';
export default {
@@ -13,15 +12,16 @@ export default {
},
},
props: {
+ isValid: {
+ type: Boolean,
+ required: true,
+ },
ciConfig: {
type: Object,
required: true,
},
},
computed: {
- isValid() {
- return this.ciConfig?.status === CI_CONFIG_STATUS_VALID;
- },
stages() {
return this.ciConfig?.stages || [];
},
@@ -45,9 +45,9 @@ export default {
<template>
<ci-lint-results
- :valid="isValid"
- :jobs="jobs"
:errors="ciConfig.errors"
+ :is-valid="isValid"
+ :jobs="jobs"
:lint-help-page-path="lintHelpPagePath"
/>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue
index 5d9697c9427..7f6dce05b6e 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue
@@ -42,34 +42,34 @@ export default {
CiLintResultsParam,
},
props: {
- valid: {
- type: Boolean,
- required: true,
- },
- jobs: {
- type: Array,
- required: false,
- default: () => [],
- },
errors: {
type: Array,
required: false,
default: () => [],
},
- warnings: {
- type: Array,
- required: false,
- default: () => [],
- },
dryRun: {
type: Boolean,
required: false,
default: false,
},
+ isValid: {
+ type: Boolean,
+ required: true,
+ },
+ jobs: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
lintHelpPagePath: {
type: String,
required: true,
},
+ warnings: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -78,7 +78,7 @@ export default {
},
computed: {
status() {
- return this.valid ? this.$options.correct : this.$options.incorrect;
+ return this.isValid ? this.$options.correct : this.$options.incorrect;
},
shouldShowTable() {
return this.errors.length === 0;
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 3bdcf383bee..5acb3355b23 100644
--- a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
+++ b/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
@@ -1,15 +1,20 @@
<script>
-import { GlAlert, GlLoadingIcon, GlTabs, GlTab } from '@gitlab/ui';
+import { GlAlert, GlLoadingIcon, GlTabs } from '@gitlab/ui';
import { s__ } from '~/locale';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
- CI_CONFIG_STATUS_INVALID,
CREATE_TAB,
+ EDITOR_APP_STATUS_EMPTY,
+ EDITOR_APP_STATUS_ERROR,
+ EDITOR_APP_STATUS_INVALID,
+ EDITOR_APP_STATUS_LOADING,
+ EDITOR_APP_STATUS_VALID,
LINT_TAB,
MERGED_TAB,
VISUALIZE_TAB,
} from '../constants';
+import getAppStatus from '../graphql/queries/client/app_status.graphql';
import CiConfigMergedPreview from './editor/ci_config_merged_preview.vue';
import TextEditor from './editor/text_editor.vue';
import CiLint from './lint/ci_lint.vue';
@@ -21,6 +26,17 @@ export default {
tabGraph: s__('Pipelines|Visualize'),
tabLint: s__('Pipelines|Lint'),
tabMergedYaml: s__('Pipelines|View merged YAML'),
+ empty: {
+ visualization: s__(
+ 'PipelineEditor|The pipeline visualization is displayed when the CI/CD configuration file has valid syntax.',
+ ),
+ lint: s__(
+ 'PipelineEditor|The CI/CD configuration is continuously validated. Errors and warnings are displayed when the CI/CD configuration file is not empty.',
+ ),
+ merge: s__(
+ 'PipelineEditor|The merged YAML view is displayed when the CI/CD configuration file has valid syntax.',
+ ),
+ },
},
errorTexts: {
loadMergedYaml: s__('Pipelines|Could not load merged YAML content'),
@@ -37,7 +53,6 @@ export default {
EditorTab,
GlAlert,
GlLoadingIcon,
- GlTab,
GlTabs,
PipelineGraph,
TextEditor,
@@ -52,17 +67,28 @@ export default {
type: String,
required: true,
},
- isCiConfigDataLoading: {
- type: Boolean,
- required: false,
- default: false,
+ },
+ apollo: {
+ appStatus: {
+ query: getAppStatus,
},
},
computed: {
- hasMergedYamlLoadError() {
- return (
- !this.ciConfigData?.mergedYaml && this.ciConfigData.status !== CI_CONFIG_STATUS_INVALID
- );
+ hasAppError() {
+ // Not an invalid config and with `mergedYaml` data missing
+ return this.appStatus === EDITOR_APP_STATUS_ERROR;
+ },
+ isEmpty() {
+ return this.appStatus === EDITOR_APP_STATUS_EMPTY;
+ },
+ isInvalid() {
+ return this.appStatus === EDITOR_APP_STATUS_INVALID;
+ },
+ isValid() {
+ return this.appStatus === EDITOR_APP_STATUS_VALID;
+ },
+ isLoading() {
+ return this.appStatus === EDITOR_APP_STATUS_LOADING;
},
},
methods: {
@@ -83,39 +109,48 @@ export default {
>
<text-editor :value="ciFileContent" v-on="$listeners" />
</editor-tab>
- <gl-tab
+ <editor-tab
v-if="glFeatures.ciConfigVisualizationTab"
class="gl-mb-3"
+ :empty-message="$options.i18n.empty.visualization"
+ :is-empty="isEmpty"
+ :is-invalid="isInvalid"
:title="$options.i18n.tabGraph"
lazy
data-testid="visualization-tab"
@click="setCurrentTab($options.tabConstants.VISUALIZE_TAB)"
>
- <gl-loading-icon v-if="isCiConfigDataLoading" size="lg" class="gl-m-3" />
+ <gl-loading-icon v-if="isLoading" size="lg" class="gl-m-3" />
<pipeline-graph v-else :pipeline-data="ciConfigData" />
- </gl-tab>
+ </editor-tab>
<editor-tab
class="gl-mb-3"
+ :empty-message="$options.i18n.empty.lint"
+ :is-empty="isEmpty"
:title="$options.i18n.tabLint"
data-testid="lint-tab"
@click="setCurrentTab($options.tabConstants.LINT_TAB)"
>
- <gl-loading-icon v-if="isCiConfigDataLoading" size="lg" class="gl-m-3" />
- <ci-lint v-else :ci-config="ciConfigData" />
+ <gl-loading-icon v-if="isLoading" size="lg" class="gl-m-3" />
+ <ci-lint v-else :is-valid="isValid" :ci-config="ciConfigData" />
</editor-tab>
- <gl-tab
+ <editor-tab
v-if="glFeatures.ciConfigMergedTab"
class="gl-mb-3"
+ :empty-message="$options.i18n.empty.merge"
+ :keep-component-mounted="false"
+ :is-empty="isEmpty"
+ :is-invalid="isInvalid"
:title="$options.i18n.tabMergedYaml"
lazy
data-testid="merged-tab"
@click="setCurrentTab($options.tabConstants.MERGED_TAB)"
>
- <gl-loading-icon v-if="isCiConfigDataLoading" size="lg" class="gl-m-3" />
- <gl-alert v-else-if="hasMergedYamlLoadError" variant="danger" :dismissible="false">
+ <gl-loading-icon v-if="isLoading" size="lg" class="gl-m-3" />
+ <gl-alert v-else-if="hasAppError" variant="danger" :dismissible="false">
{{ $options.errorTexts.loadMergedYaml }}
</gl-alert>
<ci-config-merged-preview v-else :ci-config-data="ciConfigData" v-on="$listeners" />
- </gl-tab>
+ </editor-tab>
</gl-tabs>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue b/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
index b0acd3ca2ee..7c032441a04 100644
--- a/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
+++ b/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
@@ -1,6 +1,6 @@
<script>
-import { GlTab } from '@gitlab/ui';
-
+import { GlAlert, GlTab } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
/**
* Wrapper of <gl-tab> to optionally lazily render this tab's content
* when its shown **without dismounting after its hidden**.
@@ -10,10 +10,10 @@ import { GlTab } from '@gitlab/ui';
* API is the same as <gl-tab>, for example:
*
* <gl-tabs>
- * <editor-tab title="Tab 1" :lazy="true">
+ * <editor-tab title="Tab 1" lazy>
* lazily mounted content (gets mounted if this is first tab)
* </editor-tab>
- * <editor-tab title="Tab 2" :lazy="true">
+ * <editor-tab title="Tab 2" lazy>
* lazily mounted content
* </editor-tab>
* <editor-tab title="Tab 3">
@@ -25,10 +25,26 @@ import { GlTab } from '@gitlab/ui';
* so it's contents are not dismounted.
*
* lazy is "false" by default, as in <gl-tab>.
+ *
+ * It is also possible to pass the `isEmpty` and or `isInvalid` to let
+ * the tab component handle that state on its own. For example:
+ *
+ * * <gl-tabs>
+ * <editor-tab-with-status title="Tab 1" :is-empty="isEmpty" :is-invalid="isInvalid">
+ * ...
+ * </editor-tab-with-status>
+ * Will be the same as normal, except it will only render the slot component
+ * if the status is not empty and not invalid. In any of these 2 cases, it will render
+ * a generic component and avoid mounting whatever it received in the slot.
+ * </gl-tabs>
*/
export default {
+ i18n: {
+ invalid: __('Your CI/CD configuration syntax is invalid. View Lint tab for more details.'),
+ },
components: {
+ GlAlert,
GlTab,
// Use a small renderless component to know when the tab content mounts because:
// - gl-tab always gets mounted, even if lazy is `true`. See:
@@ -40,29 +56,63 @@ export default {
},
inheritAttrs: false,
props: {
+ emptyMessage: {
+ type: String,
+ required: false,
+ default: s__(
+ 'PipelineEditor|This tab will be usable when the CI/CD configuration file is populated with valid syntax.',
+ ),
+ },
+ isEmpty: {
+ type: Boolean,
+ required: false,
+ default: null,
+ },
+ isInvalid: {
+ type: Boolean,
+ required: false,
+ default: null,
+ },
lazy: {
type: Boolean,
required: false,
default: false,
},
+ keepComponentMounted: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
data() {
return {
isLazy: this.lazy,
};
},
+ computed: {
+ slots() {
+ return Object.keys(this.$slots);
+ },
+ },
methods: {
onContentMounted() {
// When a child is first mounted make the entire tab
- // permanently mounted by setting 'lazy' to false.
- this.isLazy = false;
+ // permanently mounted by setting 'lazy' to false unless
+ // explicitly opted out.
+ if (this.keepComponentMounted) {
+ this.isLazy = false;
+ }
},
},
};
</script>
<template>
<gl-tab :lazy="isLazy" v-bind="$attrs" v-on="$listeners">
- <slot v-for="slot in Object.keys($slots)" :slot="slot" :name="slot"></slot>
- <mount-spy @hook:mounted="onContentMounted" />
+ <gl-alert v-if="isEmpty" variant="tip">{{ emptyMessage }}</gl-alert>
+ <gl-alert v-else-if="isInvalid" variant="danger">{{ $options.i18n.invalid }}</gl-alert>
+ <template v-else>
+ <slot v-for="slot in slots" :name="slot"></slot>
+ <mount-spy @hook:mounted="onContentMounted" />
+ </template>
</gl-tab>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/constants.js b/app/assets/javascripts/pipeline_editor/constants.js
index 353deafe770..8d0ec6c3e2d 100644
--- a/app/assets/javascripts/pipeline_editor/constants.js
+++ b/app/assets/javascripts/pipeline_editor/constants.js
@@ -1,5 +1,14 @@
-export const CI_CONFIG_STATUS_VALID = 'VALID';
+// Values for CI_CONFIG_STATUS_* comes from lint graphQL
export const CI_CONFIG_STATUS_INVALID = 'INVALID';
+export const CI_CONFIG_STATUS_VALID = 'VALID';
+
+// Values for EDITOR_APP_STATUS_* are frontend specifics and
+// represent the global state of the pipeline editor app.
+export const EDITOR_APP_STATUS_EMPTY = 'EMPTY';
+export const EDITOR_APP_STATUS_ERROR = 'ERROR';
+export const EDITOR_APP_STATUS_INVALID = CI_CONFIG_STATUS_INVALID;
+export const EDITOR_APP_STATUS_LOADING = 'LOADING';
+export const EDITOR_APP_STATUS_VALID = CI_CONFIG_STATUS_VALID;
export const COMMIT_FAILURE = 'COMMIT_FAILURE';
export const COMMIT_SUCCESS = 'COMMIT_SUCCESS';
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql
new file mode 100644
index 00000000000..f162bb11d47
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/available_branches.graphql
@@ -0,0 +1,9 @@
+query getAvailableBranches($projectFullPath: ID!) {
+ project(fullPath: $projectFullPath) @client {
+ repository {
+ branches {
+ name
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/app_status.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/client/app_status.graphql
new file mode 100644
index 00000000000..938f36c7d5c
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/client/app_status.graphql
@@ -0,0 +1,3 @@
+query getAppStatus {
+ appStatus @client
+}
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql
index 7cc7f92fb60..d3a7387ad2d 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql
@@ -1,10 +1,9 @@
query getPipeline($fullPath: ID!, $sha: String!) {
- project(fullPath: $fullPath) @client {
+ project(fullPath: $fullPath) {
pipeline(sha: $sha) {
commitPath
id
iid
- shortSha
status
detailedStatus {
detailsPath
diff --git a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
index 13f6200693b..caa2a65d424 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
+++ b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
@@ -11,25 +11,19 @@ export const resolvers = {
}),
};
},
-
/* eslint-disable @gitlab/require-i18n-strings */
project() {
return {
__typename: 'Project',
- pipeline: {
- __typename: 'Pipeline',
- commitPath: `/-/commit/aabbccdd`,
- id: 'gid://gitlab/Ci::Pipeline/118',
- iid: '28',
- shortSha: 'aabbccdd',
- status: 'SUCCESS',
- detailedStatus: {
- __typename: 'DetailedStatus',
- detailsPath: '/root/sample-ci-project/-/pipelines/118"',
- group: 'success',
- icon: 'status_success',
- text: 'passed',
- },
+ repository: {
+ __typename: 'Repository',
+ branches: [
+ { __typename: 'Branch', name: 'master' },
+ { __typename: 'Branch', name: 'main' },
+ { __typename: 'Branch', name: 'develop' },
+ { __typename: 'Branch', name: 'production' },
+ { __typename: 'Branch', name: 'test' },
+ ],
},
};
},
diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/pipeline_editor/index.js
index b17ec2d5c25..8a1e26f9bff 100644
--- a/app/assets/javascripts/pipeline_editor/index.js
+++ b/app/assets/javascripts/pipeline_editor/index.js
@@ -3,6 +3,9 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
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 { resolvers } from './graphql/resolvers';
import typeDefs from './graphql/typedefs.graphql';
import PipelineEditorApp from './pipeline_editor_app.vue';
@@ -35,15 +38,30 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
ymlHelpPagePath,
} = el?.dataset;
+ const configurationPaths = Object.fromEntries(
+ Object.entries(CODE_SNIPPET_SOURCE_SETTINGS).map(([source, { datasetKey }]) => [
+ source,
+ el.dataset[datasetKey],
+ ]),
+ );
+
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(resolvers, { typeDefs }),
});
+ const { cache } = apolloProvider.clients.defaultClient;
- apolloProvider.clients.defaultClient.cache.writeData({
+ cache.writeQuery({
+ query: getCurrentBranch,
data: {
currentBranch: initialBranchName || defaultBranch,
+ },
+ });
+
+ cache.writeQuery({
+ query: getCommitSha,
+ data: {
commitSha,
},
});
@@ -61,6 +79,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
projectPath,
projectNamespace,
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 c1168979e9f..e0fb38004ec 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
@@ -1,14 +1,29 @@
<script>
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import httpStatusCodes from '~/lib/utils/http_status';
+import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
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 { COMMIT_FAILURE, COMMIT_SUCCESS, DEFAULT_FAILURE, LOAD_FAILURE_UNKNOWN } from './constants';
+import {
+ COMMIT_FAILURE,
+ COMMIT_SUCCESS,
+ DEFAULT_FAILURE,
+ EDITOR_APP_STATUS_EMPTY,
+ EDITOR_APP_STATUS_ERROR,
+ EDITOR_APP_STATUS_LOADING,
+ LOAD_FAILURE_UNKNOWN,
+} from './constants';
import getBlobContent from './graphql/queries/blob_content.graphql';
import getCiConfigData from './graphql/queries/ci_config.graphql';
+import getAppStatus from './graphql/queries/client/app_status.graphql';
import getCurrentBranch from './graphql/queries/client/current_branch.graphql';
import getIsNewCiConfigFile from './graphql/queries/client/is_new_ci_config_file.graphql';
import PipelineEditorHome from './pipeline_editor_home.vue';
@@ -20,6 +35,7 @@ export default {
GlLoadingIcon,
PipelineEditorEmptyState,
PipelineEditorHome,
+ CodeSnippetAlert,
},
inject: {
ciConfigPath: {
@@ -32,7 +48,6 @@ export default {
data() {
return {
ciConfigData: {},
- // Success and failure state
failureType: null,
failureReasons: [],
showStartScreen: false,
@@ -43,8 +58,10 @@ export default {
showFailureAlert: false,
showSuccessAlert: false,
successType: null,
+ codeSnippetCopiedFrom: '',
};
},
+
apollo: {
initialCiFileContent: {
query: getBlobContent,
@@ -77,8 +94,7 @@ export default {
},
ciConfigData: {
query: getCiConfigData,
- // If content is not loaded, we can't lint the data
- skip: ({ currentCiFileContent }) => {
+ skip({ currentCiFileContent }) {
return !currentCiFileContent;
},
variables() {
@@ -94,9 +110,20 @@ export default {
return { ...ciConfig, stages };
},
+ result({ data }) {
+ this.setAppStatus(data?.ciConfig?.status || EDITOR_APP_STATUS_ERROR);
+ },
error() {
this.reportFailure(LOAD_FAILURE_UNKNOWN);
},
+ watchLoading(isLoading) {
+ if (isLoading) {
+ this.setAppStatus(EDITOR_APP_STATUS_LOADING);
+ }
+ },
+ },
+ appStatus: {
+ query: getAppStatus,
},
currentBranch: {
query: getCurrentBranch,
@@ -115,6 +142,9 @@ export default {
isCiConfigDataLoading() {
return this.$apollo.queries.ciConfigData.loading;
},
+ isEmpty() {
+ return this.currentCiFileContent === '';
+ },
failure() {
switch (this.failureType) {
case LOAD_FAILURE_UNKNOWN:
@@ -159,6 +189,16 @@ export default {
successTexts: {
[COMMIT_SUCCESS]: __('Your changes have been successfully committed.'),
},
+ watch: {
+ isEmpty(flag) {
+ if (flag) {
+ this.setAppStatus(EDITOR_APP_STATUS_EMPTY);
+ }
+ },
+ },
+ created() {
+ this.parseCodeSnippetSourceParam();
+ },
methods: {
handleBlobContentError(error = {}) {
const { networkError } = error;
@@ -170,6 +210,7 @@ export default {
response?.status === httpStatusCodes.NOT_FOUND ||
response?.status === httpStatusCodes.BAD_REQUEST
) {
+ this.setAppStatus(EDITOR_APP_STATUS_EMPTY);
this.showStartScreen = true;
} else {
this.reportFailure(LOAD_FAILURE_UNKNOWN);
@@ -183,6 +224,8 @@ export default {
this.showSuccessAlert = false;
},
reportFailure(type, reasons = []) {
+ this.setAppStatus(EDITOR_APP_STATUS_ERROR);
+
window.scrollTo({ top: 0, behavior: 'smooth' });
this.showFailureAlert = true;
this.failureType = type;
@@ -196,6 +239,9 @@ export default {
resetContent() {
this.currentCiFileContent = this.lastCommittedContent;
},
+ setAppStatus(appStatus) {
+ this.$apollo.getClient().writeQuery({ query: getAppStatus, data: { appStatus } });
+ },
setNewEmptyCiConfigFile() {
this.$apollo
.getClient()
@@ -220,6 +266,20 @@ 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>
@@ -232,19 +292,35 @@ export default {
@createEmptyConfigFile="setNewEmptyCiConfigFile"
/>
<div v-else>
- <gl-alert v-if="showSuccessAlert" :variant="success.variant" @dismiss="dismissSuccess">
+ <code-snippet-alert
+ v-if="codeSnippetCopiedFrom"
+ :source="codeSnippetCopiedFrom"
+ class="gl-mb-5"
+ @dismiss="dismissCodeSnippetAlert"
+ />
+ <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" @dismiss="dismissFailure">
+ <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
- :is-ci-config-data-loading="isCiConfigDataLoading"
:ci-config-data="ciConfigData"
:ci-file-content="currentCiFileContent"
+ :is-new-ci-config-file="isNewCiConfigFile"
@commit="updateOnCommit"
@resetContent="resetContent"
@showError="showErrorAlert"
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
index ef46040153f..adba55f9f4b 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
@@ -1,5 +1,6 @@
<script>
import CommitSection from './components/commit/commit_section.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';
import { TABS_WITH_COMMIT_FORM, CREATE_TAB } from './constants';
@@ -7,6 +8,7 @@ import { TABS_WITH_COMMIT_FORM, CREATE_TAB } from './constants';
export default {
components: {
CommitSection,
+ PipelineEditorFileNav,
PipelineEditorHeader,
PipelineEditorTabs,
},
@@ -19,7 +21,7 @@ export default {
type: String,
required: true,
},
- isCiConfigDataLoading: {
+ isNewCiConfigFile: {
type: Boolean,
required: true,
},
@@ -44,15 +46,14 @@ export default {
<template>
<div>
+ <pipeline-editor-file-nav v-on="$listeners" />
<pipeline-editor-header
- :ci-file-content="ciFileContent"
:ci-config-data="ciConfigData"
- :is-ci-config-data-loading="isCiConfigDataLoading"
+ :is-new-ci-config-file="isNewCiConfigFile"
/>
<pipeline-editor-tabs
:ci-config-data="ciConfigData"
:ci-file-content="ciFileContent"
- :is-ci-config-data-loading="isCiConfigDataLoading"
v-on="$listeners"
@set-current-tab="setCurrentTab"
/>