diff --git a/.gitlab/ci/qa-common/main.gitlab-ci.yml b/.gitlab/ci/qa-common/main.gitlab-ci.yml index 5c9043f8694..b48c3f67a6f 100644 --- a/.gitlab/ci/qa-common/main.gitlab-ci.yml +++ b/.gitlab/ci/qa-common/main.gitlab-ci.yml @@ -6,7 +6,7 @@ workflow: include: - project: gitlab-org/quality/pipeline-common - ref: 7.2.3 + ref: 7.5.0 file: - /ci/base.gitlab-ci.yml - /ci/knapsack-report.yml diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index a7f17a90700..ae2e9dfcb03 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0183d30e998cfab0b4f93fd1aa60200fba8e3771 +46892cb4fa5b0e86b7e86fbb7acda5effac412d6 diff --git a/app/assets/javascripts/ci/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci/ci_variable_list/ci_variable_list.js deleted file mode 100644 index 574a5e7fd99..00000000000 --- a/app/assets/javascripts/ci/ci_variable_list/ci_variable_list.js +++ /dev/null @@ -1,262 +0,0 @@ -import $ from 'jquery'; -import SecretValues from '~/behaviors/secret_values'; -import CreateItemDropdown from '~/create_item_dropdown'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import { s__ } from '~/locale'; - -const ALL_ENVIRONMENTS_STRING = s__('CiVariable|All environments'); - -function createEnvironmentItem(value) { - return { - title: value === '*' ? ALL_ENVIRONMENTS_STRING : value, - id: value, - text: value === '*' ? s__('CiVariable|* (All environments)') : value, - }; -} - -export default class VariableList { - constructor({ container, formField, maskableRegex }) { - this.$container = $(container); - this.formField = formField; - this.maskableRegex = new RegExp(maskableRegex); - this.environmentDropdownMap = new WeakMap(); - - this.inputMap = { - id: { - selector: '.js-ci-variable-input-id', - default: '', - }, - variable_type: { - selector: '.js-ci-variable-input-variable-type', - default: 'env_var', - }, - key: { - selector: '.js-ci-variable-input-key', - default: '', - }, - secret_value: { - selector: '.js-ci-variable-input-value', - default: '', - }, - protected: { - selector: '.js-ci-variable-input-protected', - // use `attr` instead of `data` as we don't want the value to be - // converted. we need the value as a string. - default: $('.js-ci-variable-input-protected').attr('data-default'), - }, - masked: { - selector: '.js-ci-variable-input-masked', - // use `attr` instead of `data` as we don't want the value to be - // converted. we need the value as a string. - default: $('.js-ci-variable-input-masked').attr('data-default'), - }, - environment_scope: { - // We can't use a `.js-` class here because - // deprecated_jquery_dropdown replaces the and doesn't copy over the class - // See https://gitlab.com/gitlab-org/gitlab-foss/issues/42458 - selector: `input[name="${this.formField}[variables_attributes][][environment_scope]"]`, - default: '*', - }, - _destroy: { - selector: '.js-ci-variable-input-destroy', - default: '', - }, - }; - - this.secretValues = new SecretValues({ - container: this.$container[0], - valueSelector: '.js-row:not(:last-child) .js-secret-value', - placeholderSelector: '.js-row:not(:last-child) .js-secret-value-placeholder', - }); - } - - init() { - this.bindEvents(); - this.secretValues.init(); - } - - bindEvents() { - this.$container.find('.js-row').each((index, rowEl) => { - this.initRow(rowEl); - }); - - this.$container.on('click', '.js-row-remove-button', (e) => { - e.preventDefault(); - this.removeRow($(e.currentTarget).closest('.js-row')); - }); - - const inputSelector = Object.keys(this.inputMap) - .map((name) => this.inputMap[name].selector) - .join(','); - - // Remove any empty rows except the last row - this.$container.on('blur', inputSelector, (e) => { - const $row = $(e.currentTarget).closest('.js-row'); - - if ($row.is(':not(:last-child)') && !this.checkIfRowTouched($row)) { - this.removeRow($row); - } - }); - - this.$container.on('input trigger-change', inputSelector, (e) => { - // Always make sure there is an empty last row - const $lastRow = this.$container.find('.js-row').last(); - - if (this.checkIfRowTouched($lastRow)) { - this.insertRow($lastRow); - } - - // If masked, validate value against regex - this.validateMaskability($(e.currentTarget).closest('.js-row')); - }); - } - - initRow(rowEl) { - const $row = $(rowEl); - - // Reset the resizable textarea - $row.find(this.inputMap.secret_value.selector).css('height', ''); - - const $environmentSelect = $row.find('.js-variable-environment-toggle'); - if ($environmentSelect.length) { - const createItemDropdown = new CreateItemDropdown({ - $dropdown: $environmentSelect, - defaultToggleLabel: ALL_ENVIRONMENTS_STRING, - fieldName: `${this.formField}[variables_attributes][][environment_scope]`, - getData: (term, callback) => callback(this.getEnvironmentValues()), - createNewItemFromValue: createEnvironmentItem, - onSelect: () => { - // Refresh the other dropdowns in the variable list - // so they have the new value we just picked - this.refreshDropdownData(); - - $row.find(this.inputMap.environment_scope.selector).trigger('trigger-change'); - }, - }); - - // Clear out any data that might have been left-over from the row clone - createItemDropdown.clearDropdown(); - - this.environmentDropdownMap.set($row[0], createItemDropdown); - } - } - - insertRow($row) { - const $rowClone = $row.clone(); - $rowClone.removeAttr('data-is-persisted'); - - // Reset the inputs to their defaults - Object.keys(this.inputMap).forEach((name) => { - const entry = this.inputMap[name]; - $rowClone.find(entry.selector).val(entry.default); - }); - - // Close any dropdowns - $rowClone.find('.dropdown-menu.show').each((index, $dropdown) => { - $dropdown.classList.remove('show'); - }); - - this.initRow($rowClone); - - $row.after($rowClone); - } - - removeRow(row) { - const $row = $(row); - const isPersisted = parseBoolean($row.attr('data-is-persisted')); - - if (isPersisted) { - $row.hide(); - $row - // eslint-disable-next-line no-underscore-dangle - .find(this.inputMap._destroy.selector) - .val(true); - } else { - $row.remove(); - } - - // Refresh the other dropdowns in the variable list - // so any value with the variable deleted is gone - this.refreshDropdownData(); - } - - checkIfRowTouched($row) { - return Object.keys(this.inputMap).some((name) => { - // Row should not qualify as touched if only switches have been touched - if (['protected', 'masked'].includes(name)) return false; - - const entry = this.inputMap[name]; - const $el = $row.find(entry.selector); - return $el.length && $el.val() !== entry.default; - }); - } - - validateMaskability($row) { - const invalidInputClass = 'gl-field-error-outline'; - - const variableValue = $row.find(this.inputMap.secret_value.selector).val(); - const isValueMaskable = this.maskableRegex.test(variableValue) || variableValue === ''; - const isMaskedChecked = $row.find(this.inputMap.masked.selector).val() === 'true'; - - // Show a validation error if the user wants to mask an unmaskable variable value - $row - .find(this.inputMap.secret_value.selector) - .toggleClass(invalidInputClass, isMaskedChecked && !isValueMaskable); - $row - .find('.js-secret-value-placeholder') - .toggleClass(invalidInputClass, isMaskedChecked && !isValueMaskable); - $row.find('.masking-validation-error').toggle(isMaskedChecked && !isValueMaskable); - } - - toggleEnableRow(isEnabled = true) { - this.$container.find(this.inputMap.key.selector).attr('disabled', !isEnabled); - this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled); - } - - hideValues() { - this.secretValues.updateDom(false); - } - - getAllData() { - // Ignore the last empty row because we don't want to try persist - // a blank variable and run into validation problems. - const validRows = this.$container.find('.js-row').toArray().slice(0, -1); - - return validRows.map((rowEl) => { - const resultant = {}; - Object.keys(this.inputMap).forEach((name) => { - const entry = this.inputMap[name]; - const $input = $(rowEl).find(entry.selector); - if ($input.length) { - resultant[name] = $input.val(); - } - }); - - return resultant; - }); - } - - getEnvironmentValues() { - const valueMap = this.$container - .find(this.inputMap.environment_scope.selector) - .toArray() - .reduce( - (prevValueMap, envInput) => ({ - ...prevValueMap, - [envInput.value]: envInput.value, - }), - {}, - ); - - return Object.keys(valueMap).map(createEnvironmentItem); - } - - refreshDropdownData() { - this.$container.find('.js-row').each((index, rowEl) => { - const environmentDropdown = this.environmentDropdownMap.get(rowEl); - if (environmentDropdown) { - environmentDropdown.refreshData(); - } - }); - } -} diff --git a/app/assets/javascripts/ci/ci_variable_list/native_form_variable_list.js b/app/assets/javascripts/ci/ci_variable_list/native_form_variable_list.js deleted file mode 100644 index fdbefd8c313..00000000000 --- a/app/assets/javascripts/ci/ci_variable_list/native_form_variable_list.js +++ /dev/null @@ -1,25 +0,0 @@ -import $ from 'jquery'; -import VariableList from './ci_variable_list'; - -// Used for the variable list on scheduled pipeline edit page -export default function setupNativeFormVariableList({ container, formField = 'variables' }) { - const $container = $(container); - - const variableList = new VariableList({ - container: $container, - formField, - }); - variableList.init(); - - // Clear out the names in the empty last row so it - // doesn't get submitted and throw validation errors - $container.closest('form').on('submit trigger-submit', () => { - const $lastRow = $container.find('.js-row').last(); - - const isTouched = variableList.checkIfRowTouched($lastRow); - if (!isTouched) { - $lastRow.find('input, textarea').attr('name', ''); - $lastRow.find('select').attr('name', ''); - } - }); -} diff --git a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue index 396ff9808f2..ea019a95cd2 100644 --- a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue +++ b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue @@ -81,7 +81,7 @@ export default { this.description = schedule.description; this.cron = schedule.cron; this.cronTimezone = schedule.cronTimezone; - this.scheduleRef = schedule.ref; + this.scheduleRef = schedule.ref || this.defaultBranch; this.variables = variables.map((variable) => { return { id: variable.id, diff --git a/app/assets/javascripts/ci/pipeline_schedules/components/table/cells/pipeline_schedule_target.vue b/app/assets/javascripts/ci/pipeline_schedules/components/table/cells/pipeline_schedule_target.vue index 08efa794bcc..56d50026f17 100644 --- a/app/assets/javascripts/ci/pipeline_schedules/components/table/cells/pipeline_schedule_target.vue +++ b/app/assets/javascripts/ci/pipeline_schedules/components/table/cells/pipeline_schedule_target.vue @@ -27,10 +27,13 @@ export default { diff --git a/app/assets/javascripts/ci/pipeline_schedules/components/take_ownership_modal_legacy.vue b/app/assets/javascripts/ci/pipeline_schedules/components/take_ownership_modal_legacy.vue deleted file mode 100644 index b4d84309c5f..00000000000 --- a/app/assets/javascripts/ci/pipeline_schedules/components/take_ownership_modal_legacy.vue +++ /dev/null @@ -1,50 +0,0 @@ - - diff --git a/app/assets/javascripts/contribution_events/components/contribution_event/contribution_event_updated.vue b/app/assets/javascripts/contribution_events/components/contribution_event/contribution_event_updated.vue new file mode 100644 index 00000000000..e795e611ee5 --- /dev/null +++ b/app/assets/javascripts/contribution_events/components/contribution_event/contribution_event_updated.vue @@ -0,0 +1,25 @@ + + + diff --git a/app/assets/javascripts/contribution_events/components/contribution_events.vue b/app/assets/javascripts/contribution_events/components/contribution_events.vue index 8b42d77675f..1abb33a4d7c 100644 --- a/app/assets/javascripts/contribution_events/components/contribution_events.vue +++ b/app/assets/javascripts/contribution_events/components/contribution_events.vue @@ -12,6 +12,7 @@ import { EVENT_TYPE_CLOSED, EVENT_TYPE_REOPENED, EVENT_TYPE_COMMENTED, + EVENT_TYPE_UPDATED, } from '../constants'; import ContributionEventApproved from './contribution_event/contribution_event_approved.vue'; import ContributionEventExpired from './contribution_event/contribution_event_expired.vue'; @@ -24,6 +25,7 @@ import ContributionEventCreated from './contribution_event/contribution_event_cr import ContributionEventClosed from './contribution_event/contribution_event_closed.vue'; import ContributionEventReopened from './contribution_event/contribution_event_reopened.vue'; import ContributionEventCommented from './contribution_event/contribution_event_commented.vue'; +import ContributionEventUpdated from './contribution_event/contribution_event_updated.vue'; export default { props: { @@ -151,6 +153,9 @@ export default { case EVENT_TYPE_COMMENTED: return ContributionEventCommented; + case EVENT_TYPE_UPDATED: + return ContributionEventUpdated; + default: return EmptyComponent; } diff --git a/app/assets/javascripts/contribution_events/constants.js b/app/assets/javascripts/contribution_events/constants.js index b5eddbf7e25..94224b08e3c 100644 --- a/app/assets/javascripts/contribution_events/constants.js +++ b/app/assets/javascripts/contribution_events/constants.js @@ -119,14 +119,24 @@ export const EVENT_COMMENTED_I18N = Object.freeze({ [COMMIT_NOTEABLE_TYPE]: s__( 'ContributionEvent|Commented on commit %{noteableLink} in %{resourceParentLink}.', ), - fallback: s__('ContributionEvent|Commented on %{noteableLink}.'), + [TYPE_FALLBACK]: s__('ContributionEvent|Commented on %{noteableLink}.'), }); export const EVENT_COMMENTED_SNIPPET_I18N = Object.freeze({ [RESOURCE_PARENT_TYPE_PROJECT]: s__( 'ContributionEvent|Commented on snippet %{noteableLink} in %{resourceParentLink}.', ), - fallback: s__('ContributionEvent|Commented on snippet %{noteableLink}.'), + [TYPE_FALLBACK]: s__('ContributionEvent|Commented on snippet %{noteableLink}.'), +}); + +export const EVENT_UPDATED_I18N = Object.freeze({ + [TARGET_TYPE_DESIGN]: s__( + 'ContributionEvent|Updated design %{targetLink} in %{resourceParentLink}.', + ), + [TARGET_TYPE_WIKI]: s__( + 'ContributionEvent|Updated wiki page %{targetLink} in %{resourceParentLink}.', + ), + [TYPE_FALLBACK]: s__('ContributionEvent|Updated resource.'), }); export const EVENT_CLOSED_ICONS = Object.freeze({ diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue index d49598d2f21..926c556966c 100644 --- a/app/assets/javascripts/environments/components/environment_actions.vue +++ b/app/assets/javascripts/environments/components/environment_actions.vue @@ -87,7 +87,7 @@ export default {