diff --git a/.gitlab/ci/review-apps/qa.gitlab-ci.yml b/.gitlab/ci/review-apps/qa.gitlab-ci.yml index a1bd57ce4f6..98928ce4715 100644 --- a/.gitlab/ci/review-apps/qa.gitlab-ci.yml +++ b/.gitlab/ci/review-apps/qa.gitlab-ci.yml @@ -5,6 +5,12 @@ include: - /ci/allure-report.yml - /ci/knapsack-report.yml +.bundler_variables: + variables: + BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES: "true" + BUNDLE_SILENCE_ROOT_WARNING: "true" + BUNDLE_PATH: vendor + .test_variables: variables: QA_DEBUG: "true" @@ -21,16 +27,17 @@ include: - .use-docker-in-docker - .qa-cache - .test_variables + - .bundler_variables image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-bullseye-ruby-2.7-bundler-2.3-git-2.33-lfs-2.9-chrome-99-docker-20.10.14-gcloud-383-kubectl-1.23 stage: qa - needs: ["review-deploy"] + needs: + - review-deploy + - download-knapsack-report variables: DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: /certs DOCKER_CERT_PATH: /certs/client DOCKER_TLS_VERIFY: 1 - BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES: "true" - BUNDLE_PATH: vendor before_script: - export EE_LICENSE="$(cat $REVIEW_APPS_EE_LICENSE_FILE)" - export QA_GITLAB_URL="$(cat environment_url.txt)" @@ -71,6 +78,25 @@ include: ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID ALLURE_RESULTS_GLOB: qa/tmp/allure-results/* +# Store knapsack report as artifact so the same report is reused across all jobs +download-knapsack-report: + image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-2.7:bundler-2.3-git-2.33-chrome-99 + extends: + - .qa-cache + - .bundler_variables + - .review:rules:review-qa-reliable + stage: prepare + before_script: + - cd qa && bundle install + script: + - QA_KNAPSACK_REPORT_NAME=review-qa-reliable bundle exec rake "knapsack:download" + - QA_KNAPSACK_REPORT_NAME=review-qa-all bundle exec rake "knapsack:download" + allow_failure: true + artifacts: + paths: + - qa/knapsack/review-qa-*.json + expire_in: 1 day + review-qa-smoke: extends: - .review-qa-base @@ -145,7 +171,7 @@ allure-report-qa-all: variables: ALLURE_JOB_NAME: review-qa-all -knapsack-report: +upload-knapsack-report: extends: - .generate-knapsack-report-base stage: post-qa diff --git a/app/assets/javascripts/environments/components/environment_folder.vue b/app/assets/javascripts/environments/components/environment_folder.vue index d5c6d26cfd0..788c3ba6fed 100644 --- a/app/assets/javascripts/environments/components/environment_folder.vue +++ b/app/assets/javascripts/environments/components/environment_folder.vue @@ -34,9 +34,6 @@ export default { variables() { return { environment: this.nestedEnvironment.latest, scope: this.scope }; }, - pollInterval() { - return this.interval; - }, }, interval: { query: pollIntervalQuery, @@ -73,6 +70,11 @@ export default { methods: { toggleCollapse() { this.visible = !this.visible; + if (this.visible) { + this.$apollo.queries.folder.startPolling(this.interval); + } else { + this.$apollo.queries.folder.stopPolling(); + } }, isFirstEnvironment(index) { return index === 0; diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index a9948fed3b6..3432fe12705 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -386,7 +386,7 @@ export default { data-testid="add-to-review-button" type="submit" category="primary" - variant="success" + variant="confirm" @click.prevent="handleSaveDraft()" >{{ __('Add to review') }} diff --git a/app/assets/javascripts/releases/components/tag_field_new.vue b/app/assets/javascripts/releases/components/tag_field_new.vue index dcdf89ae0d9..d3b6d07590f 100644 --- a/app/assets/javascripts/releases/components/tag_field_new.vue +++ b/app/assets/javascripts/releases/components/tag_field_new.vue @@ -52,7 +52,10 @@ export default { }, }, showTagNameValidationError() { - return this.isInputDirty && this.validationErrors.isTagNameEmpty; + return ( + this.isInputDirty && + (this.validationErrors.isTagNameEmpty || this.validationErrors.existingRelease) + ); }, tagNameInputId() { return uniqueId('tag-name-input-'); @@ -60,6 +63,11 @@ export default { createFromSelectorId() { return uniqueId('create-from-selector-'); }, + tagFeedback() { + return this.validationErrors.existingRelease + ? __('Selected tag is already in use. Choose another option.') + : __('Tag name is required.'); + }, }, methods: { ...mapActions('editNew', ['updateReleaseTagName', 'updateCreateFrom', 'fetchTagNotes']), @@ -112,7 +120,7 @@ export default { { }) .catch((error) => { createFlash({ - message: s__('Release|Something went wrong while getting the tag notes.'), + message: s__('Release|Unable to fetch the tag notes.'), }); commit(types.RECEIVE_TAG_NOTES_ERROR, error); diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/getters.js b/app/assets/javascripts/releases/stores/modules/edit_new/getters.js index 036bf4f3eaf..0ca5eb9931a 100644 --- a/app/assets/javascripts/releases/stores/modules/edit_new/getters.js +++ b/app/assets/javascripts/releases/stores/modules/edit_new/getters.js @@ -53,6 +53,10 @@ export const validationErrors = (state) => { errors.isTagNameEmpty = true; } + if (state.existingRelease) { + errors.existingRelease = true; + } + // Each key of this object is a URL, and the value is an // array of Release link objects that share this URL. // This is used for detecting duplicate URLs. @@ -114,7 +118,11 @@ export const validationErrors = (state) => { /** Returns whether or not the release object is valid */ export const isValid = (_state, getters) => { const errors = getters.validationErrors; - return Object.values(errors.assets.links).every(isEmpty) && !errors.isTagNameEmpty; + return ( + Object.values(errors.assets.links).every(isEmpty) && + !errors.isTagNameEmpty && + !errors.existingRelease + ); }; /** Returns all the variables for a `releaseUpdate` GraphQL mutation */ diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js b/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js index 38153e4c67b..6b22468bbfe 100644 --- a/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js +++ b/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js @@ -103,6 +103,7 @@ export default { state.fetchError = undefined; state.isFetchingTagNotes = false; state.tagNotes = data.message; + state.existingRelease = data.release; }, [types.RECEIVE_TAG_NOTES_ERROR](state, error) { state.fetchError = error; diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/state.js b/app/assets/javascripts/releases/stores/modules/edit_new/state.js index 9f8146997c1..33cb3ee06d0 100644 --- a/app/assets/javascripts/releases/stores/modules/edit_new/state.js +++ b/app/assets/javascripts/releases/stores/modules/edit_new/state.js @@ -53,4 +53,5 @@ export default ({ tagNotes: '', includeTagNotes: false, + existingRelease: null, }); diff --git a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue b/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue index 7c2a575bf93..40787cf72da 100644 --- a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue +++ b/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue @@ -5,7 +5,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils'; import RunnerHeader from '../components/runner_header.vue'; import RunnerUpdateForm from '../components/runner_update_form.vue'; import { I18N_FETCH_ERROR } from '../constants'; -import runnerQuery from '../graphql/details/runner.query.graphql'; +import runnerFormQuery from '../graphql/edit/runner_form.query.graphql'; import { captureException } from '../sentry_utils'; export default { @@ -32,7 +32,7 @@ export default { }, apollo: { runner: { - query: runnerQuery, + query: runnerFormQuery, variables() { return { id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId), diff --git a/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue b/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue index ef48d7799f8..c3f317b40b0 100644 --- a/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue +++ b/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue @@ -10,7 +10,7 @@ import RunnerPauseButton from '../components/runner_pause_button.vue'; import RunnerHeader from '../components/runner_header.vue'; import RunnerDetails from '../components/runner_details.vue'; import { I18N_FETCH_ERROR } from '../constants'; -import runnerQuery from '../graphql/details/runner.query.graphql'; +import runnerQuery from '../graphql/show/runner.query.graphql'; import { captureException } from '../sentry_utils'; import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage'; diff --git a/app/assets/javascripts/runner/components/runner_jobs.vue b/app/assets/javascripts/runner/components/runner_jobs.vue index b25d92d049e..4eb1312b204 100644 --- a/app/assets/javascripts/runner/components/runner_jobs.vue +++ b/app/assets/javascripts/runner/components/runner_jobs.vue @@ -1,7 +1,7 @@