diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-13 21:15:18 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-13 21:15:18 +0300 |
commit | 999cc13e0a77fad7322fbbe559013565355c2bfb (patch) | |
tree | cda92e5b004f6e4527f91acb1d24f228880a4526 | |
parent | aacba12c6e9817b552989de77b54e89b7c863b85 (diff) |
Add latest changes from gitlab-org/gitlab@master
61 files changed, 673 insertions, 170 deletions
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 9db4a2afd91..28c24c0dd26 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -342,9 +342,8 @@ rspec fast_spec_helper minimal: db:rollback: extends: .db-job-base script: - - if [[ -d "ee/" ]]; then task="db:migrate:main"; else task="db:migrate"; fi - - bundle exec rake "${task}" VERSION=20181228175414 - - bundle exec rake "${task}" SKIP_SCHEMA_VERSION_CHECK=true + - scripts/db_migrate VERSION=20181228175414 + - scripts/db_migrate SKIP_SCHEMA_VERSION_CHECK=true db:migrate:reset: extends: .db-job-base @@ -369,8 +368,7 @@ db:migrate-from-previous-major-version: - git checkout -f $CI_COMMIT_SHA - SETUP_DB=false USE_BUNDLE_INSTALL=true bash scripts/prepare_build.sh script: - - if [[ -d "ee/" ]]; then task="db:migrate:main"; else task="db:migrate"; fi - - run_timed_command "bundle exec rake ${task}" + - run_timed_command "scripts/db_migrate" db:check-schema: extends: @@ -379,8 +377,7 @@ db:check-schema: variables: TAG_TO_CHECKOUT: "v14.4.0" script: - - if [[ -d "ee/" ]]; then task="db:migrate:main"; else task="db:migrate"; fi - - run_timed_command "bundle exec rake ${task}" + - run_timed_command "scripts/db_migrate" - scripts/schema_changed.sh - scripts/validate_migration_timestamps diff --git a/.rubocop_todo/gitlab/delegate_predicate_methods.yml b/.rubocop_todo/gitlab/delegate_predicate_methods.yml index 2f3a1dce1e8..d31b4a30fc8 100644 --- a/.rubocop_todo/gitlab/delegate_predicate_methods.yml +++ b/.rubocop_todo/gitlab/delegate_predicate_methods.yml @@ -4,10 +4,7 @@ Gitlab/DelegatePredicateMethods: - app/models/clusters/cluster.rb - app/models/concerns/ci/metadatable.rb - app/models/concerns/integrations/base_data_fields.rb - - app/models/concerns/resolvable_discussion.rb - app/models/project.rb - ee/app/models/concerns/ee/ci/metadatable.rb - - ee/app/models/ee/group.rb - - ee/app/models/ee/namespace.rb - ee/app/models/license.rb - lib/gitlab/ci/trace/stream.rb diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index f16099effce..d39801c5c67 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -8ede1944ec9793c06dba5011234af7f5c5ec92b7 +15a1323ae16dffd3ba6b078f6cb81e283a96c72d diff --git a/app/assets/javascripts/environments/components/confirm_rollback_modal.vue b/app/assets/javascripts/environments/components/confirm_rollback_modal.vue index 4783b92942c..0e556f093e2 100644 --- a/app/assets/javascripts/environments/components/confirm_rollback_modal.vue +++ b/app/assets/javascripts/environments/components/confirm_rollback_modal.vue @@ -7,6 +7,7 @@ import { escape } from 'lodash'; import csrf from '~/lib/utils/csrf'; import { __, s__, sprintf } from '~/locale'; +import rollbackEnvironment from '../graphql/mutations/rollback_environment.mutation.graphql'; import eventHub from '../event_hub'; export default { @@ -40,10 +41,15 @@ export default { required: false, default: null, }, + graphql: { + type: Boolean, + required: false, + default: false, + }, }, computed: { modalTitle() { - const title = this.environment.isLastDeployment + const title = this.isLastDeployment ? s__('Environments|Re-deploy environment %{name}?') : s__('Environments|Rollback environment %{name}?'); @@ -53,6 +59,11 @@ export default { }, commitShortSha() { if (this.hasMultipleCommits) { + if (this.graphql) { + const { lastDeployment } = this.environment; + return this.commitData(lastDeployment, 'shortId'); + } + const { last_deployment } = this.environment; return this.commitData(last_deployment, 'short_id'); } @@ -61,6 +72,11 @@ export default { }, commitUrl() { if (this.hasMultipleCommits) { + if (this.graphql) { + const { lastDeployment } = this.environment; + return this.commitData(lastDeployment, 'commitPath'); + } + const { last_deployment } = this.environment; return this.commitData(last_deployment, 'commit_path'); } @@ -68,9 +84,7 @@ export default { return this.environment.commitUrl; }, modalActionText() { - return this.environment.isLastDeployment - ? s__('Environments|Re-deploy') - : s__('Environments|Rollback'); + return this.isLastDeployment ? s__('Environments|Re-deploy') : s__('Environments|Rollback'); }, primaryProps() { let attributes = [{ variant: 'danger' }]; @@ -84,20 +98,27 @@ export default { attributes, }; }, + isLastDeployment() { + // eslint-disable-next-line @gitlab/require-i18n-strings + return this.environment?.isLastDeployment || this.environment?.lastDeployment?.['last?']; + }, }, methods: { handleChange(event) { this.$emit('change', event); }, onOk() { - eventHub.$emit('rollbackEnvironment', this.environment); + if (this.graphql) { + this.$apollo.mutate({ + mutation: rollbackEnvironment, + variables: { environment: this.environment }, + }); + } else { + eventHub.$emit('rollbackEnvironment', this.environment); + } }, commitData(lastDeployment, key) { - if (lastDeployment && lastDeployment.commit) { - return lastDeployment.commit[key]; - } - - return ''; + return lastDeployment?.commit?.[key] ?? ''; }, }, csrf, diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue index 00497b3c683..f7f0cf4cb8d 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.vue +++ b/app/assets/javascripts/environments/components/environment_rollback.vue @@ -8,6 +8,7 @@ import { GlModalDirective, GlDropdownItem } from '@gitlab/ui'; import { s__ } from '~/locale'; import eventHub from '../event_hub'; +import setEnvironmentToRollback from '../graphql/mutations/set_environment_to_rollback.mutation.graphql'; export default { components: { @@ -32,11 +33,12 @@ export default { type: String, required: true, }, - }, - data() { - return { - isLoading: false, - }; + + graphql: { + type: Boolean, + required: false, + default: false, + }, }, computed: { @@ -49,16 +51,18 @@ export default { methods: { onClick() { - eventHub.$emit('requestRollbackEnvironment', { - ...this.environment, - retryUrl: this.retryUrl, - isLastDeployment: this.isLastDeployment, - }); - eventHub.$on('rollbackEnvironment', (environment) => { - if (environment.id === this.environment.id) { - this.isLoading = true; - } - }); + if (this.graphql) { + this.$apollo.mutate({ + mutation: setEnvironmentToRollback, + variables: { environment: this.environment }, + }); + } else { + eventHub.$emit('requestRollbackEnvironment', { + ...this.environment, + retryUrl: this.retryUrl, + isLastDeployment: this.isLastDeployment, + }); + } }, }, }; diff --git a/app/assets/javascripts/environments/graphql/mutations/set_environment_to_rollback.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_rollback.mutation.graphql new file mode 100644 index 00000000000..aba978ed79e --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_rollback.mutation.graphql @@ -0,0 +1,3 @@ +mutation SetEnvironmentToRollback($environment: Environment) { + setEnvironmentToRollback(environment: $environment) @client +} diff --git a/app/assets/javascripts/environments/graphql/queries/environment_to_rollback.query.graphql b/app/assets/javascripts/environments/graphql/queries/environment_to_rollback.query.graphql new file mode 100644 index 00000000000..f7586e27665 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/queries/environment_to_rollback.query.graphql @@ -0,0 +1,7 @@ +query environmentToRollback { + environmentToRollback @client { + id + name + lastDeployment + } +} diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js index 56e552fe461..b8d570a2d78 100644 --- a/app/assets/javascripts/environments/graphql/resolvers.js +++ b/app/assets/javascripts/environments/graphql/resolvers.js @@ -2,6 +2,7 @@ import axios from '~/lib/utils/axios_utils'; import { s__ } from '~/locale'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import pollIntervalQuery from './queries/poll_interval.query.graphql'; +import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql'; const buildErrors = (errors = []) => ({ errors, @@ -84,6 +85,12 @@ export const resolvers = (endpoint) => ({ ]); }); }, + setEnvironmentToRollback(_, { environment }, { client }) { + client.writeQuery({ + query: environmentToRollbackQuery, + data: { environmentToRollback: environment }, + }); + }, cancelAutoStop(_, { environment: { autoStopPath } }) { return axios .post(autoStopPath) diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql index 026f2ca9375..9c205a6b7b3 100644 --- a/app/assets/javascripts/environments/graphql/typedefs.graphql +++ b/app/assets/javascripts/environments/graphql/typedefs.graphql @@ -58,6 +58,7 @@ type LocalErrors { extend type Query { environmentApp: LocalEnvironmentApp folder(environment: NestedLocalEnvironmentInput): LocalEnvironmentFolder + environmentToRollback: LocalEnvironment isLastDeployment: Boolean } @@ -66,4 +67,5 @@ extend type Mutation { deleteEnvironment(environment: LocalEnvironmentInput): LocalErrors rollbackEnvironment(environment: LocalEnvironmentInput): LocalErrors cancelAutoStop(environment: LocalEnvironmentInput): LocalErrors + setEnvironmentToRollback(environment: LocalEnvironmentInput): LocalErrors } diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js index 463e0e5837e..14824820c0d 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js @@ -115,7 +115,7 @@ export default { }); // Add uniqueIds to add it as argument for _.intersection labelIds.unshift(uniqueIds); - // Return IDs that are present but not in all selected issueables + // Return IDs that are present but not in all selected issuables return uniqueIds.filter((x) => !intersection.apply(this, labelIds).includes(x)); }, diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js index 8f94f54dc78..1eb3ffc9808 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js @@ -3,7 +3,7 @@ import $ from 'jquery'; import { property } from 'lodash'; -import issueableEventHub from '~/issues_list/eventhub'; +import issuableEventHub from '~/issues_list/eventhub'; import LabelsSelect from '~/labels/labels_select'; import MilestoneSelect from '~/milestones/milestone_select'; import initIssueStatusSelect from './init_issue_status_select'; @@ -50,8 +50,8 @@ export default class IssuableBulkUpdateSidebar { // The event hub connects this bulk update logic with `issues_list_app.vue`. // We can remove it once we've refactored the issues list page bulk edit sidebar to Vue. // https://gitlab.com/gitlab-org/gitlab/-/issues/325874 - issueableEventHub.$on('issuables:enableBulkEdit', () => this.toggleBulkEdit(null, true)); - issueableEventHub.$on('issuables:updateBulkEdit', () => this.updateFormState()); + issuableEventHub.$on('issuables:enableBulkEdit', () => this.toggleBulkEdit(null, true)); + issuableEventHub.$on('issuables:updateBulkEdit', () => this.updateFormState()); } initDropdowns() { @@ -110,7 +110,7 @@ export default class IssuableBulkUpdateSidebar { toggleBulkEdit(e, enable) { e?.preventDefault(); - issueableEventHub.$emit('issuables:toggleBulkEdit', enable); + issuableEventHub.$emit('issuables:toggleBulkEdit', enable); this.toggleSidebarDisplay(enable); this.toggleBulkEditButtonDisabled(enable); diff --git a/app/assets/javascripts/issuable/index.js b/app/assets/javascripts/issuable/index.js index ad7c1942074..072422944f5 100644 --- a/app/assets/javascripts/issuable/index.js +++ b/app/assets/javascripts/issuable/index.js @@ -56,7 +56,7 @@ export function initCsvImportExportButtons() { export function initIssuableByEmail() { Vue.use(GlToast); - const el = document.querySelector('.js-issueable-by-email'); + const el = document.querySelector('.js-issuable-by-email'); if (!el) return null; diff --git a/app/assets/javascripts/issues/show/components/incidents/highlight_bar.vue b/app/assets/javascripts/issues/show/components/incidents/highlight_bar.vue index 96f187f26dd..d509f0dbc09 100644 --- a/app/assets/javascripts/issues/show/components/incidents/highlight_bar.vue +++ b/app/assets/javascripts/issues/show/components/incidents/highlight_bar.vue @@ -5,7 +5,7 @@ import { formatDate } from '~/lib/utils/datetime_utility'; export default { components: { GlLink, - IncidentSla: () => import('ee_component/issue_show/components/incidents/incident_sla.vue'), + IncidentSla: () => import('ee_component/issues/show/components/incidents/incident_sla.vue'), }, directives: { GlTooltip: GlTooltipDirective, diff --git a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue index 84107d3eaca..4790062ab7d 100644 --- a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue +++ b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue @@ -16,7 +16,7 @@ export default { GlTab, GlTabs, HighlightBar, - MetricsTab: () => import('ee_component/issue_show/components/incidents/metrics_tab.vue'), + MetricsTab: () => import('ee_component/issues/show/components/incidents/metrics_tab.vue'), }, inject: ['fullPath', 'iid', 'uploadMetricsFeatureAvailable'], apollo: { diff --git a/app/assets/javascripts/issues_list/components/issuables_list_app.vue b/app/assets/javascripts/issues_list/components/issuables_list_app.vue index 516a48aaa5b..71136bf0159 100644 --- a/app/assets/javascripts/issues_list/components/issuables_list_app.vue +++ b/app/assets/javascripts/issues_list/components/issuables_list_app.vue @@ -21,7 +21,7 @@ import { PAGE_SIZE_MANUAL, LOADING_LIST_ITEMS_LENGTH, } from '../constants'; -import issueableEventHub from '../eventhub'; +import issuableEventHub from '../eventhub'; import { emptyStateHelper } from '../service_desk_helper'; import Issuable from './issuable.vue'; @@ -192,7 +192,7 @@ export default { // We need to call nextTick here to wait for all of the boxes to be checked and rendered // before we query the dom in issuable_bulk_update_actions.js. this.$nextTick(() => { - issueableEventHub.$emit('issuables:updateBulkEdit'); + issuableEventHub.$emit('issuables:updateBulkEdit'); }); }, issuables() { @@ -203,7 +203,7 @@ export default { }, mounted() { if (this.canBulkEdit) { - this.unsubscribeToggleBulkEdit = issueableEventHub.$on('issuables:toggleBulkEdit', (val) => { + this.unsubscribeToggleBulkEdit = issuableEventHub.$on('issuables:toggleBulkEdit', (val) => { this.isBulkEditing = val; }); } @@ -211,7 +211,7 @@ export default { }, beforeDestroy() { // eslint-disable-next-line @gitlab/no-global-event-off - issueableEventHub.$off('issuables:toggleBulkEdit'); + issuableEventHub.$off('issuables:toggleBulkEdit'); }, methods: { isSelected(issuableId) { diff --git a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue index 9a8ec30bb2d..7dfa963a857 100644 --- a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue +++ b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue @@ -12,6 +12,7 @@ import { JOB_SCHEDULED, PLAY_JOB_CONFIRMATION_MESSAGE, RUN_JOB_NOW_HEADER_TITLE, + FILE_TYPE_ARCHIVE, } from '../constants'; import eventHub from '../event_hub'; import cancelJobMutation from '../graphql/mutations/job_cancel.mutation.graphql'; @@ -58,8 +59,11 @@ export default { }, }, computed: { + hasArtifacts() { + return this.job.artifacts.nodes.find((artifact) => artifact.fileType === FILE_TYPE_ARCHIVE); + }, artifactDownloadPath() { - return this.job.artifacts?.nodes[0]?.downloadPath; + return this.hasArtifacts.downloadPath; }, canReadJob() { return this.job.userPermissions?.readBuild; @@ -67,6 +71,9 @@ export default { canUpdateJob() { return this.job.userPermissions?.updateBuild; }, + canReadArtifacts() { + return this.job.userPermissions?.readJobArtifacts; + }, isActive() { return this.job.active; }, @@ -89,7 +96,7 @@ export default { return this.job.detailedStatus?.action?.method; }, shouldDisplayArtifacts() { - return this.job.userPermissions?.readJobArtifacts && this.job.artifacts?.nodes.length > 0; + return this.canReadArtifacts && this.hasArtifacts; }, }, methods: { diff --git a/app/assets/javascripts/jobs/components/table/constants.js b/app/assets/javascripts/jobs/components/table/constants.js index e5d1bc01cbf..d3c2f4ec39f 100644 --- a/app/assets/javascripts/jobs/components/table/constants.js +++ b/app/assets/javascripts/jobs/components/table/constants.js @@ -17,6 +17,9 @@ export const DEFAULT = 'default'; /* Job Status Constants */ export const JOB_SCHEDULED = 'SCHEDULED'; +/* Artifact file types */ +export const FILE_TYPE_ARCHIVE = 'ARCHIVE'; + /* i18n */ export const ACTIONS_DOWNLOAD_ARTIFACTS = __('Download artifacts'); export const ACTIONS_START_NOW = s__('DelayedJobs|Start now'); diff --git a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql index df37e084408..88937185a8c 100644 --- a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql +++ b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql @@ -19,6 +19,7 @@ query getJobs( artifacts { nodes { downloadPath + fileType } } allowFailure diff --git a/app/assets/javascripts/labels/labels_select.js b/app/assets/javascripts/labels/labels_select.js index ae0cf94b861..9d8ee165df2 100644 --- a/app/assets/javascripts/labels/labels_select.js +++ b/app/assets/javascripts/labels/labels_select.js @@ -101,7 +101,7 @@ export default class LabelsSelect { if (IS_EE) { /** * For Scoped labels, the last label selected with the - * same key will be applied to the current issueable. + * same key will be applied to the current issuable. * * If these are the labels - priority::1, priority::2; and if * we apply them in the same order, only priority::2 will stick diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 8a6ae268376..fc67cd98d15 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -10,7 +10,7 @@ class Projects::IssuesController < Projects::ApplicationController include RecordUserLastActivity ISSUES_EXCEPT_ACTIONS = %i[index calendar new create bulk_update import_csv export_csv service_desk].freeze - SET_ISSUEABLES_INDEX_ONLY_ACTIONS = %i[index calendar service_desk].freeze + SET_ISSUABLES_INDEX_ONLY_ACTIONS = %i[index calendar service_desk].freeze prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) } prepend_before_action(only: [:calendar]) { authenticate_sessionless_user!(:ics) } @@ -22,7 +22,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } after_action :log_issue_show, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } - before_action :set_issuables_index, if: ->(c) { SET_ISSUEABLES_INDEX_ONLY_ACTIONS.include?(c.action_name.to_sym) } + before_action :set_issuables_index, if: ->(c) { SET_ISSUABLES_INDEX_ONLY_ACTIONS.include?(c.action_name.to_sym) } # Allow write(create) issue before_action :authorize_create_issue!, only: [:new, :create] diff --git a/app/graphql/types/packages/package_type.rb b/app/graphql/types/packages/package_type.rb index 9851c6aec7e..d1312cb963d 100644 --- a/app/graphql/types/packages/package_type.rb +++ b/app/graphql/types/packages/package_type.rb @@ -21,7 +21,8 @@ module Types field :tags, Types::Packages::PackageTagType.connection_type, null: true, description: 'Package tags.' field :project, Types::ProjectType, null: false, description: 'Project where the package is stored.' field :pipelines, Types::Ci::PipelineType.connection_type, null: true, - description: 'Pipelines that built the package.' + description: 'Pipelines that built the package.', + deprecated: { reason: 'Due to scalability concerns, this field is going to be removed', milestone: '14.6' } field :metadata, Types::Packages::MetadataType, null: true, description: 'Package metadata.' field :versions, ::Types::Packages::PackageType.connection_type, null: true, diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb index 60e1dde17b9..aae338e9759 100644 --- a/app/models/concerns/resolvable_discussion.rb +++ b/app/models/concerns/resolvable_discussion.rb @@ -30,11 +30,14 @@ module ResolvableDiscussion delegate :resolved_at, :resolved_by, - :resolved_by_push?, to: :last_resolved_note, allow_nil: true end + def resolved_by_push? + !!last_resolved_note&.resolved_by_push? + end + def resolvable? strong_memoize(:resolvable) do potentially_resolvable? && notes.any?(&:resolvable?) diff --git a/app/models/repository.rb b/app/models/repository.rb index 47482f04bca..645cc9773bd 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -519,6 +519,8 @@ class Repository raw_repository.batch_blobs(items, blob_size_limit: blob_size_limit).map do |blob| Blob.decorate(blob, container) end + rescue Gitlab::Git::Repository::NoRepository + [] end def root_ref diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 53c2052bfab..10c48177ae4 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -30,7 +30,7 @@ = render 'issues' - if new_issue_email .gl-text-center.gl-pt-5.gl-pb-7 - .js-issueable-by-email{ data: { initial_email: new_issue_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } + .js-issuable-by-email{ data: { initial_email: new_issue_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } - else - new_project_issue_button_path = @project.archived? ? false : new_project_issue_path(@project) = render 'shared/empty_states/issues', new_project_issue_button_path: new_project_issue_button_path, show_import_button: true diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 41c6696789d..a3f40207d20 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -26,6 +26,6 @@ = render 'merge_requests', new_merge_request_path: new_merge_request_path - if new_merge_request_email .gl-text-center.gl-pt-5.gl-pb-7 - .js-issueable-by-email{ data: { initial_email: new_merge_request_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } + .js-issuable-by-email{ data: { initial_email: new_merge_request_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } - else = render 'shared/empty_states/merge_requests', button_path: new_merge_request_path diff --git a/config/feature_flags/development/geo_pages_deployment_verification.yml b/config/feature_flags/development/geo_pages_deployment_verification.yml new file mode 100644 index 00000000000..3c74d4d3485 --- /dev/null +++ b/config/feature_flags/development/geo_pages_deployment_verification.yml @@ -0,0 +1,8 @@ +--- +name: geo_pages_deployment_verification +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74905 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346754 +milestone: '14.6' +type: development +group: group::geo +default_enabled: false diff --git a/data/deprecations/14-6-pipeline-fields-package-deprecation.yml b/data/deprecations/14-6-pipeline-fields-package-deprecation.yml new file mode 100644 index 00000000000..b06bb4d16dc --- /dev/null +++ b/data/deprecations/14-6-pipeline-fields-package-deprecation.yml @@ -0,0 +1,12 @@ +- name: "Deprecate `pipelines` fields in the Package GraphQL types" + announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated. + announcement_date: "2021-12-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post. + removal_milestone: "15.0" # The milestone when this feature is planned to be removed + removal_date: "2022-05-22" # the date of the milestone release when this feature is planned to be removed + body: | # Do not modify this line, instead modify the lines below. + As part of the work to create a [Package Registry GraphQL API](https://gitlab.com/groups/gitlab-org/-/epics/6318), the Package group deprecated the `pipelines` fields in all Package-related GraphQL types. As of GitLab 14.6, the `pipelines` field is deprecated in [`Package`](https://docs.gitlab.com/ee/api/graphql/reference/index.html#package) and [`PackageDetailsType`](https://docs.gitlab.com/ee/api/graphql/reference/index.html#packagedetailstype) due to scalability and performance concerns. + + In milestone 15.0, we will completely remove `pipelines` from `Package` and `PackageDetailsType`. You can follow and contribute to work on a replacement in the epic [GitLab-#7214](https://gitlab.com/groups/gitlab-org/-/epics/7214). + stage: package + tiers: Free + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347219 diff --git a/db/migrate/20211119154221_create_pages_deployment_states.rb b/db/migrate/20211119154221_create_pages_deployment_states.rb new file mode 100644 index 00000000000..283f6c7d0d7 --- /dev/null +++ b/db/migrate/20211119154221_create_pages_deployment_states.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class CreatePagesDeploymentStates < Gitlab::Database::Migration[1.0] + VERIFICATION_STATE_INDEX_NAME = "index_pages_deployment_states_on_verification_state" + PENDING_VERIFICATION_INDEX_NAME = "index_pages_deployment_states_pending_verification" + FAILED_VERIFICATION_INDEX_NAME = "index_pages_deployment_states_failed_verification" + NEEDS_VERIFICATION_INDEX_NAME = "index_pages_deployment_states_needs_verification" + + disable_ddl_transaction! + + def up + unless table_exists?(:pages_deployment_states) + with_lock_retries do + create_table :pages_deployment_states, id: false do |t| + t.references :pages_deployment, primary_key: true, null: false, foreign_key: { on_delete: :cascade } + t.integer :verification_state, default: 0, limit: 2, null: false + t.column :verification_started_at, :datetime_with_timezone + t.datetime_with_timezone :verification_retry_at + t.datetime_with_timezone :verified_at + t.integer :verification_retry_count, limit: 2 + t.binary :verification_checksum, using: 'verification_checksum::bytea' + t.text :verification_failure + + t.index :verification_state, name: VERIFICATION_STATE_INDEX_NAME + t.index :verified_at, where: "(verification_state = 0)", order: { verified_at: 'ASC NULLS FIRST' }, name: PENDING_VERIFICATION_INDEX_NAME + t.index :verification_retry_at, where: "(verification_state = 3)", order: { verification_retry_at: 'ASC NULLS FIRST' }, name: FAILED_VERIFICATION_INDEX_NAME + t.index :verification_state, where: "(verification_state = 0 OR verification_state = 3)", name: NEEDS_VERIFICATION_INDEX_NAME + end + end + end + + add_text_limit :pages_deployment_states, :verification_failure, 255 + end + + def down + drop_table :pages_deployment_states + end +end diff --git a/db/post_migrate/20211208171402_reschedule_recalculate_vulnerability_finding_signatures_for_findings.rb b/db/post_migrate/20211208171402_reschedule_recalculate_vulnerability_finding_signatures_for_findings.rb new file mode 100644 index 00000000000..8eee082809b --- /dev/null +++ b/db/post_migrate/20211208171402_reschedule_recalculate_vulnerability_finding_signatures_for_findings.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class RescheduleRecalculateVulnerabilityFindingSignaturesForFindings < Gitlab::Database::Migration[1.0] + MIGRATION = 'RecalculateVulnerabilityFindingSignaturesForFindings' + BATCH_SIZE = 1_000 + DELAY_INTERVAL = 2.minutes + + disable_ddl_transaction! + + # Due to production incident previous migration was orphaned and must be rescheduled, + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72919#note_741188600 + def up + return unless Gitlab.ee? + + delete_queued_jobs(MIGRATION) + + requeue_background_migration_jobs_by_range_at_intervals( + MIGRATION, + DELAY_INTERVAL, + batch_size: BATCH_SIZE + ) + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20211119154221 b/db/schema_migrations/20211119154221 new file mode 100644 index 00000000000..c8b6005b48b --- /dev/null +++ b/db/schema_migrations/20211119154221 @@ -0,0 +1 @@ +020e17ffd6851fb861a17c1b120ca7cdfa300434d4a9ec923a4edcaa7f951b31
\ No newline at end of file diff --git a/db/schema_migrations/20211208171402 b/db/schema_migrations/20211208171402 new file mode 100644 index 00000000000..a563797dea5 --- /dev/null +++ b/db/schema_migrations/20211208171402 @@ -0,0 +1 @@ +09a9e7fc042aab19bf768a79401f33b6e7408acff303fc0ee68360dfd7605101
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index b25db1fbfbc..bde98b6932f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -17493,6 +17493,27 @@ CREATE SEQUENCE packages_tags_id_seq ALTER SEQUENCE packages_tags_id_seq OWNED BY packages_tags.id; +CREATE TABLE pages_deployment_states ( + pages_deployment_id bigint NOT NULL, + verification_state smallint DEFAULT 0 NOT NULL, + verification_started_at timestamp with time zone, + verification_retry_at timestamp with time zone, + verified_at timestamp with time zone, + verification_retry_count smallint, + verification_checksum bytea, + verification_failure text, + CONSTRAINT check_15217e8c3a CHECK ((char_length(verification_failure) <= 255)) +); + +CREATE SEQUENCE pages_deployment_states_pages_deployment_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE pages_deployment_states_pages_deployment_id_seq OWNED BY pages_deployment_states.pages_deployment_id; + CREATE TABLE pages_deployments ( id bigint NOT NULL, created_at timestamp with time zone NOT NULL, @@ -21954,6 +21975,8 @@ ALTER TABLE ONLY packages_packages ALTER COLUMN id SET DEFAULT nextval('packages ALTER TABLE ONLY packages_tags ALTER COLUMN id SET DEFAULT nextval('packages_tags_id_seq'::regclass); +ALTER TABLE ONLY pages_deployment_states ALTER COLUMN pages_deployment_id SET DEFAULT nextval('pages_deployment_states_pages_deployment_id_seq'::regclass); + ALTER TABLE ONLY pages_deployments ALTER COLUMN id SET DEFAULT nextval('pages_deployments_id_seq'::regclass); ALTER TABLE ONLY pages_domain_acme_orders ALTER COLUMN id SET DEFAULT nextval('pages_domain_acme_orders_id_seq'::regclass); @@ -23793,6 +23816,9 @@ ALTER TABLE ONLY packages_rubygems_metadata ALTER TABLE ONLY packages_tags ADD CONSTRAINT packages_tags_pkey PRIMARY KEY (id); +ALTER TABLE ONLY pages_deployment_states + ADD CONSTRAINT pages_deployment_states_pkey PRIMARY KEY (pages_deployment_id); + ALTER TABLE ONLY pages_deployments ADD CONSTRAINT pages_deployments_pkey PRIMARY KEY (id); @@ -27022,6 +27048,16 @@ CREATE INDEX index_packages_tags_on_package_id ON packages_tags USING btree (pac CREATE INDEX index_packages_tags_on_package_id_and_updated_at ON packages_tags USING btree (package_id, updated_at DESC); +CREATE INDEX index_pages_deployment_states_failed_verification ON pages_deployment_states USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3); + +CREATE INDEX index_pages_deployment_states_needs_verification ON pages_deployment_states USING btree (verification_state) WHERE ((verification_state = 0) OR (verification_state = 3)); + +CREATE INDEX index_pages_deployment_states_on_pages_deployment_id ON pages_deployment_states USING btree (pages_deployment_id); + +CREATE INDEX index_pages_deployment_states_on_verification_state ON pages_deployment_states USING btree (verification_state); + +CREATE INDEX index_pages_deployment_states_pending_verification ON pages_deployment_states USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0); + CREATE INDEX index_pages_deployments_on_ci_build_id ON pages_deployments USING btree (ci_build_id); CREATE INDEX index_pages_deployments_on_file_store_and_id ON pages_deployments USING btree (file_store, id); @@ -31570,6 +31606,9 @@ ALTER TABLE ONLY project_tracing_settings ALTER TABLE ONLY resource_label_events ADD CONSTRAINT fk_rails_fe91ece594 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL; +ALTER TABLE ONLY pages_deployment_states + ADD CONSTRAINT fk_rails_ff6ca551a4 FOREIGN KEY (pages_deployment_id) REFERENCES pages_deployments(id) ON DELETE CASCADE; + ALTER TABLE ONLY ci_builds_metadata ADD CONSTRAINT fk_rails_ffcf702a02 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index c6a1a93af7c..4a504b07a1b 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -262,15 +262,15 @@ configuration option in `gitlab.yml`. These metrics are served from the | `geo_group_wiki_repositories_failed` | Gauge | 13.10 | Number of syncable group wikis failed on secondary | `url` | | `geo_group_wiki_repositories_registry` | Gauge | 13.10 | Number of syncable group wikis in the registry | `url` | | `geo_pages_deployments` | Gauge | 14.3 | Number of pages deployments on primary | `url` | -| `geo_pages_deployments_checksum_total` | Gauge | 14.3 | Number of pages deployments tried to checksum on primary | `url` | -| `geo_pages_deployments_checksummed` | Gauge | 14.3 | Number of pages deployments successfully checksummed on primary | `url` | -| `geo_pages_deployments_checksum_failed` | Gauge | 14.3 | Number of pages deployments failed to calculate the checksum on primary | `url` | +| `geo_pages_deployments_checksum_total` | Gauge | 14.6 | Number of pages deployments tried to checksum on primary | `url` | +| `geo_pages_deployments_checksummed` | Gauge | 14.6 | Number of pages deployments successfully checksummed on primary | `url` | +| `geo_pages_deployments_checksum_failed` | Gauge | 14.6 | Number of pages deployments failed to calculate the checksum on primary | `url` | | `geo_pages_deployments_synced` | Gauge | 14.3 | Number of syncable pages deployments synced on secondary | `url` | | `geo_pages_deployments_failed` | Gauge | 14.3 | Number of syncable pages deployments failed to sync on secondary | `url` | | `geo_pages_deployments_registry` | Gauge | 14.3 | Number of pages deployments in the registry | `url` | -| `geo_pages_deployments_verification_total` | Gauge | 14.3 | Number of pages deployments verifications tried on secondary | `url` | -| `geo_pages_deployments_verified` | Gauge | 14.3 | Number of pages deployments verified on secondary | `url` | -| `geo_pages_deployments_verification_failed` | Gauge | 14.3 | Number of pages deployments verifications failed on secondary | `url` | +| `geo_pages_deployments_verification_total` | Gauge | 14.6 | Number of pages deployments verifications tried on secondary | `url` | +| `geo_pages_deployments_verified` | Gauge | 14.6 | Number of pages deployments verified on secondary | `url` | +| `geo_pages_deployments_verification_failed` | Gauge | 14.6 | Number of pages deployments verifications failed on secondary | `url` | | `limited_capacity_worker_running_jobs` | Gauge | 13.5 | Number of running jobs | `worker` | | `limited_capacity_worker_max_running_jobs` | Gauge | 13.5 | Maximum number of running jobs | `worker` | | `limited_capacity_worker_remaining_work_count` | Gauge | 13.5 | Number of jobs waiting to be enqueued | `worker` | diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 7fa58f59717..e4c7907c278 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -12497,7 +12497,7 @@ Represents a package in the Package Registry. Note that this type is in beta and | <a id="packagemetadata"></a>`metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. | | <a id="packagename"></a>`name` | [`String!`](#string) | Name of the package. | | <a id="packagepackagetype"></a>`packageType` | [`PackageTypeEnum!`](#packagetypeenum) | Package type. | -| <a id="packagepipelines"></a>`pipelines` | [`PipelineConnection`](#pipelineconnection) | Pipelines that built the package. (see [Connections](#connections)) | +| <a id="packagepipelines"></a>`pipelines` **{warning-solid}** | [`PipelineConnection`](#pipelineconnection) | **Deprecated** in 14.6. Due to scalability concerns, this field is going to be removed. | | <a id="packageproject"></a>`project` | [`Project!`](#project) | Project where the package is stored. | | <a id="packagestatus"></a>`status` | [`PackageStatus!`](#packagestatus) | Package status. | | <a id="packagetags"></a>`tags` | [`PackageTagConnection`](#packagetagconnection) | Package tags. (see [Connections](#connections)) | @@ -12559,7 +12559,7 @@ Represents a package details in the Package Registry. Note that this type is in | <a id="packagedetailstypename"></a>`name` | [`String!`](#string) | Name of the package. | | <a id="packagedetailstypepackagefiles"></a>`packageFiles` | [`PackageFileConnection`](#packagefileconnection) | Package files. (see [Connections](#connections)) | | <a id="packagedetailstypepackagetype"></a>`packageType` | [`PackageTypeEnum!`](#packagetypeenum) | Package type. | -| <a id="packagedetailstypepipelines"></a>`pipelines` | [`PipelineConnection`](#pipelineconnection) | Pipelines that built the package. (see [Connections](#connections)) | +| <a id="packagedetailstypepipelines"></a>`pipelines` **{warning-solid}** | [`PipelineConnection`](#pipelineconnection) | **Deprecated** in 14.6. Due to scalability concerns, this field is going to be removed. | | <a id="packagedetailstypeproject"></a>`project` | [`Project!`](#project) | Project where the package is stored. | | <a id="packagedetailstypestatus"></a>`status` | [`PackageStatus!`](#packagestatus) | Package status. | | <a id="packagedetailstypetags"></a>`tags` | [`PackageTagConnection`](#packagetagconnection) | Package tags. (see [Connections](#connections)) | diff --git a/doc/raketasks/index.md b/doc/raketasks/index.md index 80aa52ed5a4..6227731e807 100644 --- a/doc/raketasks/index.md +++ b/doc/raketasks/index.md @@ -45,6 +45,7 @@ The following Rake tasks are available for use with GitLab: | [SMTP maintenance](../administration/raketasks/smtp.md) | SMTP-related tasks. | | [SPDX license list import](spdx.md) | Import a local copy of the [SPDX license list](https://spdx.org/licenses/) for matching [License Compliance policies](../user/compliance/license_compliance/index.md). | | [Repository storage](../administration/raketasks/storage.md) | List and migrate existing projects and attachments from legacy storage to hashed storage. | +| [Reset user passwords](../security/reset_user_password.md#use-a-rake-task) | Reset user passwords using Rake. | | [Uploads migrate](../administration/raketasks/uploads/migrate.md) | Migrate uploads between local storage and object storage. | | [Uploads sanitize](../administration/raketasks/uploads/sanitize.md) | Remove EXIF data from images uploaded to earlier versions of GitLab. | | [Service Data](../administration/troubleshooting/gitlab_rails_cheat_sheet.md#generate-service-ping) | Generate and troubleshoot [Service Ping](../development/service_ping/index.md). | diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md index f63c35ab475..09e1b626c0a 100644 --- a/doc/raketasks/user_management.md +++ b/doc/raketasks/user_management.md @@ -176,3 +176,7 @@ cp config/secrets.yml.bak config/secrets.yml sudo /etc/init.d/gitlab start ``` + +## Related topics + +- [Reset a user's password](../security/reset_user_password.md#use-a-rake-task). diff --git a/doc/security/reset_user_password.md b/doc/security/reset_user_password.md index 8b89200e1a7..a61660f6a2f 100644 --- a/doc/security/reset_user_password.md +++ b/doc/security/reset_user_password.md @@ -5,121 +5,120 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: howto --- -# How to reset user password **(FREE SELF)** +# Reset a user's password **(FREE SELF)** -There are a few ways to reset the password of a user. +You can reset user passwords by using a Rake task, a Rails console, or the +[Users API](../api/users.md#user-modification). -## Rake Task +## Prerequisites + +To reset a user password, you must be an administrator of a self-managed GitLab instance. + +## Use a Rake task > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52347) in GitLab 13.9. -GitLab provides a Rake Task to reset passwords of users using their usernames, -which can be invoked by the following command: +Use the following Rake task to reset a user's password: -```shell -sudo gitlab-rake "gitlab:password:reset" -``` +- **For Omnibus installations** -GitLab asks for a username, a password, and a password confirmation. Upon giving -proper values for them, the password of the specified user is updated. + ```shell + sudo gitlab-rake "gitlab:password:reset" + ``` -The Rake task also takes the username as an argument, as shown in the example -below: +- **For installations from source** -```shell -sudo gitlab-rake "gitlab:password:reset[johndoe]" -``` + ```shell + bundle exec rake "gitlab:password:reset" + ``` -NOTE: -To reset the default admin password, run this Rake task with the username -`root`, which is the default username of that administrator account. +GitLab requests a username, a password, and confirmation of the password. When complete, the user's password is updated. -## Rails console +The Rake task can take a username as an argument. For example, to reset the password for the user with username +`sidneyjones`: -The Rake task is capable of finding users via their usernames. However, if only -user ID or email ID of the user is known, Rails console can be used to find user -using user ID and then change password of the user manually. +- **For Omnibus installations** -1. [Start a Rails console](../administration/operations/rails_console.md) + ```shell + sudo gitlab-rake "gitlab:password:reset[sidneyjones]" + ``` -1. Find the user either by username, user ID or email ID: +- **For installations from source** - ```ruby - user = User.find_by_username 'exampleuser' + ```shell + bundle exec rake "gitlab:password:reset[sidneyjones]" + ``` - #or +## Use a Rails console - user = User.find(123) +If you know the username, user ID, or email address, you can use the Rails console to reset their password: - #or +1. Open a [Rails console](../administration/operations/rails_console.md). +1. Find the user: - user = User.find_by(email: 'user@example.com') - ``` + - By username: -1. Reset the password + ```ruby + user = User.find_by_username 'exampleuser' + ``` - ```ruby - user.password = 'secret_pass' - user.password_confirmation = 'secret_pass' - ``` + - By user ID: -1. When using this method instead of the [Users API](../api/users.md#user-modification), - GitLab sends an email to the user stating that the user changed their - password. If the password was changed by an administrator, execute the - following command to notify the user by email: + ```ruby + user = User.find(123) + ``` - ```ruby - user.send_only_admin_changed_your_password_notification! + - By email address: + + ```ruby + user = User.find_by(email: 'user@example.com') + ``` + +1. Reset the password: + + ```ruby + user.password = 'secret_pass' + user.password_confirmation = 'secret_pass' ``` +1. Optional. Notify the user that an administrator changed their password: + + ```ruby + user.send_only_admin_changed_your_password_notification! + ``` + 1. Save the changes: ```ruby user.save! ``` -1. Exit the console, and then try to sign in with your new password. +1. Exit the console: + + ```ruby + exit + ``` -NOTE: -You can also reset passwords by using the [Users API](../api/users.md#user-modification). +## Reset the root password -## Password reset does not appear to work +To reset the root password, follow the steps listed previously. -If you can't sign on with the new password, it might be because of the [reconfirmation feature](../user/upgrade_email_bypass.md). +- If the root account name hasn't changed, use the username `root`. +- If the root account name has changed and you don't know the new username, + you might be able to use a Rails console with user ID `1`. In almost all + cases, the first user is the default administrator account. -Try fixing this on the rails console. For example, if your new `root` password isn't working: +## Troubleshooting -1. [Start a Rails console](../administration/operations/rails_console.md). +If the new password doesn't work, it might be [an email confirmation issue](../user/upgrade_email_bypass.md). You can +attempt to fix this issue in a Rails console. For example, if a new `root` password isn't working: -1. Find the user and skip reconfirmation, using any of the methods above: +1. Start a [Rails console](../administration/operations/rails_console.md). +1. Find the user and skip reconfirmation: ```ruby user = User.find(1) user.skip_reconfirmation! ``` -1. Try to sign in again. - -## Reset your root password - -The previously described steps can also be used to reset the root password. - -In normal installations where the username of root account hasn't been changed -manually, the Rake task can be used with username `root` to reset the root -password. - -If the username was changed to something else and has been forgotten, one -possible way is to reset the password using Rails console with user ID `1` (in -almost all the cases, the first user is the default administrator account). - -<!-- ## Troubleshooting - -Include any troubleshooting steps that you can foresee. If you know beforehand what issues -one might have when setting this up, or when something is changed, or on upgrading, it's -important to describe those, too. Think of things that may go wrong and include them here. -This is important to minimize requests for support, and to avoid doc comments with -questions that you know someone might ask. - -Each scenario can be a third-level heading, e.g. `### Getting error message X`. -If you have none to add when creating a doc, leave this section in place -but commented out to help encourage others to add to it in the future. --> +1. Attempt to sign in again. diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index 2f6468ff9e1..8bf14bc2dce 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -127,6 +127,15 @@ In milestone 15.0, we will completely remove `Version` from `PackageType`. Announced: 2021-11-22 Planned removal: 2022-05-22 +### Deprecate `pipelines` fields in the Package GraphQL types + +As part of the work to create a [Package Registry GraphQL API](https://gitlab.com/groups/gitlab-org/-/epics/6318), the Package group deprecated the `pipelines` fields in all Package-related GraphQL types. As of GitLab 14.6, the `pipelines` field is deprecated in [`Package`](https://docs.gitlab.com/ee/api/graphql/reference/index.html#package) and [`PackageDetailsType`](https://docs.gitlab.com/ee/api/graphql/reference/index.html#packagedetailstype) due to scalability and performance concerns. + +In milestone 15.0, we will completely remove `pipelines` from `Package` and `PackageDetailsType`. You can follow and contribute to work on a replacement in the epic [GitLab-#7214](https://gitlab.com/groups/gitlab-org/-/epics/7214). + +Announced: 2021-12-22 +Planned removal: 2022-05-22 + ### Deprecate legacy approval status names from License Compliance API We deprecated legacy names for approval status of license policy (blacklisted, approved) in the `managed_licenses` API but they are still used in our API queries and responses. They will be removed in 15.0. diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md index 23e186e25ec..329e10be239 100644 --- a/doc/user/application_security/dast/index.md +++ b/doc/user/application_security/dast/index.md @@ -975,6 +975,11 @@ To view running completed and scheduled on-demand DAST scans for a project, go t To cancel a pending or running on-demand scan, select **Cancel** (**{cancel}**) in the on-demand scans list. +#### Retry an on-demand scan + +To retry a scan that failed or succeeded with warnings, select **Retry** (**{retry}**) in the +on-demand scans list. + ### Run an on-demand DAST scan Prerequisites: diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md index c9cdc8643f4..9497dd1625b 100644 --- a/doc/user/packages/container_registry/index.md +++ b/doc/user/packages/container_registry/index.md @@ -370,10 +370,17 @@ WARNING: Deleting images is a destructive action and can't be undone. To restore a deleted image, you must rebuild and re-upload it. -NOTE: -Administrators should review how to -[garbage collect](../../../administration/packages/container_registry.md#container-registry-garbage-collection) -the deleted images. +On self-managed instances, deleting an image doesn't free up storage space - it only marks the image +as eligible for deletion. To actually delete images and recover storage space, in case they're +unreferenced, administrators must run [garbage collection](../../../administration/packages/container_registry.md#container-registry-garbage-collection). + +On GitLab.com, the latest version of the Container Registry includes an automatic online garbage +collector. For more information, see [this blog post](https://about.gitlab.com/blog/2021/10/25/gitlab-com-container-registry-update/). +This is an instance-wide feature, rolling out gradually to a subset of the user base, so some new image repositories created +from GitLab 14.5 onwards are served by this new version of the Container Registry. In this new +version of the Container Registry, layers that aren't referenced by any image manifest, and image +manifests that have no tags and aren't referenced by another manifest (such as multi-architecture +images), are automatically scheduled for deletion after 24 hours if left unreferenced. ### Delete images from within GitLab diff --git a/doc/user/project/releases/img/feature_count_v14_6.png b/doc/user/project/releases/img/feature_count_v14_6.png Binary files differnew file mode 100644 index 00000000000..0b1a0552631 --- /dev/null +++ b/doc/user/project/releases/img/feature_count_v14_6.png diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md index 83f0d253e7f..1e00220b172 100644 --- a/doc/user/project/releases/index.md +++ b/doc/user/project/releases/index.md @@ -595,6 +595,29 @@ links to a release is not recommended, because artifacts are ephemeral and are used to pass data in the same pipeline. This means there's a risk that they could either expire or someone might manually delete them. +#### Number of new and total features **(FREE SAAS)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235618) in GitLab 13.5. + +On [GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/releases), you can view the number of new and total features in the project. + +![Feature count](img/feature_count_v14_6.png "Number of features in a release") + +The totals are displayed on [shields](https://shields.io/) and are generated per release by +[a Rake task in the `www-gitlab-com` repo](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/lib/tasks/update_gitlab_project_releases_page.rake). + +| Item | Formula | +| ------ | ------ | +| `New features` | Total count of release posts across all tiers for a single release in the project. | +| `Total features` | Total count of release posts in reverse order for all releases in the project. | + +The counts are also shown by license tier. + +| Item | Formula | +| ------ | ------ | +| `New features` | Total count of release posts across a single tier for a single release in the project. | +| `Total features` | Total count of release posts across a single tier in reverse order for all releases in the project. | + ## Release evidence > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26019) in GitLab 12.6. diff --git a/doc/user/project/wiki/img/content_editor_v14.0.png b/doc/user/project/wiki/img/content_editor_v14.0.png Binary files differdeleted file mode 100644 index b44a633073d..00000000000 --- a/doc/user/project/wiki/img/content_editor_v14.0.png +++ /dev/null diff --git a/doc/user/project/wiki/img/content_editor_v14.6.png b/doc/user/project/wiki/img/content_editor_v14.6.png Binary files differnew file mode 100644 index 00000000000..55fca0ace1e --- /dev/null +++ b/doc/user/project/wiki/img/content_editor_v14.6.png diff --git a/doc/user/project/wiki/img/use_new_editor_button_v14.0.png b/doc/user/project/wiki/img/use_new_editor_button_v14.0.png Binary files differdeleted file mode 100644 index d9a5cf83302..00000000000 --- a/doc/user/project/wiki/img/use_new_editor_button_v14.0.png +++ /dev/null diff --git a/doc/user/project/wiki/img/use_new_editor_button_v14.6.png b/doc/user/project/wiki/img/use_new_editor_button_v14.6.png Binary files differnew file mode 100644 index 00000000000..078fed8a1e9 --- /dev/null +++ b/doc/user/project/wiki/img/use_new_editor_button_v14.6.png diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md index bdef83bd6c4..9f1670d3f4c 100644 --- a/doc/user/project/wiki/index.md +++ b/doc/user/project/wiki/index.md @@ -339,27 +339,24 @@ experience in the Wiki. To opt in for the new editor: 1. Create a new wiki page, or edit an existing one. 1. Ensure the wiki page uses the Markdown format. Other formats are not yet supported. -1. Below the **Format** select box, select **Use the new editor**: +1. Above the content field, select **Edit rich text**: - ![Use new editor button image](img/use_new_editor_button_v14.0.png) + ![Use new editor button image](img/use_new_editor_button_v14.6.png) ### Use the Content Editor 1. [Create](#create-a-new-wiki-page) a new wiki page, or [edit](#edit-a-wiki-page) an existing one. 1. Select **Markdown** as your format. -1. Below the **Format** select box, select **Use new editor**. +1. Above **Content**, select **Edit rich text**. 1. Customize your page's content using the various formatting options available in the content editor. 1. Select **Create page** for a new page, or **Save changes** for an existing page: - ![Content Editor in Wikis image](img/content_editor_v14.0.png) + ![Content Editor in Wikis image](img/content_editor_v14.6.png) ### Switch back to the old editor 1. *If you're editing the page in the content editor,* scroll to **Content**. -1. Select **Switch me back to the classic editor**. -1. Select **Switch to classic editor** in the confirmation popup to confirm. - -When you switch back to the old editor, any unsaved changes are lost. +1. Select **Edit source**. ### GitLab Flavored Markdown support diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml index 2469c5dd44b..f2c1beb3cc9 100644 --- a/lib/gitlab/database/gitlab_schemas.yml +++ b/lib/gitlab/database/gitlab_schemas.yml @@ -371,6 +371,7 @@ packages_pypi_metadata: :gitlab_main packages_rubygems_metadata: :gitlab_main packages_tags: :gitlab_main pages_deployments: :gitlab_main +pages_deployment_states: :gitlab_main pages_domain_acme_orders: :gitlab_main pages_domains: :gitlab_main partitioned_foreign_keys: :gitlab_main diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 17abab94272..d72dbc44516 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5481,9 +5481,15 @@ msgstr "" msgid "Billing|An error occurred while loading billable members list" msgstr "" +msgid "Billing|An error occurred while loading pending members list" +msgstr "" + msgid "Billing|An error occurred while removing a billable member" msgstr "" +msgid "Billing|Awaiting member signup" +msgstr "" + msgid "Billing|Cannot remove user" msgstr "" @@ -24484,6 +24490,9 @@ msgstr "" msgid "OnDemandScans|The scan could not be canceled." msgstr "" +msgid "OnDemandScans|The scan could not be retried." +msgstr "" + msgid "OnDemandScans|There are no finished scans." msgstr "" diff --git a/scripts/db_migrate b/scripts/db_migrate new file mode 100755 index 00000000000..e001843cd50 --- /dev/null +++ b/scripts/db_migrate @@ -0,0 +1,11 @@ +#!/bin/bash + +root_path="$(cd "$(dirname "$0")/.." || exit ; pwd -P)" + +if [[ -d "${root_path}/ee/" ]]; then + task="db:migrate:main" +else + task="db:migrate" +fi + +eval "bundle exec rake ${task} ${*}" diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index 8538b894869..a47aab1ec70 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -181,7 +181,7 @@ RSpec.describe 'User browses jobs' do name: 'rspec tests', stage: 'test') - create(:ci_job_artifact, :codequality, job: build) + create(:ci_job_artifact, :archive, job: build) end before do diff --git a/spec/frontend/environments/confirm_rollback_modal_spec.js b/spec/frontend/environments/confirm_rollback_modal_spec.js index d62aaec4f69..b699f953945 100644 --- a/spec/frontend/environments/confirm_rollback_modal_spec.js +++ b/spec/frontend/environments/confirm_rollback_modal_spec.js @@ -1,6 +1,9 @@ import { GlModal, GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue'; +import createMockApollo from 'helpers/mock_apollo_helper'; import eventHub from '~/environments/event_hub'; describe('Confirm Rollback Modal Component', () => { @@ -17,6 +20,17 @@ describe('Confirm Rollback Modal Component', () => { modalId: 'test', }; + const envWithLastDeploymentGraphql = { + name: 'test', + lastDeployment: { + commit: { + shortId: 'abc0123', + }, + 'last?': true, + }, + modalId: 'test', + }; + const envWithoutLastDeployment = { name: 'test', modalId: 'test', @@ -26,7 +40,7 @@ describe('Confirm Rollback Modal Component', () => { const retryPath = 'test/-/jobs/123/retry'; - const createComponent = (props = {}) => { + const createComponent = (props = {}, options = {}) => { component = shallowMount(ConfirmRollbackModal, { propsData: { ...props, @@ -34,6 +48,7 @@ describe('Confirm Rollback Modal Component', () => { stubs: { GlSprintf, }, + ...options, }); }; @@ -101,4 +116,121 @@ describe('Confirm Rollback Modal Component', () => { }); }, ); + + describe('graphql', () => { + describe.each` + hasMultipleCommits | environmentData | retryUrl | primaryPropsAttrs + ${true} | ${envWithLastDeploymentGraphql} | ${null} | ${[{ variant: 'danger' }]} + ${false} | ${envWithoutLastDeployment} | ${retryPath} | ${[{ variant: 'danger' }, { 'data-method': 'post' }, { href: retryPath }]} + `( + 'when hasMultipleCommits=$hasMultipleCommits', + ({ hasMultipleCommits, environmentData, retryUrl, primaryPropsAttrs }) => { + Vue.use(VueApollo); + + let apolloProvider; + let rollbackResolver; + + beforeEach(() => { + rollbackResolver = jest.fn(); + apolloProvider = createMockApollo([], { + Mutation: { rollbackEnvironment: rollbackResolver }, + }); + environment = environmentData; + }); + + it('should set contain the commit hash and ask for confirmation', () => { + createComponent( + { + environment: { + ...environment, + lastDeployment: { + ...environment.lastDeployment, + 'last?': false, + }, + }, + hasMultipleCommits, + retryUrl, + graphql: true, + }, + { apolloProvider }, + ); + const modal = component.find(GlModal); + + expect(modal.text()).toContain('commit abc0123'); + expect(modal.text()).toContain('Are you sure you want to continue?'); + }); + + it('should show "Rollback" when isLastDeployment is false', () => { + createComponent( + { + environment: { + ...environment, + lastDeployment: { + ...environment.lastDeployment, + 'last?': false, + }, + }, + hasMultipleCommits, + retryUrl, + graphql: true, + }, + { apolloProvider }, + ); + const modal = component.find(GlModal); + + expect(modal.attributes('title')).toContain('Rollback'); + expect(modal.attributes('title')).toContain('test'); + expect(modal.props('actionPrimary').text).toBe('Rollback'); + expect(modal.props('actionPrimary').attributes).toEqual(primaryPropsAttrs); + }); + + it('should show "Re-deploy" when isLastDeployment is true', () => { + createComponent( + { + environment: { + ...environment, + lastDeployment: { + ...environment.lastDeployment, + 'last?': true, + }, + }, + hasMultipleCommits, + graphql: true, + }, + { apolloProvider }, + ); + + const modal = component.find(GlModal); + + expect(modal.attributes('title')).toContain('Re-deploy'); + expect(modal.attributes('title')).toContain('test'); + expect(modal.props('actionPrimary').text).toBe('Re-deploy'); + }); + + it('should commit the "rollback" mutation when "ok" is clicked', async () => { + const env = { ...environmentData, isLastDeployment: true }; + + createComponent( + { + environment: env, + hasMultipleCommits, + graphql: true, + }, + { apolloProvider }, + ); + + const modal = component.find(GlModal); + modal.vm.$emit('ok'); + + await nextTick(); + expect(rollbackResolver).toHaveBeenCalledWith( + expect.anything(), + { environment: env }, + expect.anything(), + expect.anything(), + ); + }); + }, + ); + }); }); diff --git a/spec/frontend/environments/environment_rollback_spec.js b/spec/frontend/environments/environment_rollback_spec.js index cde675cd9e7..7eff46baaf7 100644 --- a/spec/frontend/environments/environment_rollback_spec.js +++ b/spec/frontend/environments/environment_rollback_spec.js @@ -1,7 +1,11 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import RollbackComponent from '~/environments/components/environment_rollback.vue'; import eventHub from '~/environments/event_hub'; +import setEnvironmentToRollback from '~/environments/graphql/mutations/set_environment_to_rollback.mutation.graphql'; +import createMockApollo from 'helpers/mock_apollo_helper'; describe('Rollback Component', () => { const retryUrl = 'https://gitlab.com/retry'; @@ -50,4 +54,29 @@ describe('Rollback Component', () => { name: 'test', }); }); + + it('should trigger a graphql mutation when graphql is enabled', () => { + Vue.use(VueApollo); + + const apolloProvider = createMockApollo(); + jest.spyOn(apolloProvider.defaultClient, 'mutate'); + const environment = { + name: 'test', + }; + const wrapper = shallowMount(RollbackComponent, { + propsData: { + retryUrl, + graphql: true, + environment, + }, + apolloProvider, + }); + const button = wrapper.find(GlDropdownItem); + button.vm.$emit('click'); + + expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ + mutation: setEnvironmentToRollback, + variables: { environment }, + }); + }); }); diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js index e56b6448b7d..e75d3ac0321 100644 --- a/spec/frontend/environments/graphql/mock_data.js +++ b/spec/frontend/environments/graphql/mock_data.js @@ -469,6 +469,33 @@ export const folder = { stopped_count: 0, }; +export const resolvedEnvironment = { + id: 41, + globalId: 'gid://gitlab/Environment/41', + name: 'review/hello', + state: 'available', + externalUrl: 'https://example.org', + environmentType: 'review', + nameWithoutType: 'hello', + lastDeployment: null, + hasStopAction: false, + rolloutStatus: null, + environmentPath: '/h5bp/html5-boilerplate/-/environments/41', + stopPath: '/h5bp/html5-boilerplate/-/environments/41/stop', + cancelAutoStopPath: '/h5bp/html5-boilerplate/-/environments/41/cancel_auto_stop', + deletePath: '/api/v4/projects/8/environments/41', + folderPath: '/h5bp/html5-boilerplate/-/environments/folders/review', + createdAt: '2021-10-04T19:27:00.527Z', + updatedAt: '2021-10-04T19:27:00.527Z', + canStop: true, + logsPath: '/h5bp/html5-boilerplate/-/logs?environment_name=review%2Fhello', + logsApiPath: '/h5bp/html5-boilerplate/-/logs/k8s.json?environment_name=review%2Fhello', + enableAdvancedLogsQuerying: false, + canDelete: false, + hasOpenedAlert: false, + __typename: 'LocalEnvironment', +}; + export const resolvedFolder = { availableCount: 2, environments: [ diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js index 24fd4e45022..a9fcb5bc907 100644 --- a/spec/frontend/environments/graphql/resolvers_spec.js +++ b/spec/frontend/environments/graphql/resolvers_spec.js @@ -1,19 +1,31 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import { resolvers } from '~/environments/graphql/resolvers'; +import environmentToRollback from '~/environments/graphql/queries/environment_to_rollback.query.graphql'; +import createMockApollo from 'helpers/mock_apollo_helper'; import pollIntervalQuery from '~/environments/graphql/queries/poll_interval.query.graphql'; import { TEST_HOST } from 'helpers/test_constants'; -import { environmentsApp, resolvedEnvironmentsApp, folder, resolvedFolder } from './mock_data'; +import { + environmentsApp, + resolvedEnvironmentsApp, + resolvedEnvironment, + folder, + resolvedFolder, +} from './mock_data'; const ENDPOINT = `${TEST_HOST}/environments`; describe('~/frontend/environments/graphql/resolvers', () => { let mockResolvers; let mock; + let mockApollo; + let localState; beforeEach(() => { mockResolvers = resolvers(ENDPOINT); mock = new MockAdapter(axios); + mockApollo = createMockApollo(); + localState = mockApollo.defaultClient.localState; }); afterEach(() => { @@ -108,4 +120,19 @@ describe('~/frontend/environments/graphql/resolvers', () => { ); }); }); + describe('setEnvironmentToRollback', () => { + it('should write the given environment to the cache', () => { + localState.client.writeQuery = jest.fn(); + mockResolvers.Mutation.setEnvironmentToRollback( + null, + { environment: resolvedEnvironment }, + localState, + ); + + expect(localState.client.writeQuery).toHaveBeenCalledWith({ + query: environmentToRollback, + data: { environmentToRollback: resolvedEnvironment }, + }); + }); + }); }); diff --git a/spec/frontend/issues/show/issue_spec.js b/spec/frontend/issues/show/issue_spec.js index 0de5e7f774f..6d7a31a6c8c 100644 --- a/spec/frontend/issues/show/issue_spec.js +++ b/spec/frontend/issues/show/issue_spec.js @@ -17,7 +17,7 @@ const setupHTML = (initialData) => { }; describe('Issue show index', () => { - describe('initIssueableApp', () => { + describe('initIssuableApp', () => { it('should initialize app with no potential XSS attack', async () => { const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {}); const parseDataSpy = jest.spyOn(parseData, 'parseIssuableData'); diff --git a/spec/frontend/issues_list/components/issuables_list_app_spec.js b/spec/frontend/issues_list/components/issuables_list_app_spec.js index 5ef2a2e0525..11854db534e 100644 --- a/spec/frontend/issues_list/components/issuables_list_app_spec.js +++ b/spec/frontend/issues_list/components/issuables_list_app_spec.js @@ -13,7 +13,7 @@ import createFlash from '~/flash'; import Issuable from '~/issues_list/components/issuable.vue'; import IssuablesListApp from '~/issues_list/components/issuables_list_app.vue'; import { PAGE_SIZE, PAGE_SIZE_MANUAL, RELATIVE_POSITION } from '~/issues_list/constants'; -import issueablesEventBus from '~/issues_list/eventhub'; +import issuablesEventBus from '~/issues_list/eventhub'; import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; jest.mock('~/flash'); @@ -185,8 +185,8 @@ describe('Issuables list component', () => { describe('with bulk editing enabled', () => { beforeEach(() => { - issueablesEventBus.$on.mockReset(); - issueablesEventBus.$emit.mockReset(); + issuablesEventBus.$on.mockReset(); + issuablesEventBus.$emit.mockReset(); setupApiMock(() => [200, MOCK_ISSUES.slice(0)]); factory({ canBulkEdit: true }); @@ -239,19 +239,19 @@ describe('Issuables list component', () => { }); it('broadcasts a message to the bulk edit sidebar when a value is added to selection', () => { - issueablesEventBus.$emit.mockReset(); + issuablesEventBus.$emit.mockReset(); const i1 = wrapper.vm.issuables[1]; wrapper.vm.onSelectIssuable({ issuable: i1, selected: true }); return wrapper.vm.$nextTick().then(() => { - expect(issueablesEventBus.$emit).toHaveBeenCalledTimes(1); - expect(issueablesEventBus.$emit).toHaveBeenCalledWith('issuables:updateBulkEdit'); + expect(issuablesEventBus.$emit).toHaveBeenCalledTimes(1); + expect(issuablesEventBus.$emit).toHaveBeenCalledWith('issuables:updateBulkEdit'); }); }); it('does not broadcast a message to the bulk edit sidebar when a value is not added to selection', () => { - issueablesEventBus.$emit.mockReset(); + issuablesEventBus.$emit.mockReset(); return wrapper.vm .$nextTick() @@ -263,19 +263,19 @@ describe('Issuables list component', () => { }) .then(wrapper.vm.$nextTick) .then(() => { - expect(issueablesEventBus.$emit).toHaveBeenCalledTimes(0); + expect(issuablesEventBus.$emit).toHaveBeenCalledTimes(0); }); }); it('listens to a message to toggle bulk editing', () => { expect(wrapper.vm.isBulkEditing).toBe(false); - expect(issueablesEventBus.$on.mock.calls[0][0]).toBe('issuables:toggleBulkEdit'); - issueablesEventBus.$on.mock.calls[0][1](true); // Call the message handler + expect(issuablesEventBus.$on.mock.calls[0][0]).toBe('issuables:toggleBulkEdit'); + issuablesEventBus.$on.mock.calls[0][1](true); // Call the message handler return waitForPromises() .then(() => { expect(wrapper.vm.isBulkEditing).toBe(true); - issueablesEventBus.$on.mock.calls[0][1](false); + issuablesEventBus.$on.mock.calls[0][1](false); }) .then(() => { expect(wrapper.vm.isBulkEditing).toBe(false); diff --git a/spec/frontend/jobs/components/table/cells/actions_cell_spec.js b/spec/frontend/jobs/components/table/cells/actions_cell_spec.js index b70fca8e9e6..6caf36e1461 100644 --- a/spec/frontend/jobs/components/table/cells/actions_cell_spec.js +++ b/spec/frontend/jobs/components/table/cells/actions_cell_spec.js @@ -58,6 +58,14 @@ describe('Job actions cell', () => { wrapper.destroy(); }); + it('displays the artifacts download button with correct link', () => { + createComponent(playableJob); + + expect(findDownloadArtifactsButton().attributes('href')).toBe( + playableJob.artifacts.nodes[0].downloadPath, + ); + }); + it('does not display an artifacts download button', () => { createComponent(retryableJob); diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js index 86be739751b..45d297ba364 100644 --- a/spec/frontend/jobs/mock_data.js +++ b/spec/frontend/jobs/mock_data.js @@ -1489,15 +1489,18 @@ export const mockJobsQueryResponse = { nodes: [ { downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=trace', + fileType: 'TRACE', __typename: 'CiJobArtifact', }, { downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=metadata', + fileType: 'METADATA', __typename: 'CiJobArtifact', }, { downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=archive', + fileType: 'ARCHIVE', __typename: 'CiJobArtifact', }, ], @@ -1586,7 +1589,16 @@ export const mockJobsQueryEmptyResponse = { }; export const retryableJob = { - artifacts: { nodes: [], __typename: 'CiJobArtifactConnection' }, + artifacts: { + nodes: [ + { + downloadPath: '/root/ci-project/-/jobs/847/artifacts/download?file_type=trace', + fileType: 'TRACE', + __typename: 'CiJobArtifact', + }, + ], + __typename: 'CiJobArtifactConnection', + }, allowFailure: false, status: 'SUCCESS', scheduledAt: null, @@ -1650,7 +1662,18 @@ export const playableJob = { artifacts: { nodes: [ { - downloadPath: '/root/test-job-artifacts/-/jobs/1982/artifacts/download?file_type=trace', + downloadPath: '/root/ci-project/-/jobs/621/artifacts/download?file_type=archive', + fileType: 'ARCHIVE', + __typename: 'CiJobArtifact', + }, + { + downloadPath: '/root/ci-project/-/jobs/621/artifacts/download?file_type=metadata', + fileType: 'METADATA', + __typename: 'CiJobArtifact', + }, + { + downloadPath: '/root/ci-project/-/jobs/621/artifacts/download?file_type=trace', + fileType: 'TRACE', __typename: 'CiJobArtifact', }, ], diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index d50c60774b4..96cbdb468aa 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1679,6 +1679,16 @@ RSpec.describe Repository do expect(blobs.first.name).to eq('foobar') expect(blobs.size).to eq(1) end + + context 'when Gitaly returns NoRepository' do + before do + allow(repository.raw_repository).to receive(:batch_blobs).and_raise(Gitlab::Git::Repository::NoRepository) + end + + it 'returns empty array' do + expect(repository.blobs_at([%w[master foobar]])).to match_array([]) + end + end end describe '#root_ref' do diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index c6b4f50afae..0944bfb6ba6 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -380,7 +380,7 @@ RSpec.describe API::Todos do end end - describe 'POST :id/issuable_type/:issueable_id/todo' do + describe 'POST :id/issuable_type/:issuable_id/todo' do context 'for an issue' do let_it_be(:issuable) do create(:issue, :confidential, project: project_1) |