summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue168
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue32
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue130
-rw-r--r--app/assets/javascripts/ci_variable_list/constants.js2
-rw-r--r--app/assets/javascripts/ci_variable_list/index.js25
-rw-r--r--app/assets/javascripts/ci_variable_list/store/utils.js7
-rw-r--r--app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js23
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js23
8 files changed, 389 insertions, 21 deletions
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
new file mode 100644
index 00000000000..3e58fc40755
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
@@ -0,0 +1,168 @@
+<script>
+import { __ } from '~/locale';
+import { mapActions, mapState } from 'vuex';
+import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
+import {
+ GlModal,
+ GlFormSelect,
+ GlFormGroup,
+ GlFormInput,
+ GlFormCheckbox,
+ GlLink,
+ GlIcon,
+} from '@gitlab/ui';
+
+export default {
+ modalId: ADD_CI_VARIABLE_MODAL_ID,
+ components: {
+ GlModal,
+ GlFormSelect,
+ GlFormGroup,
+ GlFormInput,
+ GlFormCheckbox,
+ GlLink,
+ GlIcon,
+ },
+ computed: {
+ ...mapState([
+ 'projectId',
+ 'environments',
+ 'typeOptions',
+ 'variable',
+ 'variableBeingEdited',
+ 'isGroup',
+ 'maskableRegex',
+ ]),
+ canSubmit() {
+ return this.variableData.key !== '' && this.variableData.secret_value !== '';
+ },
+ canMask() {
+ const regex = RegExp(this.maskableRegex);
+ return regex.test(this.variableData.secret_value);
+ },
+ variableData() {
+ return this.variableBeingEdited || this.variable;
+ },
+ modalActionText() {
+ return this.variableBeingEdited ? __('Update Variable') : __('Add variable');
+ },
+ primaryAction() {
+ return {
+ text: this.modalActionText,
+ attributes: { variant: 'success', disabled: !this.canSubmit },
+ };
+ },
+ cancelAction() {
+ return {
+ text: __('Cancel'),
+ };
+ },
+ },
+ methods: {
+ ...mapActions([
+ 'addVariable',
+ 'updateVariable',
+ 'resetEditing',
+ 'displayInputValue',
+ 'clearModal',
+ ]),
+ updateOrAddVariable() {
+ if (this.variableBeingEdited) {
+ this.updateVariable(this.variableBeingEdited);
+ } else {
+ this.addVariable();
+ }
+ },
+ resetModalHandler() {
+ if (this.variableBeingEdited) {
+ this.resetEditing();
+ } else {
+ this.clearModal();
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ :modal-id="$options.modalId"
+ :title="modalActionText"
+ :action-primary="primaryAction"
+ :action-cancel="cancelAction"
+ @ok="updateOrAddVariable"
+ @hidden="resetModalHandler"
+ >
+ <form>
+ <gl-form-group label="Type" label-for="ci-variable-type">
+ <gl-form-select
+ id="ci-variable-type"
+ v-model="variableData.variable_type"
+ :options="typeOptions"
+ />
+ </gl-form-group>
+
+ <div class="d-flex">
+ <gl-form-group label="Key" label-for="ci-variable-key" class="w-50 append-right-15">
+ <gl-form-input
+ id="ci-variable-key"
+ v-model="variableData.key"
+ type="text"
+ data-qa-selector="variable_key"
+ />
+ </gl-form-group>
+
+ <gl-form-group label="Value" label-for="ci-variable-value" class="w-50">
+ <gl-form-input
+ id="ci-variable-value"
+ v-model="variableData.secret_value"
+ type="text"
+ data-qa-selector="variable_value"
+ />
+ </gl-form-group>
+ </div>
+
+ <gl-form-group v-if="!isGroup" label="Environment scope" label-for="ci-variable-env">
+ <gl-form-select
+ id="ci-variable-env"
+ v-model="variableData.environment_scope"
+ :options="environments"
+ />
+ </gl-form-group>
+
+ <gl-form-group label="Flags" label-for="ci-variable-flags">
+ <gl-form-checkbox v-model="variableData.protected" class="mb-0">
+ {{ __('Protect variable') }}
+ <gl-link href="/help/ci/variables/README#protected-environment-variables">
+ <gl-icon name="question" :size="12" />
+ </gl-link>
+ <p class="prepend-top-4 clgray">
+ {{ __('Allow variables to run on protected branches and tags.') }}
+ </p>
+ </gl-form-checkbox>
+
+ <gl-form-checkbox
+ ref="masked-ci-variable"
+ v-model="variableData.masked"
+ :disabled="!canMask"
+ data-qa-selector="variable_masked"
+ >
+ {{ __('Mask variable') }}
+ <gl-link href="/help/ci/variables/README#masked-variables">
+ <gl-icon name="question" :size="12" />
+ </gl-link>
+ <p class="prepend-top-4 append-bottom-0 clgray">
+ {{
+ __(
+ 'Variables will be masked in job logs. Requires values to meet regular expression requirements.',
+ )
+ }}
+ <gl-link href="/help/ci/variables/README#masked-variables">{{
+ __('More information')
+ }}</gl-link>
+ </p>
+ </gl-form-checkbox>
+ </gl-form-group>
+ </form>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
new file mode 100644
index 00000000000..ed1240c247f
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
@@ -0,0 +1,32 @@
+<script>
+import CiVariableModal from './ci_variable_modal.vue';
+import CiVariableTable from './ci_variable_table.vue';
+import { mapState, mapActions } from 'vuex';
+
+export default {
+ components: {
+ CiVariableModal,
+ CiVariableTable,
+ },
+ computed: {
+ ...mapState(['isGroup']),
+ },
+ mounted() {
+ if (!this.isGroup) {
+ this.fetchEnvironments();
+ }
+ },
+ methods: {
+ ...mapActions(['fetchEnvironments']),
+ },
+};
+</script>
+
+<template>
+ <div class="row">
+ <div class="col-lg-12">
+ <ci-variable-table />
+ <ci-variable-modal />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
new file mode 100644
index 00000000000..e240323d2c5
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
@@ -0,0 +1,130 @@
+<script>
+import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
+import { s__, __ } from '~/locale';
+import { mapState, mapActions } from 'vuex';
+import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
+
+export default {
+ modalId: ADD_CI_VARIABLE_MODAL_ID,
+ fields: [
+ {
+ key: 'variable_type',
+ label: s__('CiVariables|Type'),
+ },
+ {
+ key: 'key',
+ label: s__('CiVariables|Key'),
+ },
+ {
+ key: 'value',
+ label: s__('CiVariables|Value'),
+ tdClass: 'qa-ci-variable-input-value',
+ },
+ {
+ key: 'protected',
+ label: s__('CiVariables|Protected'),
+ },
+ {
+ key: 'masked',
+ label: s__('CiVariables|Masked'),
+ },
+ {
+ key: 'environment_scope',
+ label: s__('CiVariables|Environment Scope'),
+ },
+ {
+ key: 'actions',
+ label: '',
+ },
+ ],
+ components: {
+ GlTable,
+ GlButton,
+ GlIcon,
+ },
+ directives: {
+ GlModalDirective,
+ },
+ computed: {
+ ...mapState(['variables', 'valuesHidden', 'isGroup', 'isLoading', 'isDeleting']),
+ valuesButtonText() {
+ return this.valuesHidden ? __('Reveal values') : __('Hide values');
+ },
+ tableIsNotEmpty() {
+ return this.variables && this.variables.length > 0;
+ },
+ fields() {
+ if (this.isGroup) {
+ return this.$options.fields.filter(field => field.key !== 'environment_scope');
+ }
+ return this.$options.fields;
+ },
+ },
+ mounted() {
+ this.fetchVariables();
+ },
+ methods: {
+ ...mapActions(['fetchVariables', 'deleteVariable', 'toggleValues', 'editVariable']),
+ },
+};
+</script>
+
+<template>
+ <div class="ci-variable-table">
+ <gl-table
+ :fields="fields"
+ :items="variables"
+ responsive
+ show-empty
+ tbody-tr-class="js-ci-variable-row"
+ >
+ <template #cell(value)="data">
+ <span v-if="valuesHidden">*****************</span>
+ <span v-else>{{ data.value }}</span>
+ </template>
+ <template #cell(actions)="data">
+ <gl-button
+ ref="edit-ci-variable"
+ v-gl-modal-directive="$options.modalId"
+ @click="editVariable(data.item)"
+ >
+ <gl-icon name="pencil" />
+ </gl-button>
+ <gl-button
+ ref="delete-ci-variable"
+ category="secondary"
+ variant="danger"
+ @click="deleteVariable(data.item)"
+ >
+ <gl-icon name="remove" />
+ </gl-button>
+ </template>
+ <template #empty>
+ <p ref="empty-variables" class="settings-message text-center empty-variables">
+ {{
+ __(
+ 'There are currently no variables, add a variable with the Add Variable button below.',
+ )
+ }}
+ </p>
+ </template>
+ </gl-table>
+ <div class="ci-variable-actions d-flex justify-content-end">
+ <gl-button
+ v-if="tableIsNotEmpty"
+ ref="secret-value-reveal-button"
+ data-qa-selector="reveal_ci_variable_value"
+ class="append-right-8"
+ @click="toggleValues(!valuesHidden)"
+ >{{ valuesButtonText }}</gl-button
+ >
+ <gl-button
+ ref="add-ci-variable"
+ v-gl-modal-directive="$options.modalId"
+ data-qa-selector="add_ci_variable"
+ variant="success"
+ >{{ __('Add Variable') }}</gl-button
+ >
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci_variable_list/constants.js b/app/assets/javascripts/ci_variable_list/constants.js
new file mode 100644
index 00000000000..bfc9cbbd840
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/constants.js
@@ -0,0 +1,2 @@
+// eslint-disable-next-line import/prefer-default-export
+export const ADD_CI_VARIABLE_MODAL_ID = 'add-ci-variable';
diff --git a/app/assets/javascripts/ci_variable_list/index.js b/app/assets/javascripts/ci_variable_list/index.js
new file mode 100644
index 00000000000..58501b216c1
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/index.js
@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import CiVariableSettings from './components/ci_variable_settings.vue';
+import createStore from './store';
+import { parseBoolean } from '~/lib/utils/common_utils';
+
+export default () => {
+ const el = document.getElementById('js-ci-project-variables');
+ const { endpoint, projectId, group, maskableRegex } = el.dataset;
+ const isGroup = parseBoolean(group);
+
+ const store = createStore({
+ endpoint,
+ projectId,
+ isGroup,
+ maskableRegex,
+ });
+
+ return new Vue({
+ el,
+ store,
+ render(createElement) {
+ return createElement(CiVariableSettings);
+ },
+ });
+};
diff --git a/app/assets/javascripts/ci_variable_list/store/utils.js b/app/assets/javascripts/ci_variable_list/store/utils.js
index 44807e03dad..0b9932d9bb5 100644
--- a/app/assets/javascripts/ci_variable_list/store/utils.js
+++ b/app/assets/javascripts/ci_variable_list/store/utils.js
@@ -1,4 +1,5 @@
import { __ } from '~/locale';
+import { cloneDeep } from 'lodash';
const variableType = 'env_var';
const fileType = 'file';
@@ -24,9 +25,9 @@ export const prepareDataForDisplay = variables => {
};
export const prepareDataForApi = (variable, destroy = false) => {
- const variableCopy = variable;
- variableCopy.protected.toString();
- variableCopy.masked.toString();
+ const variableCopy = cloneDeep(variable);
+ variableCopy.protected = variableCopy.protected.toString();
+ variableCopy.masked = variableCopy.masked.toString();
variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type);
if (variableCopy.environment_scope === __('All environments')) {
diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
index 8a5300c9266..479c82265f2 100644
--- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
@@ -1,17 +1,22 @@
import initSettingsPanels from '~/settings_panels';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
+import initVariableList from '~/ci_variable_list';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
- const variableListEl = document.querySelector('.js-ci-variable-list-section');
- // eslint-disable-next-line no-new
- new AjaxVariableList({
- container: variableListEl,
- saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
- errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
- saveEndpoint: variableListEl.dataset.saveEndpoint,
- maskableRegex: variableListEl.dataset.maskableRegex,
- });
+ if (gon.features.newVariablesUi) {
+ initVariableList();
+ } else {
+ const variableListEl = document.querySelector('.js-ci-variable-list-section');
+ // eslint-disable-next-line no-new
+ new AjaxVariableList({
+ container: variableListEl,
+ saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
+ errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
+ saveEndpoint: variableListEl.dataset.saveEndpoint,
+ maskableRegex: variableListEl.dataset.maskableRegex,
+ });
+ }
});
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index b4aac8eea2b..e08d0407245 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -2,6 +2,7 @@ import initSettingsPanels from '~/settings_panels';
import SecretValues from '~/behaviors/secret_values';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
import registrySettingsApp from '~/registry/settings/registry_settings_bundle';
+import initVariableList from '~/ci_variable_list';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
@@ -15,15 +16,19 @@ document.addEventListener('DOMContentLoaded', () => {
runnerTokenSecretValue.init();
}
- const variableListEl = document.querySelector('.js-ci-variable-list-section');
- // eslint-disable-next-line no-new
- new AjaxVariableList({
- container: variableListEl,
- saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
- errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
- saveEndpoint: variableListEl.dataset.saveEndpoint,
- maskableRegex: variableListEl.dataset.maskableRegex,
- });
+ if (gon.features.newVariablesUi) {
+ initVariableList();
+ } else {
+ const variableListEl = document.querySelector('.js-ci-variable-list-section');
+ // eslint-disable-next-line no-new
+ new AjaxVariableList({
+ container: variableListEl,
+ saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
+ errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
+ saveEndpoint: variableListEl.dataset.saveEndpoint,
+ maskableRegex: variableListEl.dataset.maskableRegex,
+ });
+ }
// hide extra auto devops settings based checkbox state
const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings');