Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-12-13 18:15:18 +00:00
parent aacba12c6e
commit 999cc13e0a
61 changed files with 675 additions and 172 deletions

View file

@ -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

View file

@ -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

View file

@ -1 +1 @@
8ede1944ec9793c06dba5011234af7f5c5ec92b7
15a1323ae16dffd3ba6b078f6cb81e283a96c72d

View file

@ -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,

View file

@ -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,
});
}
},
},
};

View file

@ -0,0 +1,3 @@
mutation SetEnvironmentToRollback($environment: Environment) {
setEnvironmentToRollback(environment: $environment) @client
}

View file

@ -0,0 +1,7 @@
query environmentToRollback {
environmentToRollback @client {
id
name
lastDeployment
}
}

View file

@ -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)

View file

@ -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
}

View file

@ -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));
},

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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: {

View file

@ -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) {

View file

@ -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: {

View file

@ -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');

View file

@ -19,6 +19,7 @@ query getJobs(
artifacts {
nodes {
downloadPath
fileType
}
}
allowFailure

View file

@ -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

View file

@ -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]

View file

@ -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,

View file

@ -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?)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1 @@
020e17ffd6851fb861a17c1b120ca7cdfa300434d4a9ec923a4edcaa7f951b31

View file

@ -0,0 +1 @@
09a9e7fc042aab19bf768a79401f33b6e7408acff303fc0ee68360dfd7605101

View file

@ -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;

View file

@ -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` |

View file

@ -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)) |

View file

@ -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). |

View file

@ -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).

View file

@ -5,72 +5,87 @@ 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:
```ruby
user = User.find_by_username 'exampleuser'
```
- By user ID:
```ruby
user = User.find(123)
```
- 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. Reset the password
1. Optional. Notify the user that an administrator changed their password:
```ruby
user.password = 'secret_pass'
user.password_confirmation = 'secret_pass'
```
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.send_only_admin_changed_your_password_notification!
```
```ruby
user.send_only_admin_changed_your_password_notification!
```
1. Save the changes:
@ -78,48 +93,32 @@ using user ID and then change password of the user manually.
user.save!
```
1. Exit the console, and then try to sign in with your new password.
1. Exit the console:
NOTE:
You can also reset passwords by using the [Users API](../api/users.md#user-modification).
```ruby
exit
```
## Password reset does not appear to work
## Reset the root password
If you can't sign on with the new password, it might be because of the [reconfirmation feature](../user/upgrade_email_bypass.md).
To reset the root password, follow the steps listed previously.
Try fixing this on the rails console. For example, if your new `root` password isn't working:
- 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.
1. [Start a Rails console](../administration/operations/rails_console.md).
## Troubleshooting
1. Find the user and skip reconfirmation, using any of the methods above:
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. 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.

View file

@ -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.

View file

@ -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:

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -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

View file

@ -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

View file

@ -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 ""

11
scripts/db_migrate Executable file
View file

@ -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} ${*}"

View file

@ -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

View file

@ -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(),
);
});
},
);
});
});

View file

@ -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 },
});
});
});

View file

@ -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: [

View file

@ -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 },
});
});
});
});

View file

@ -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');

View file

@ -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);

View file

@ -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);

View file

@ -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',
},
],

View file

@ -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

View file

@ -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)