diff options
Diffstat (limited to 'app/assets/javascripts')
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'); |