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>2020-10-06 09:08:38 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-06 09:08:38 +0300
commita7cacc8293bdc9b088c7755a53efd304daab3a1c (patch)
tree8c4996a95c3932a85275c1136c47938976979d0c /app/assets/javascripts/ci_lint
parent5f4d224e546bdded3f9578e0ac7b30642009c9ab (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/ci_lint')
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint.vue98
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint_results.vue115
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint_results_param.vue26
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint_results_value.vue81
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint_warnings.vue69
-rw-r--r--app/assets/javascripts/ci_lint/graphql/mutations/lint_ci.mutation.graphql22
-rw-r--r--app/assets/javascripts/ci_lint/index.js47
7 files changed, 448 insertions, 10 deletions
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint.vue b/app/assets/javascripts/ci_lint/components/ci_lint.vue
index 135d02e4f76..871aaecd165 100644
--- a/app/assets/javascripts/ci_lint/components/ci_lint.vue
+++ b/app/assets/javascripts/ci_lint/components/ci_lint.vue
@@ -1,14 +1,106 @@
<script>
+import { GlButton, GlFormCheckbox, GlIcon, GlLink, GlAlert } from '@gitlab/ui';
+import CiLintResults from './ci_lint_results.vue';
+import lintCIMutation from '../graphql/mutations/lint_ci.mutation.graphql';
+
export default {
+ components: {
+ GlButton,
+ GlFormCheckbox,
+ GlIcon,
+ GlLink,
+ GlAlert,
+ CiLintResults,
+ },
props: {
endpoint: {
type: String,
required: true,
},
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ content: '',
+ valid: false,
+ errors: null,
+ warnings: null,
+ jobs: [],
+ dryRun: false,
+ showingResults: false,
+ apiError: null,
+ isErrorDismissed: false,
+ };
+ },
+ computed: {
+ shouldShowError() {
+ return this.apiError && !this.isErrorDismissed;
+ },
+ },
+ methods: {
+ async lint() {
+ try {
+ const {
+ data: {
+ lintCI: { valid, errors, warnings, jobs },
+ },
+ } = await this.$apollo.mutate({
+ mutation: lintCIMutation,
+ variables: { endpoint: this.endpoint, content: this.content, dry: this.dryRun },
+ });
+
+ this.showingResults = true;
+ this.valid = valid;
+ this.errors = errors;
+ this.warnings = warnings;
+ this.jobs = jobs;
+ } catch (error) {
+ this.apiError = error;
+ this.isErrorDismissed = false;
+ }
+ },
},
};
</script>
-<template
- ><div></div
-></template>
+<template>
+ <div class="row">
+ <div class="col-sm-12">
+ <gl-alert
+ v-if="shouldShowError"
+ class="gl-mb-3"
+ variant="danger"
+ @dismiss="isErrorDismissed = true"
+ >{{ apiError }}</gl-alert
+ >
+
+ <textarea v-model="content" cols="175" rows="20"></textarea>
+ </div>
+
+ <div class="col-sm-12 gl-display-flex gl-justify-content-space-between">
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-button class="gl-mr-4" category="primary" variant="success" @click="lint">{{
+ __('Validate')
+ }}</gl-button>
+ <gl-form-checkbox v-model="dryRun"
+ >{{ __('Simulate a pipeline created for the default branch') }}
+ <gl-link :href="helpPagePath" target="_blank"
+ ><gl-icon class="gl-text-blue-600" name="question-o"/></gl-link
+ ></gl-form-checkbox>
+ </div>
+ <gl-button>{{ __('Clear') }}</gl-button>
+ </div>
+
+ <ci-lint-results
+ v-if="showingResults"
+ :valid="valid"
+ :jobs="jobs"
+ :errors="errors"
+ :warnings="warnings"
+ :dry-run="dryRun"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_results.vue b/app/assets/javascripts/ci_lint/components/ci_lint_results.vue
index 9fd1bd30c49..28b2a028b29 100644
--- a/app/assets/javascripts/ci_lint/components/ci_lint_results.vue
+++ b/app/assets/javascripts/ci_lint/components/ci_lint_results.vue
@@ -1,9 +1,116 @@
<script>
+import { GlAlert, 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';
+
+const thBorderColor = 'gl-border-gray-100!';
+
export default {
- props: {},
+ correct: { variant: 'success', text: __('syntax is correct') },
+ incorrect: { variant: 'danger', text: __('syntax is incorrect') },
+ warningTitle: __('The form contains the following warning:'),
+ fields: [
+ {
+ key: 'parameter',
+ label: __('Parameter'),
+ thClass: thBorderColor,
+ },
+ {
+ key: 'value',
+ label: __('Value'),
+ thClass: thBorderColor,
+ },
+ ],
+ components: {
+ GlAlert,
+ GlTable,
+ CiLintWarnings,
+ CiLintResultsValue,
+ CiLintResultsParam,
+ },
+ props: {
+ valid: {
+ type: Boolean,
+ required: true,
+ },
+ jobs: {
+ type: Array,
+ required: true,
+ },
+ errors: {
+ type: Array,
+ required: true,
+ },
+ warnings: {
+ type: Array,
+ required: true,
+ },
+ dryRun: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isWarningDismissed: false,
+ };
+ },
+ computed: {
+ status() {
+ return this.valid ? this.$options.correct : this.$options.incorrect;
+ },
+ shouldShowTable() {
+ return this.errors.length === 0;
+ },
+ shouldShowError() {
+ return this.errors.length > 0;
+ },
+ shouldShowWarning() {
+ return this.warnings.length > 0 && !this.isWarningDismissed;
+ },
+ },
};
</script>
-<template
- ><div></div
-></template>
+<template>
+ <div class="col-sm-12 gl-mt-5">
+ <gl-alert
+ class="gl-mb-5"
+ :variant="status.variant"
+ :title="__('Status:')"
+ :dismissible="false"
+ data-testid="ci-lint-status"
+ >{{ status.text }}</gl-alert
+ >
+
+ <pre
+ v-if="shouldShowError"
+ class="gl-mb-5"
+ data-testid="ci-lint-errors"
+ ><div v-for="error in errors" :key="error">{{ error }}</div></pre>
+
+ <ci-lint-warnings
+ v-if="shouldShowWarning"
+ :warnings="warnings"
+ data-testid="ci-lint-warnings"
+ @dismiss="isWarningDismissed = true"
+ />
+
+ <gl-table
+ v-if="shouldShowTable"
+ :items="jobs"
+ :fields="$options.fields"
+ bordered
+ data-testid="ci-lint-table"
+ >
+ <template #cell(parameter)="{ item }">
+ <ci-lint-results-param :stage="item.stage" :job-name="item.name" />
+ </template>
+ <template #cell(value)="{ item }">
+ <ci-lint-results-value :item="item" :dry-run="dryRun" />
+ </template>
+ </gl-table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_results_param.vue b/app/assets/javascripts/ci_lint/components/ci_lint_results_param.vue
new file mode 100644
index 00000000000..23808bcb292
--- /dev/null
+++ b/app/assets/javascripts/ci_lint/components/ci_lint_results_param.vue
@@ -0,0 +1,26 @@
+<script>
+import { __ } from '~/locale';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+
+export default {
+ props: {
+ stage: {
+ type: String,
+ required: true,
+ },
+ jobName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ formatParameter() {
+ return __(`${capitalizeFirstCharacter(this.stage)} Job - ${this.jobName}`);
+ },
+ },
+};
+</script>
+
+<template>
+ <span data-testid="ci-lint-parameter">{{ formatParameter }}</span>
+</template>
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_results_value.vue b/app/assets/javascripts/ci_lint/components/ci_lint_results_value.vue
new file mode 100644
index 00000000000..4929c3206df
--- /dev/null
+++ b/app/assets/javascripts/ci_lint/components/ci_lint_results_value.vue
@@ -0,0 +1,81 @@
+<script>
+import { isEmpty } from 'lodash';
+
+export default {
+ props: {
+ item: {
+ type: Object,
+ required: true,
+ },
+ dryRun: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ tagList() {
+ return this.item.tagList.join(', ');
+ },
+ onlyPolicy() {
+ return this.item.only ? this.item.only.refs.join(', ') : this.item.only;
+ },
+ exceptPolicy() {
+ return this.item.except ? this.item.except.refs.join(', ') : this.item.except;
+ },
+ scripts() {
+ return {
+ beforeScript: {
+ show: !isEmpty(this.item.beforeScript),
+ content: this.item.beforeScript.join('\n'),
+ },
+ script: {
+ show: !isEmpty(this.item.script),
+ content: this.item.script.join('\n'),
+ },
+ afterScript: {
+ show: !isEmpty(this.item.afterScript),
+ content: this.item.afterScript.join('\n'),
+ },
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <pre v-if="scripts.beforeScript.show" data-testid="ci-lint-before-script">{{
+ scripts.beforeScript.content
+ }}</pre>
+ <pre v-if="scripts.script.show" data-testid="ci-lint-script">{{ scripts.script.content }}</pre>
+ <pre v-if="scripts.afterScript.show" data-testid="ci-lint-after-script">{{
+ scripts.afterScript.content
+ }}</pre>
+
+ <ul class="gl-list-style-none gl-pl-0 gl-mb-0">
+ <li>
+ <b>{{ __('Tag list:') }}</b>
+ {{ tagList }}
+ </li>
+ <div v-if="!dryRun" data-testid="ci-lint-only-except">
+ <li>
+ <b>{{ __('Only policy:') }}</b>
+ {{ onlyPolicy }}
+ </li>
+ <li>
+ <b>{{ __('Except policy:') }}</b>
+ {{ exceptPolicy }}
+ </li>
+ </div>
+ <li>
+ <b>{{ __('Environment:') }}</b>
+ {{ item.environment }}
+ </li>
+ <li>
+ <b>{{ __('When:') }}</b>
+ {{ item.when }}
+ <b v-if="item.allowFailure">{{ __('Allowed to fail') }}</b>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_warnings.vue b/app/assets/javascripts/ci_lint/components/ci_lint_warnings.vue
new file mode 100644
index 00000000000..ac0332cb0bd
--- /dev/null
+++ b/app/assets/javascripts/ci_lint/components/ci_lint_warnings.vue
@@ -0,0 +1,69 @@
+<script>
+import { GlAlert, GlSprintf } from '@gitlab/ui';
+import { __, n__ } from '~/locale';
+
+export default {
+ maxWarningsSummary: __('%{total} warnings found: showing first %{warningsDisplayed}'),
+ components: {
+ GlAlert,
+ GlSprintf,
+ },
+ props: {
+ warnings: {
+ type: Array,
+ required: true,
+ },
+ maxWarnings: {
+ type: Number,
+ required: false,
+ default: 25,
+ },
+ title: {
+ type: String,
+ required: false,
+ default: __('The form contains the following warning:'),
+ },
+ },
+ computed: {
+ totalWarnings() {
+ return this.warnings.length;
+ },
+ overMaxWarningsLimit() {
+ return this.totalWarnings > this.maxWarnings;
+ },
+ warningsSummary() {
+ return n__('%d warning found:', '%d warnings found:', this.totalWarnings);
+ },
+ summaryMessage() {
+ return this.overMaxWarningsLimit ? this.$options.maxWarningsSummary : this.warningsSummary;
+ },
+ limitWarnings() {
+ return this.warnings.slice(0, this.maxWarnings);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-alert class="gl-mb-4" :title="title" variant="warning" @dismiss="$emit('dismiss')">
+ <details>
+ <summary>
+ <gl-sprintf :message="summaryMessage">
+ <template #total>
+ {{ totalWarnings }}
+ </template>
+ <template #warningsDisplayed>
+ {{ maxWarnings }}
+ </template>
+ </gl-sprintf>
+ </summary>
+ <p
+ v-for="(warning, index) in limitWarnings"
+ :key="`warning-${index}`"
+ data-testid="ci-lint-warning"
+ >
+ {{ warning }}
+ </p>
+ </details>
+ </gl-alert>
+</template>
diff --git a/app/assets/javascripts/ci_lint/graphql/mutations/lint_ci.mutation.graphql b/app/assets/javascripts/ci_lint/graphql/mutations/lint_ci.mutation.graphql
new file mode 100644
index 00000000000..496036f690f
--- /dev/null
+++ b/app/assets/javascripts/ci_lint/graphql/mutations/lint_ci.mutation.graphql
@@ -0,0 +1,22 @@
+mutation lintCI($endpoint: String, $content: String, $dry: Boolean) {
+ lintCI(endpoint: $endpoint, content: $content, dry_run: $dry) @client {
+ valid
+ errors
+ warnings
+ jobs {
+ afterScript
+ allowFailure
+ beforeScript
+ environment
+ except
+ name
+ only {
+ refs
+ }
+ afterScript
+ stage
+ tagList
+ when
+ }
+ }
+}
diff --git a/app/assets/javascripts/ci_lint/index.js b/app/assets/javascripts/ci_lint/index.js
index ed2cf1fe714..c41e6d47d75 100644
--- a/app/assets/javascripts/ci_lint/index.js
+++ b/app/assets/javascripts/ci_lint/index.js
@@ -1,16 +1,57 @@
import Vue from 'vue';
-import CILint from './components/ci_lint.vue';
+import VueApollo from 'vue-apollo';
+import axios from '~/lib/utils/axios_utils';
+import createDefaultClient from '~/lib/graphql';
+import CiLint from './components/ci_lint.vue';
+
+Vue.use(VueApollo);
+
+const resolvers = {
+ Mutation: {
+ lintCI: (_, { endpoint, content, dry_run }) => {
+ return axios.post(endpoint, { content, dry_run }).then(({ data }) => ({
+ valid: data.valid,
+ errors: data.errors,
+ warnings: data.warnings,
+ jobs: data.jobs.map(job => ({
+ name: job.name,
+ stage: job.stage,
+ beforeScript: job.before_script,
+ script: job.script,
+ afterScript: job.after_script,
+ tagList: job.tag_list,
+ environment: job.environment,
+ when: job.when,
+ allowFailure: job.allow_failure,
+ only: {
+ refs: job.only.refs,
+ __typename: 'CiLintJobOnlyPolicy',
+ },
+ except: job.except,
+ __typename: 'CiLintJob',
+ })),
+ __typename: 'CiLintContent',
+ }));
+ },
+ },
+};
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(resolvers),
+});
export default (containerId = '#js-ci-lint') => {
const containerEl = document.querySelector(containerId);
- const { endpoint } = containerEl.dataset;
+ const { endpoint, helpPagePath } = containerEl.dataset;
return new Vue({
el: containerEl,
+ apolloProvider,
render(createElement) {
- return createElement(CILint, {
+ return createElement(CiLint, {
props: {
endpoint,
+ helpPagePath,
},
});
},