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/components')
-rw-r--r--app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue114
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue100
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue (renamed from app/assets/javascripts/pipeline_editor/components/text_editor.vue)30
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue30
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue (renamed from app/assets/javascripts/pipeline_editor/components/info/validation_segment.vue)2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue6
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue121
-rw-r--r--app/assets/javascripts/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue26
9 files changed, 414 insertions, 17 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
new file mode 100644
index 00000000000..b40c9a48903
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue
@@ -0,0 +1,114 @@
+<script>
+import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
+import { __, s__, sprintf } from '~/locale';
+import { COMMIT_FAILURE, COMMIT_SUCCESS } from '../../constants';
+import commitCIFile from '../../graphql/mutations/commit_ci_file.mutation.graphql';
+import getCommitSha from '../../graphql/queries/client/commit_sha.graphql';
+
+import CommitForm from './commit_form.vue';
+
+const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
+const MR_TARGET_BRANCH = 'merge_request[target_branch]';
+
+export default {
+ alertTexts: {
+ [COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
+ [COMMIT_SUCCESS]: __('Your changes have been successfully committed.'),
+ },
+ i18n: {
+ defaultCommitMessage: __('Update %{sourcePath} file'),
+ },
+ components: {
+ CommitForm,
+ },
+ inject: ['projectFullPath', 'ciConfigPath', 'defaultBranch', 'newMergeRequestPath'],
+ props: {
+ ciFileContent: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ commit: {},
+ isSaving: false,
+ };
+ },
+ apollo: {
+ commitSha: {
+ query: getCommitSha,
+ },
+ },
+ computed: {
+ defaultCommitMessage() {
+ return sprintf(this.$options.i18n.defaultCommitMessage, { sourcePath: this.ciConfigPath });
+ },
+ },
+ methods: {
+ redirectToNewMergeRequest(sourceBranch) {
+ const url = mergeUrlParams(
+ {
+ [MR_SOURCE_BRANCH]: sourceBranch,
+ [MR_TARGET_BRANCH]: this.defaultBranch,
+ },
+ this.newMergeRequestPath,
+ );
+ redirectTo(url);
+ },
+ async onCommitSubmit({ message, branch, openMergeRequest }) {
+ this.isSaving = true;
+
+ try {
+ const {
+ data: {
+ commitCreate: { errors },
+ },
+ } = await this.$apollo.mutate({
+ mutation: commitCIFile,
+ variables: {
+ projectPath: this.projectFullPath,
+ branch,
+ startBranch: this.defaultBranch,
+ message,
+ filePath: this.ciConfigPath,
+ content: this.ciFileContent,
+ lastCommitId: this.commitSha,
+ },
+ update(store, { data }) {
+ const commitSha = data?.commitCreate?.commit?.sha;
+
+ if (commitSha) {
+ store.writeQuery({ query: getCommitSha, data: { commitSha } });
+ }
+ },
+ });
+
+ if (errors?.length) {
+ this.$emit('showError', { type: COMMIT_FAILURE, reasons: errors });
+ } else if (openMergeRequest) {
+ this.redirectToNewMergeRequest(branch);
+ } else {
+ this.$emit('commit', { type: COMMIT_SUCCESS });
+ }
+ } catch (error) {
+ this.$emit('showError', { type: COMMIT_FAILURE, reasons: [error?.message] });
+ } finally {
+ this.isSaving = false;
+ }
+ },
+ onCommitCancel() {
+ this.$emit('resetContent');
+ },
+ },
+};
+</script>
+
+<template>
+ <commit-form
+ :default-branch="defaultBranch"
+ :default-message="defaultCommitMessage"
+ :is-saving="isSaving"
+ @cancel="onCommitCancel"
+ @submit="onCommitSubmit"
+ />
+</template>
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
new file mode 100644
index 00000000000..007faa4ed0d
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
@@ -0,0 +1,100 @@
+<script>
+import { GlAlert, 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 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'],
+ props: {
+ ciConfigData: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ failureType: null,
+ };
+ },
+ 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" 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>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/text_editor.vue b/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
index b8d49d77ea9..872da88d3e6 100644
--- a/app/assets/javascripts/pipeline_editor/components/text_editor.vue
+++ b/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
@@ -1,26 +1,30 @@
<script>
-import EditorLite from '~/vue_shared/components/editor_lite.vue';
+import { EDITOR_READY_EVENT } from '~/editor/constants';
import { CiSchemaExtension } from '~/editor/extensions/editor_ci_schema_ext';
+import EditorLite from '~/vue_shared/components/editor_lite.vue';
+import getCommitSha from '../../graphql/queries/client/commit_sha.graphql';
export default {
components: {
EditorLite,
},
- inject: ['projectPath', 'projectNamespace'],
+ inject: ['ciConfigPath', 'projectPath', 'projectNamespace'],
inheritAttrs: false,
- props: {
- ciConfigPath: {
- type: String,
- required: true,
- },
+ data() {
+ return {
+ commitSha: '',
+ };
+ },
+ apollo: {
commitSha: {
- type: String,
- required: false,
- default: null,
+ query: getCommitSha,
},
},
methods: {
- onEditorReady() {
+ onCiConfigUpdate(content) {
+ this.$emit('updateCiConfig', content);
+ },
+ registerCiSchema() {
const editorInstance = this.$refs.editor.getEditor();
editorInstance.use(new CiSchemaExtension());
@@ -31,6 +35,7 @@ export default {
});
},
},
+ readyEvent: EDITOR_READY_EVENT,
};
</script>
<template>
@@ -39,7 +44,8 @@ export default {
ref="editor"
:file-name="ciConfigPath"
v-bind="$attrs"
- @editor-ready="onEditorReady"
+ @[$options.readyEvent]="registerCiSchema"
+ @input="onCiConfigUpdate"
v-on="$listeners"
/>
</div>
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
new file mode 100644
index 00000000000..ab41c0170e9
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_header.vue
@@ -0,0 +1,30 @@
+<script>
+import ValidationSegment from './validation_segment.vue';
+
+export default {
+ validationSegmentClasses:
+ 'gl-p-5 gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base',
+ components: {
+ ValidationSegment,
+ },
+ props: {
+ ciConfigData: {
+ type: Object,
+ required: true,
+ },
+ isCiConfigDataLoading: {
+ type: Boolean,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-mb-5">
+ <validation-segment
+ :class="$options.validationSegmentClasses"
+ :loading="isCiConfigDataLoading"
+ :ci-config="ciConfigData"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/info/validation_segment.vue b/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue
index 22f378c571a..94fb3a66fdd 100644
--- a/app/assets/javascripts/pipeline_editor/components/info/validation_segment.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue
@@ -1,8 +1,8 @@
<script>
import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
-import { CI_CONFIG_STATUS_VALID } from '../../constants';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+import { CI_CONFIG_STATUS_VALID } from '../../constants';
export const i18n = {
learnMore: __('Learn more'),
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 58a96c3f725..5d9697c9427 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
@@ -1,9 +1,9 @@
<script>
import { GlAlert, GlLink, GlSprintf, GlTable } from '@gitlab/ui';
-import CiLintWarnings from './ci_lint_warnings.vue';
-import CiLintResultsValue from './ci_lint_results_value.vue';
-import CiLintResultsParam from './ci_lint_results_param.vue';
import { __ } from '~/locale';
+import CiLintResultsParam from './ci_lint_results_param.vue';
+import CiLintResultsValue from './ci_lint_results_value.vue';
+import CiLintWarnings from './ci_lint_warnings.vue';
const thBorderColor = 'gl-border-gray-100!';
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue
index 23808bcb292..49225a7cac7 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue
@@ -1,6 +1,6 @@
<script>
-import { __ } from '~/locale';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { __ } from '~/locale';
export default {
props: {
diff --git a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue b/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
new file mode 100644
index 00000000000..3bdcf383bee
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
@@ -0,0 +1,121 @@
+<script>
+import { GlAlert, GlLoadingIcon, GlTabs, GlTab } 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,
+ LINT_TAB,
+ MERGED_TAB,
+ VISUALIZE_TAB,
+} from '../constants';
+import CiConfigMergedPreview from './editor/ci_config_merged_preview.vue';
+import TextEditor from './editor/text_editor.vue';
+import CiLint from './lint/ci_lint.vue';
+import EditorTab from './ui/editor_tab.vue';
+
+export default {
+ i18n: {
+ tabEdit: s__('Pipelines|Write pipeline configuration'),
+ tabGraph: s__('Pipelines|Visualize'),
+ tabLint: s__('Pipelines|Lint'),
+ tabMergedYaml: s__('Pipelines|View merged YAML'),
+ },
+ errorTexts: {
+ loadMergedYaml: s__('Pipelines|Could not load merged YAML content'),
+ },
+ tabConstants: {
+ CREATE_TAB,
+ LINT_TAB,
+ MERGED_TAB,
+ VISUALIZE_TAB,
+ },
+ components: {
+ CiConfigMergedPreview,
+ CiLint,
+ EditorTab,
+ GlAlert,
+ GlLoadingIcon,
+ GlTab,
+ GlTabs,
+ PipelineGraph,
+ TextEditor,
+ },
+ mixins: [glFeatureFlagsMixin()],
+ props: {
+ ciConfigData: {
+ type: Object,
+ required: true,
+ },
+ ciFileContent: {
+ type: String,
+ required: true,
+ },
+ isCiConfigDataLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ hasMergedYamlLoadError() {
+ return (
+ !this.ciConfigData?.mergedYaml && this.ciConfigData.status !== CI_CONFIG_STATUS_INVALID
+ );
+ },
+ },
+ methods: {
+ setCurrentTab(tabName) {
+ this.$emit('set-current-tab', tabName);
+ },
+ },
+};
+</script>
+<template>
+ <gl-tabs class="file-editor gl-mb-3">
+ <editor-tab
+ class="gl-mb-3"
+ :title="$options.i18n.tabEdit"
+ lazy
+ data-testid="editor-tab"
+ @click="setCurrentTab($options.tabConstants.CREATE_TAB)"
+ >
+ <text-editor :value="ciFileContent" v-on="$listeners" />
+ </editor-tab>
+ <gl-tab
+ v-if="glFeatures.ciConfigVisualizationTab"
+ class="gl-mb-3"
+ :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" />
+ <pipeline-graph v-else :pipeline-data="ciConfigData" />
+ </gl-tab>
+ <editor-tab
+ class="gl-mb-3"
+ :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" />
+ </editor-tab>
+ <gl-tab
+ v-if="glFeatures.ciConfigMergedTab"
+ class="gl-mb-3"
+ :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">
+ {{ $options.errorTexts.loadMergedYaml }}
+ </gl-alert>
+ <ci-config-merged-preview v-else :ci-config-data="ciConfigData" v-on="$listeners" />
+ </gl-tab>
+ </gl-tabs>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue b/app/assets/javascripts/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue
new file mode 100644
index 00000000000..bc076fbe349
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/ui/confirm_unsaved_changes_dialog.vue
@@ -0,0 +1,26 @@
+<script>
+export default {
+ props: {
+ hasUnsavedChanges: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ created() {
+ window.addEventListener('beforeunload', this.confirmChanges);
+ },
+ destroyed() {
+ window.removeEventListener('beforeunload', this.confirmChanges);
+ },
+ methods: {
+ confirmChanges(e = {}) {
+ if (this.hasUnsavedChanges) {
+ e.preventDefault();
+ // eslint-disable-next-line no-param-reassign
+ e.returnValue = ''; // Chrome requires returnValue to be set
+ }
+ },
+ },
+ render: () => null,
+};
+</script>