Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-08-11 15:10:57 +00:00
parent 4d68d8b937
commit 4f54ec92ed
56 changed files with 997 additions and 229 deletions

View File

@ -1 +1 @@
574ce1641f5de518338c2d4c7fbe17681d741363
3ec72513ce59862f022cf5375f1d4739653f8cee

View File

@ -6,7 +6,8 @@
import { GlModal } from '@gitlab/ui';
import { escape } from 'lodash';
import { s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
import { __, s__, sprintf } from '~/locale';
import eventHub from '../event_hub';
@ -35,6 +36,11 @@ export default {
required: false,
default: true,
},
retryUrl: {
type: String,
required: false,
default: null,
},
},
computed: {
@ -66,6 +72,12 @@ export default {
return this.environment.commitUrl;
},
modalActionText() {
return this.environment.isLastDeployment
? s__('Environments|Re-deploy')
: s__('Environments|Rollback');
},
modalText() {
const linkStart = `<a class="commit-sha mr-0" href="${escape(this.commitUrl)}">`;
const commitId = escape(this.commitShortSha);
@ -90,10 +102,17 @@ export default {
);
},
modalActionText() {
return this.environment.isLastDeployment
? s__('Environments|Re-deploy')
: s__('Environments|Rollback');
primaryProps() {
let attributes = [{ variant: 'danger' }];
if (this.retryUrl) {
attributes = [...attributes, { 'data-method': 'post' }, { href: this.retryUrl }];
}
return {
text: this.modalActionText,
attributes,
};
},
},
@ -114,15 +133,20 @@ export default {
return '';
},
},
csrf,
cancelProps: {
text: __('Cancel'),
attributes: [{ variant: 'danger' }],
},
};
</script>
<template>
<gl-modal
:title="modalTitle"
:visible="visible"
:action-cancel="$options.cancelProps"
:action-primary="primaryProps"
modal-id="confirm-rollback-modal"
:ok-title="modalActionText"
ok-variant="danger"
@ok="onOk"
@change="handleChange"
>

View File

@ -1,7 +1,5 @@
<script>
import { parseBoolean } from '~/lib/utils/common_utils';
import { redirectTo } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
import ConfirmRollbackModal from './confirm_rollback_modal.vue';
export default {
@ -22,10 +20,6 @@ export default {
};
},
mounted() {
eventHub.$on('rollbackEnvironment', () => {
redirectTo(this.retryPath);
});
document.querySelectorAll(this.selector).forEach((button) => {
button.addEventListener('click', (e) => {
e.preventDefault();
@ -57,6 +51,7 @@ export default {
v-model="visible"
:environment="environment"
:has-multiple-commits="false"
:retry-url="retryPath"
/>
<div v-else></div>
</template>

View File

@ -20,7 +20,6 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
import { objectToQuery } from '~/lib/utils/url_utility';
import { s__, __ } from '~/locale';
// import DependencyRow from '~/packages/details/components/dependency_row.vue';
// import PackageListRow from '~/packages/shared/components/package_list_row.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import { packageTypeToTrackCategory } from '~/packages/shared/utils';
import AdditionalMetadata from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
@ -28,6 +27,7 @@ import InstallationCommands from '~/packages_and_registries/package_registry/com
import PackageFiles from '~/packages_and_registries/package_registry/components/details/package_files.vue';
import PackageHistory from '~/packages_and_registries/package_registry/components/details/package_history.vue';
import PackageTitle from '~/packages_and_registries/package_registry/components/details/package_title.vue';
import VersionRow from '~/packages_and_registries/package_registry/components/details/version_row.vue';
import {
PACKAGE_TYPE_NUGET,
PACKAGE_TYPE_COMPOSER,
@ -62,7 +62,7 @@ export default {
GlSprintf,
PackageTitle,
PackagesListLoader,
// PackageListRow,
VersionRow,
// DependencyRow,
PackageHistory,
AdditionalMetadata,
@ -138,7 +138,7 @@ export default {
};
},
hasVersions() {
return this.packageEntity.versions?.length > 0;
return this.packageEntity.versions?.nodes?.length > 0;
},
packageDependencies() {
return this.packageEntity.dependency_links || [];
@ -154,11 +154,6 @@ export default {
formatSize(size) {
return numberToHumanSize(size);
},
getPackageVersions() {
if (!this.packageEntity.versions) {
// this.fetchPackageVersions();
}
},
async deletePackage() {
const { data } = await this.$apollo.mutate({
mutation: destroyPackageMutation,
@ -329,24 +324,13 @@ export default {
</p>
</gl-tab>
<gl-tab
:title="__('Other versions')"
title-item-class="js-versions-tab"
@click="getPackageVersions"
>
<gl-tab :title="__('Other versions')" title-item-class="js-versions-tab">
<template v-if="isLoading && !hasVersions">
<packages-list-loader />
</template>
<template v-else-if="hasVersions">
<!-- <package-list-row
v-for="v in packageEntity.versions"
:key="v.id"
:package-entity="{ name: packageEntity.name, ...v }"
:package-link="v.id.toString()"
:disable-delete="true"
:show-package-type="false"
/> -->
<version-row v-for="v in packageEntity.versions.nodes" :key="v.id" :package-entity="v" />
</template>
<p v-else class="gl-mt-3" data-testid="no-versions-message">

View File

@ -0,0 +1,71 @@
<script>
import { GlLink, GlSprintf, GlTruncate } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import PublishMethod from '~/packages/shared/components/publish_method.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { PACKAGE_DEFAULT_STATUS } from '../../constants';
export default {
name: 'PackageListRow',
components: {
GlLink,
GlSprintf,
GlTruncate,
PackageTags,
PublishMethod,
ListItem,
TimeAgoTooltip,
},
props: {
packageEntity: {
type: Object,
required: true,
},
},
computed: {
packageLink() {
return `${getIdFromGraphQLId(this.packageEntity.id)}`;
},
disabledRow() {
return this.packageEntity.status && this.packageEntity.status !== PACKAGE_DEFAULT_STATUS;
},
},
};
</script>
<template>
<list-item :disabled="disabledRow">
<template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<gl-link :href="packageLink" class="gl-text-body gl-min-w-0" :disabled="disabledRow">
<gl-truncate :text="packageEntity.name" />
</gl-link>
<package-tags
v-if="packageEntity.tags.nodes && packageEntity.tags.nodes.length"
class="gl-ml-3"
:tags="packageEntity.tags.nodes"
hide-label
:tag-display-limit="1"
/>
</div>
</template>
<template #left-secondary>
{{ packageEntity.version }}
</template>
<template #right-primary>
<publish-method :package-entity="packageEntity" />
</template>
<template #right-secondary>
<gl-sprintf :message="__('Created %{timestamp}')">
<template #timestamp>
<time-ago-tooltip :time="packageEntity.createdAt" />
</template>
</gl-sprintf>
</template>
</list-item>
</template>

View File

@ -79,10 +79,10 @@ export const FETCH_PACKAGE_DETAILS_ERROR_MESSAGE = s__(
'PackageRegistry|Failed to load the package data',
);
export const PACKAGE_ERROR_STATUS = 'error';
export const PACKAGE_DEFAULT_STATUS = 'default';
export const PACKAGE_HIDDEN_STATUS = 'hidden';
export const PACKAGE_PROCESSING_STATUS = 'processing';
export const PACKAGE_ERROR_STATUS = 'ERROR';
export const PACKAGE_DEFAULT_STATUS = 'DEFAULT';
export const PACKAGE_HIDDEN_STATUS = 'HIDDEN';
export const PACKAGE_PROCESSING_STATUS = 'PROCESSING';
export const NPM_PACKAGE_MANAGER = 'npm';
export const YARN_PACKAGE_MANAGER = 'yarn';

View File

@ -7,7 +7,10 @@ query getPackageDetails($id: ID!) {
createdAt
updatedAt
status
tags {
project {
path
}
tags(first: 10) {
nodes {
id
name
@ -42,6 +45,21 @@ query getPackageDetails($id: ID!) {
downloadPath
}
}
versions(first: 100) {
nodes {
id
name
createdAt
version
status
tags(first: 1) {
nodes {
id
name
}
}
}
}
metadata {
... on ComposerMetadata {
targetSha

View File

@ -14,7 +14,7 @@ export const initSecurityConfiguration = (el) => {
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
defaultClient: createDefaultClient({}, { assumeImmutableResults: true }),
});
const {

View File

@ -153,16 +153,16 @@
// pseudo-element that is the same size as our element, then
// animate opacity/transform to give a soothing single pulse
.board-column-highlighted::after {
@include gl-focus;
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
top: -1px;
bottom: -1px;
left: -1px;
right: -1px;
pointer-events: none;
opacity: 0;
z-index: -1;
box-shadow: 0 0 6px 3px $blue-200;
border-radius: $border-radius-default;
animation-name: board-column-flash-border;
animation-duration: 1.2s;
animation-fill-mode: forwards;
@ -173,18 +173,11 @@
0%,
100% {
opacity: 0;
transform: scale(0.98);
}
25%,
75% {
opacity: 1;
transform: scale(0.99);
}
50% {
opacity: 1;
transform: scale(1);
}
}

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Types
module Packages
class DependencyLinkMetadataType < BaseUnion
graphql_name 'DependencyLinkMetadata'
description 'Represents metadata associated with a dependency link'
possible_types ::Types::Packages::Nuget::DependencyLinkMetadatumType
def self.resolve_type(object, context)
case object
when ::Packages::Nuget::DependencyLinkMetadatum
::Types::Packages::Nuget::DependencyLinkMetadatumType
else
# NOTE: This method must be kept in sync with `PackageDependencyLinkType#metadata`,
# which must never produce data that this discriminator cannot handle.
raise 'Unsupported metadata type'
end
end
end
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module Types
module Packages
module Nuget
class DependencyLinkMetadatumType < BaseObject
graphql_name 'NugetDependencyLinkMetadata'
description 'Nuget dependency link metadata'
authorize :read_package
field :id, ::Types::GlobalIDType[::Packages::Nuget::DependencyLinkMetadatum], null: false, description: 'ID of the metadatum.'
field :target_framework, GraphQL::Types::String, null: false, description: 'Target framework of the depdency link package.'
end
end
end
end

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
module Types
module Packages
class PackageDependencyLinkType < BaseObject
graphql_name 'PackageDependencyLink'
description 'Represents a package dependency link'
authorize :read_package
field :id, ::Types::GlobalIDType[::Packages::DependencyLink], null: false, description: 'ID of the dependency link.'
field :dependency_type, Types::Packages::PackageDependencyTypeEnum, null: false, description: 'Dependency type.'
field :dependency, Types::Packages::PackageDependencyType, null: true, description: 'Dependency.'
field :metadata, Types::Packages::DependencyLinkMetadataType, null: true, description: 'Dependency link metadata.'
# NOTE: This method must be kept in sync with the union
# type: `Types::Packages::DependencyLinkMetadata`.
#
# `Types::Packages::DependencyLinkMetadata.resolve_type(metadata, ctx)` must never raise.
def metadata
model_class = case object.package.package_type
when 'nuget'
::Packages::Nuget::DependencyLinkMetadatum
end
return unless model_class
# rubocop: disable CodeReuse/ActiveRecord
BatchLoader::GraphQL.for(object.id).batch do |ids, loader|
results = model_class.where(dependency_link_id: ids)
results.each { |record| loader.call(record.dependency_link_id, record) }
end
# rubocop: enable CodeReuse/ActiveRecord
end
def dependency
::Gitlab::Graphql::Loaders::BatchModelLoader.new(::Packages::Dependency, object.dependency_id).find
end
end
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
# this model does not have any kind of authorization so we disable it
# rubocop:disable Graphql/AuthorizeTypes
module Types
module Packages
class PackageDependencyType < BaseObject
graphql_name 'PackageDependency'
description 'Represents a package dependency.'
field :id, ::Types::GlobalIDType[::Packages::Dependency], null: false, description: 'ID of the dependency.'
field :name, GraphQL::Types::String, null: false, description: 'Name of the dependency.'
field :version_pattern, GraphQL::Types::String, null: false, description: 'Version pattern of the dependency.'
end
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Types
module Packages
class PackageDependencyTypeEnum < BaseEnum
graphql_name 'PackageDependencyType'
::Packages::DependencyLink.dependency_types.keys.each do |type|
value type.to_s.underscore.upcase, description: "#{type} dependency type", value: type.to_s
end
end
end
end

View File

@ -12,6 +12,8 @@ module Types
field :package_files, Types::Packages::PackageFileType.connection_type, null: true, description: 'Package files.'
field :dependency_links, Types::Packages::PackageDependencyLinkType.connection_type, null: true, description: 'Dependency link.'
def versions
object.versions
end

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
module Packages
class DependencyLinkPolicy < BasePolicy
delegate { @subject.package }
end
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
module Packages
module Nuget
class DependencyLinkMetadatumPolicy < BasePolicy
delegate { @subject.dependency_link.package }
end
end
end

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/328253
milestone: '14.1'
type: development
group: group::pipeline authoring
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
class RenameTablesCiBuildTraceSection < ActiveRecord::Migration[6.1]
DOWNTIME = false
def change
# Shorten deprecated to dep to avoid 'Index name..too long'
rename_table(:ci_build_trace_sections, :dep_ci_build_trace_sections)
rename_table(:ci_build_trace_section_names, :dep_ci_build_trace_section_names)
end
end

View File

@ -0,0 +1 @@
35475ad862d713055a90af508dba393834ce8aa60eb1abf46b8f9698c2d42276

View File

@ -10574,32 +10574,6 @@ CREATE SEQUENCE ci_build_trace_chunks_id_seq
ALTER SEQUENCE ci_build_trace_chunks_id_seq OWNED BY ci_build_trace_chunks.id;
CREATE TABLE ci_build_trace_section_names (
id integer NOT NULL,
project_id integer NOT NULL,
name character varying NOT NULL
);
CREATE SEQUENCE ci_build_trace_section_names_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE ci_build_trace_section_names_id_seq OWNED BY ci_build_trace_section_names.id;
CREATE TABLE ci_build_trace_sections (
project_id integer NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL,
byte_start bigint NOT NULL,
byte_end bigint NOT NULL,
build_id integer NOT NULL,
section_name_id integer NOT NULL,
build_id_convert_to_bigint bigint DEFAULT 0 NOT NULL
);
CREATE TABLE ci_builds (
id integer NOT NULL,
status character varying,
@ -12303,6 +12277,33 @@ CREATE SEQUENCE dast_sites_id_seq
ALTER SEQUENCE dast_sites_id_seq OWNED BY dast_sites.id;
CREATE TABLE dep_ci_build_trace_section_names (
id integer NOT NULL,
project_id integer NOT NULL,
name character varying NOT NULL
);
CREATE SEQUENCE dep_ci_build_trace_section_names_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE dep_ci_build_trace_section_names_id_seq OWNED BY dep_ci_build_trace_section_names.id;
CREATE TABLE dep_ci_build_trace_sections (
project_id integer NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL,
byte_start bigint NOT NULL,
byte_end bigint NOT NULL,
build_id integer NOT NULL,
section_name_id integer NOT NULL,
build_id_convert_to_bigint bigint DEFAULT 0 NOT NULL
);
CREATE TABLE dependency_proxy_blobs (
id integer NOT NULL,
group_id integer NOT NULL,
@ -20087,8 +20088,6 @@ ALTER TABLE ONLY ci_build_report_results ALTER COLUMN build_id SET DEFAULT nextv
ALTER TABLE ONLY ci_build_trace_chunks ALTER COLUMN id SET DEFAULT nextval('ci_build_trace_chunks_id_seq'::regclass);
ALTER TABLE ONLY ci_build_trace_section_names ALTER COLUMN id SET DEFAULT nextval('ci_build_trace_section_names_id_seq'::regclass);
ALTER TABLE ONLY ci_builds ALTER COLUMN id SET DEFAULT nextval('ci_builds_id_seq'::regclass);
ALTER TABLE ONLY ci_builds_metadata ALTER COLUMN id SET DEFAULT nextval('ci_builds_metadata_id_seq'::regclass);
@ -20237,6 +20236,8 @@ ALTER TABLE ONLY dast_site_validations ALTER COLUMN id SET DEFAULT nextval('dast
ALTER TABLE ONLY dast_sites ALTER COLUMN id SET DEFAULT nextval('dast_sites_id_seq'::regclass);
ALTER TABLE ONLY dep_ci_build_trace_section_names ALTER COLUMN id SET DEFAULT nextval('dep_ci_build_trace_section_names_id_seq'::regclass);
ALTER TABLE ONLY dependency_proxy_blobs ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_blobs_id_seq'::regclass);
ALTER TABLE ONLY dependency_proxy_group_settings ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_group_settings_id_seq'::regclass);
@ -21292,10 +21293,7 @@ ALTER TABLE ONLY ci_build_report_results
ALTER TABLE ONLY ci_build_trace_chunks
ADD CONSTRAINT ci_build_trace_chunks_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ci_build_trace_section_names
ADD CONSTRAINT ci_build_trace_section_names_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ci_build_trace_sections
ALTER TABLE ONLY dep_ci_build_trace_sections
ADD CONSTRAINT ci_build_trace_sections_pkey PRIMARY KEY (build_id, section_name_id);
ALTER TABLE ONLY ci_builds_metadata
@ -21541,6 +21539,9 @@ ALTER TABLE ONLY dast_site_validations
ALTER TABLE ONLY dast_sites
ADD CONSTRAINT dast_sites_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dep_ci_build_trace_section_names
ADD CONSTRAINT dep_ci_build_trace_section_names_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dependency_proxy_blobs
ADD CONSTRAINT dependency_proxy_blobs_pkey PRIMARY KEY (id);
@ -23241,12 +23242,6 @@ CREATE INDEX index_ci_build_report_results_on_project_id ON ci_build_report_resu
CREATE UNIQUE INDEX index_ci_build_trace_chunks_on_build_id_and_chunk_index ON ci_build_trace_chunks USING btree (build_id, chunk_index);
CREATE UNIQUE INDEX index_ci_build_trace_section_names_on_project_id_and_name ON ci_build_trace_section_names USING btree (project_id, name);
CREATE INDEX index_ci_build_trace_sections_on_project_id ON ci_build_trace_sections USING btree (project_id);
CREATE INDEX index_ci_build_trace_sections_on_section_name_id ON ci_build_trace_sections USING btree (section_name_id);
CREATE UNIQUE INDEX index_ci_builds_metadata_on_build_id ON ci_builds_metadata USING btree (build_id);
CREATE INDEX index_ci_builds_metadata_on_build_id_and_has_exposed_artifacts ON ci_builds_metadata USING btree (build_id) WHERE (has_exposed_artifacts IS TRUE);
@ -23647,6 +23642,12 @@ CREATE INDEX index_dast_sites_on_dast_site_validation_id ON dast_sites USING btr
CREATE UNIQUE INDEX index_dast_sites_on_project_id_and_url ON dast_sites USING btree (project_id, url);
CREATE UNIQUE INDEX index_dep_ci_build_trace_section_names_on_project_id_and_name ON dep_ci_build_trace_section_names USING btree (project_id, name);
CREATE INDEX index_dep_ci_build_trace_sections_on_project_id ON dep_ci_build_trace_sections USING btree (project_id);
CREATE INDEX index_dep_ci_build_trace_sections_on_section_name_id ON dep_ci_build_trace_sections USING btree (section_name_id);
CREATE INDEX index_dependency_proxy_blobs_on_group_id_and_file_name ON dependency_proxy_blobs USING btree (group_id, file_name);
CREATE INDEX index_dependency_proxy_group_settings_on_group_id ON dependency_proxy_group_settings USING btree (group_id);
@ -25989,7 +25990,7 @@ CREATE TRIGGER trigger_8485e97c00e3 BEFORE INSERT OR UPDATE ON ci_sources_pipeli
CREATE TRIGGER trigger_8487d4de3e7b BEFORE INSERT OR UPDATE ON ci_builds_metadata FOR EACH ROW EXECUTE FUNCTION trigger_8487d4de3e7b();
CREATE TRIGGER trigger_91dc388a5fe6 BEFORE INSERT OR UPDATE ON ci_build_trace_sections FOR EACH ROW EXECUTE FUNCTION trigger_91dc388a5fe6();
CREATE TRIGGER trigger_91dc388a5fe6 BEFORE INSERT OR UPDATE ON dep_ci_build_trace_sections FOR EACH ROW EXECUTE FUNCTION trigger_91dc388a5fe6();
CREATE TRIGGER trigger_aebe8b822ad3 BEFORE INSERT OR UPDATE ON taggings FOR EACH ROW EXECUTE FUNCTION trigger_aebe8b822ad3();
@ -26133,8 +26134,8 @@ ALTER TABLE ONLY projects
ALTER TABLE ONLY ci_pipelines
ADD CONSTRAINT fk_262d4c2d19 FOREIGN KEY (auto_canceled_by_id) REFERENCES ci_pipelines(id) ON DELETE SET NULL;
ALTER TABLE ONLY ci_build_trace_sections
ADD CONSTRAINT fk_264e112c66 FOREIGN KEY (section_name_id) REFERENCES ci_build_trace_section_names(id) ON DELETE CASCADE;
ALTER TABLE ONLY dep_ci_build_trace_sections
ADD CONSTRAINT fk_264e112c66 FOREIGN KEY (section_name_id) REFERENCES dep_ci_build_trace_section_names(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_27548c6db3 FOREIGN KEY (hashed_storage_migrated_event_id) REFERENCES geo_hashed_storage_migrated_events(id) ON DELETE CASCADE;
@ -26220,7 +26221,7 @@ ALTER TABLE ONLY releases
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_4a99ebfd60 FOREIGN KEY (repositories_changed_event_id) REFERENCES geo_repositories_changed_events(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_sections
ALTER TABLE ONLY dep_ci_build_trace_sections
ADD CONSTRAINT fk_4ebe41f502 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
ALTER TABLE ONLY alert_management_alerts
@ -26517,6 +26518,9 @@ ALTER TABLE ONLY alert_management_alerts
ALTER TABLE ONLY identities
ADD CONSTRAINT fk_aade90f0fc FOREIGN KEY (saml_provider_id) REFERENCES saml_providers(id) ON DELETE CASCADE;
ALTER TABLE ONLY dep_ci_build_trace_sections
ADD CONSTRAINT fk_ab7c104e26 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_sources_pipelines
ADD CONSTRAINT fk_acd9737679 FOREIGN KEY (source_project_id) REFERENCES projects(id) ON DELETE CASCADE;
@ -26787,6 +26791,9 @@ ALTER TABLE ONLY cluster_agents
ALTER TABLE ONLY protected_tag_create_access_levels
ADD CONSTRAINT fk_f7dfda8c51 FOREIGN KEY (protected_tag_id) REFERENCES protected_tags(id) ON DELETE CASCADE;
ALTER TABLE ONLY dep_ci_build_trace_section_names
ADD CONSTRAINT fk_f8cd72cd26 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_stages
ADD CONSTRAINT fk_fb57e6cc56 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
@ -27903,9 +27910,6 @@ ALTER TABLE ONLY merge_request_user_mentions
ALTER TABLE ONLY x509_commit_signatures
ADD CONSTRAINT fk_rails_ab07452314 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_sections
ADD CONSTRAINT fk_rails_ab7c104e26 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY resource_iteration_events
ADD CONSTRAINT fk_rails_abf5d4affa FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
@ -28365,9 +28369,6 @@ ALTER TABLE ONLY issues_self_managed_prometheus_alert_events
ALTER TABLE ONLY merge_requests_closing_issues
ADD CONSTRAINT fk_rails_f8540692be FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_section_names
ADD CONSTRAINT fk_rails_f8cd72cd26 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY merge_trains
ADD CONSTRAINT fk_rails_f90820cb08 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE SET NULL;

View File

@ -200,7 +200,7 @@ successfully, you must replicate their data using some other means.
|[Package Registry for generic packages](../../../user/packages/generic_packages/index.md) | **Yes** (13.5) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
|[Versioned Terraform State](../../terraform_state.md) | **Yes** (13.5) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.12) | Via Object Storage provider if supported. Native Geo support (Beta). | Replication is behind the feature flag `geo_terraform_state_version_replication`, enabled by default. Verification was behind the feature flag `geo_terraform_state_version_verification`, which was removed in 14.0|
|[External merge request diffs](../../merge_request_diffs.md) | **Yes** (13.5) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Replication is behind the feature flag `geo_merge_request_diff_replication`, enabled by default. Verification is under development, behind the feature flag `geo_merge_request_diff_verification`, introduced in 14.0.|
|[Versioned snippets](../../../user/snippets.md#versioned-snippets) | [**Yes** (13.7)](https://gitlab.com/groups/gitlab-org/-/epics/2809) | [**Yes**](https://gitlab.com/groups/gitlab-org/-/epics/2810) | No | Verification was implemented behind the feature flag `geo_snippet_repository_verification` in 13.11, and the feature flag was removed in 14.2. |
|[Versioned snippets](../../../user/snippets.md#versioned-snippets) | [**Yes** (13.7)](https://gitlab.com/groups/gitlab-org/-/epics/2809) | [**Yes** (14.2)](https://gitlab.com/groups/gitlab-org/-/epics/2810) | No | Verification was implemented behind the feature flag `geo_snippet_repository_verification` in 13.11, and the feature flag was removed in 14.2. |
|[Server-side Git hooks](../../server_hooks.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1867) | No | No | |
|[Elasticsearch integration](../../../integration/elasticsearch.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/1186) | No | No | |
|[GitLab Pages](../../pages/index.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/589) | No | Via Object Storage provider if supported. **No** native Geo support (Beta). | |

View File

@ -323,7 +323,7 @@ See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-foss/-/mer
### LFS commands fail on TLS v1.3 server
If you configure GitLab to [disable TLS v1.2](https://docs.gitlab.com/omnibus/settings/nginx.md)
If you configure GitLab to [disable TLS v1.2](https://docs.gitlab.com/omnibus/settings/nginx.html)
and only enable TLS v1.3 connections, LFS operations require a
[Git LFS client](https://git-lfs.github.com) version 2.11.0 or later. If you use
a Git LFS client earlier than version 2.11.0, GitLab displays an error:
@ -334,7 +334,7 @@ error: failed to fetch some objects from 'https://username:[MASKED]@gitlab.examp
```
When using GitLab CI over a TLS v1.3 configured GitLab server, you must
[upgrade to GitLab Runner](https://docs.gitlab.com/runner/install/index.md) 13.2.0
[upgrade to GitLab Runner](https://docs.gitlab.com/runner/install/index.html) 13.2.0
or later to receive an updated Git LFS client version via
the included [GitLab Runner Helper image](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#helper-image).

View File

@ -303,6 +303,63 @@ If you enable Monitoring, it must be enabled on **all** database servers.
1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
#### Enable TLS support for the Patroni API
By default, Patroni's [REST API](https://patroni.readthedocs.io/en/latest/rest_api.html#rest-api) is served over HTTP.
You have the option to enable TLS and use HTTPS over the same [port](https://docs.gitlab.com/omnibus/package-information/defaults.html#patroni).
To enable TLS, you need PEM-formatted certificate and private key files. Both files must be readable by the PostgreSQL user (`gitlab-psql` by default, or the one set by `postgresql['username']`):
```ruby
patroni['tls_certificate_file'] = '/path/to/server/certificate.pem'
patroni['tls_key_file'] = '/path/to/server/key.pem'
```
If the server's private key is encrypted, specify the password to decrypt it:
```ruby
patroni['tls_key_password'] = 'private-key-password' # This is the plain-text password.
```
If you are using a self-signed certificate or an internal CA, you need to either disable the TLS verification or pass the certificate of the
internal CA, otherwise you may run into an unexpected error when using the `gitlab-ctl patroni ....` commands. Omnibus ensures that Patroni API
clients honor this configuration.
TLS certificate verification is enabled by default. To disable it:
```ruby
patroni['tls_verify'] = false
```
Alternatively, you can pass a PEM-formatted certificate of the internal CA. Again, the file must be readable by the PostgreSQL user:
```ruby
patroni['tls_ca_file'] = '/path/to/ca.pem'
```
When TLS is enabled, mutual authentication of the API server and client is possible for all endpoints, the extent of which depends on
the `patroni['tls_client_mode']` attribute:
- `none` (default): the API will not check for any client certificates.
- `optional`: client certificates are required for all [unsafe](https://patroni.readthedocs.io/en/latest/security.html#protecting-the-rest-api) API calls.
- `required`: client certificates are required for all API calls.
The client certificates are verified against the CA certificate that is specified with the `patroni['tls_ca_file']` attribute. Therefore,
this attribute is required for mutual TLS authentication. You also need to specify PEM-formatted client certificate and private key files.
Both files must be readable by the PostgreSQL user:
```ruby
patroni['tls_client_mode'] = 'required'
patroni['tls_ca_file'] = '/path/to/ca.pem'
patroni['tls_client_certificate_file'] = '/path/to/client/certificate.pem'
patroni['tls_client_key_file'] = '/path/to/client/key.pem'
```
You can use different certificates and keys for both API server and client on different Patroni nodes as long as they can be verified.
However, the CA certificate (`patroni['tls_ca_file']`), TLS certificate verification (`patroni['tls_verify']`), and client TLS
authentication mode (`patroni['tls_client_mode']`), must each have the same value on all nodes.
### Configuring the PgBouncer node
1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information), [`CONSUL_PASSWORD_HASH`](#consul-information), and [`PGBOUNCER_PASSWORD_HASH`](#pgbouncer-information) before executing the next step.

View File

@ -285,5 +285,5 @@ The output in `/tmp/puma.txt` may help diagnose the root cause.
## More information
- [Debugging Stuck Ruby Processes](https://newrelic.com/blog/engineering/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9/)
- [Debugging Stuck Ruby Processes](https://newrelic.com/blog/best-practices/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9)
- [Cheat sheet of using GDB and Ruby processes](gdb-stuck-ruby.txt)

View File

@ -6237,6 +6237,29 @@ The connection type for [`Package`](#package).
| <a id="packageconnectionnodes"></a>`nodes` | [`[Package]`](#package) | A list of nodes. |
| <a id="packageconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `PackageDependencyLinkConnection`
The connection type for [`PackageDependencyLink`](#packagedependencylink).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencylinkconnectionedges"></a>`edges` | [`[PackageDependencyLinkEdge]`](#packagedependencylinkedge) | A list of edges. |
| <a id="packagedependencylinkconnectionnodes"></a>`nodes` | [`[PackageDependencyLink]`](#packagedependencylink) | A list of nodes. |
| <a id="packagedependencylinkconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `PackageDependencyLinkEdge`
The edge type for [`PackageDependencyLink`](#packagedependencylink).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencylinkedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="packagedependencylinkedgenode"></a>`node` | [`PackageDependencyLink`](#packagedependencylink) | The item at the end of the edge. |
#### `PackageEdge`
The edge type for [`Package`](#package).
@ -11330,6 +11353,17 @@ Represents the network policy.
| <a id="notepermissionsrepositionnote"></a>`repositionNote` | [`Boolean!`](#boolean) | Indicates the user can perform `reposition_note` on this resource. |
| <a id="notepermissionsresolvenote"></a>`resolveNote` | [`Boolean!`](#boolean) | Indicates the user can perform `resolve_note` on this resource. |
### `NugetDependencyLinkMetadata`
Nuget dependency link metadata.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="nugetdependencylinkmetadataid"></a>`id` | [`PackagesNugetDependencyLinkMetadatumID!`](#packagesnugetdependencylinkmetadatumid) | ID of the metadatum. |
| <a id="nugetdependencylinkmetadatatargetframework"></a>`targetFramework` | [`String!`](#string) | Target framework of the depdency link package. |
### `NugetMetadata`
Nuget metadata.
@ -11401,6 +11435,31 @@ Represents a composer JSON file.
| <a id="packagecomposerjsontypetype"></a>`type` | [`String`](#string) | The type set in the Composer JSON file. |
| <a id="packagecomposerjsontypeversion"></a>`version` | [`String`](#string) | The version set in the Composer JSON file. |
### `PackageDependency`
Represents a package dependency.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencyid"></a>`id` | [`PackagesDependencyID!`](#packagesdependencyid) | ID of the dependency. |
| <a id="packagedependencyname"></a>`name` | [`String!`](#string) | Name of the dependency. |
| <a id="packagedependencyversionpattern"></a>`versionPattern` | [`String!`](#string) | Version pattern of the dependency. |
### `PackageDependencyLink`
Represents a package dependency link.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencylinkdependency"></a>`dependency` | [`PackageDependency`](#packagedependency) | Dependency. |
| <a id="packagedependencylinkdependencytype"></a>`dependencyType` | [`PackageDependencyType!`](#packagedependencytype) | Dependency type. |
| <a id="packagedependencylinkid"></a>`id` | [`PackagesDependencyLinkID!`](#packagesdependencylinkid) | ID of the dependency link. |
| <a id="packagedependencylinkmetadata"></a>`metadata` | [`DependencyLinkMetadata`](#dependencylinkmetadata) | Dependency link metadata. |
### `PackageDetailsType`
Represents a package details in the Package Registry. Note that this type is in beta and susceptible to changes.
@ -11410,6 +11469,7 @@ Represents a package details in the Package Registry. Note that this type is in
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedetailstypecreatedat"></a>`createdAt` | [`Time!`](#time) | Date of creation. |
| <a id="packagedetailstypedependencylinks"></a>`dependencyLinks` | [`PackageDependencyLinkConnection`](#packagedependencylinkconnection) | Dependency link. (see [Connections](#connections)) |
| <a id="packagedetailstypeid"></a>`id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. |
| <a id="packagedetailstypemetadata"></a>`metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. |
| <a id="packagedetailstypename"></a>`name` | [`String!`](#string) | Name of the package. |
@ -15330,6 +15390,15 @@ Rotation length unit of an on-call rotation.
| <a id="oncallrotationunitenumhours"></a>`HOURS` | Hours. |
| <a id="oncallrotationunitenumweeks"></a>`WEEKS` | Weeks. |
### `PackageDependencyType`
| Value | Description |
| ----- | ----------- |
| <a id="packagedependencytypebundle_dependencies"></a>`BUNDLE_DEPENDENCIES` | bundleDependencies dependency type. |
| <a id="packagedependencytypedependencies"></a>`DEPENDENCIES` | dependencies dependency type. |
| <a id="packagedependencytypedev_dependencies"></a>`DEV_DEPENDENCIES` | devDependencies dependency type. |
| <a id="packagedependencytypepeer_dependencies"></a>`PEER_DEPENDENCIES` | peerDependencies dependency type. |
### `PackageGroupSort`
Values for sorting group packages.
@ -16204,12 +16273,30 @@ A `PackagesConanMetadatumID` is a global ID. It is encoded as a string.
An example `PackagesConanMetadatumID` is: `"gid://gitlab/Packages::Conan::Metadatum/1"`.
### `PackagesDependencyID`
A `PackagesDependencyID` is a global ID. It is encoded as a string.
An example `PackagesDependencyID` is: `"gid://gitlab/Packages::Dependency/1"`.
### `PackagesDependencyLinkID`
A `PackagesDependencyLinkID` is a global ID. It is encoded as a string.
An example `PackagesDependencyLinkID` is: `"gid://gitlab/Packages::DependencyLink/1"`.
### `PackagesMavenMetadatumID`
A `PackagesMavenMetadatumID` is a global ID. It is encoded as a string.
An example `PackagesMavenMetadatumID` is: `"gid://gitlab/Packages::Maven::Metadatum/1"`.
### `PackagesNugetDependencyLinkMetadatumID`
A `PackagesNugetDependencyLinkMetadatumID` is a global ID. It is encoded as a string.
An example `PackagesNugetDependencyLinkMetadatumID` is: `"gid://gitlab/Packages::Nuget::DependencyLinkMetadatum/1"`.
### `PackagesNugetMetadatumID`
A `PackagesNugetMetadatumID` is a global ID. It is encoded as a string.
@ -16339,6 +16426,14 @@ abstract types.
### Unions
#### `DependencyLinkMetadata`
Represents metadata associated with a dependency link.
One of:
- [`NugetDependencyLinkMetadata`](#nugetdependencylinkmetadata)
#### `Issuable`
Represents an issuable.

View File

@ -115,11 +115,15 @@ We also use two secure variables:
## Storing API keys
To add secure variables, navigate to your project's
**Settings > CI/CD > Variables**. The variables defined
in the project settings are sent along with the build script to the runner.
To store API keys as secure variables:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Variables**.
The variables defined in the project settings are sent along with the build script to the runner.
The secure variables are stored out of the repository. Never store secrets in
your project's `.gitlab-ci.yml`. It is also important that the secret's value
your project's `.gitlab-ci.yml` file. It is also important that the secret's value
is hidden in the job log.
You access added variable by prefixing it's name with `$` (on non-Windows runners)
@ -128,4 +132,4 @@ or `%` (for Windows Batch runners):
- `$VARIABLE`: Use for non-Windows runners
- `%VARIABLE%`: Use for Windows Batch runners
Read more about the [CI/CD variables](../../variables/index.md).
Read more about [CI/CD variables](../../variables/index.md).

View File

@ -1471,6 +1471,7 @@ in the project.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47063) in GitLab 12.2.
> - In GitLab 12.3, maximum number of jobs in `needs` array raised from five to 50.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30631) in GitLab 12.8, `needs: []` lets jobs start immediately.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30632) in GitLab 14.2, you can refer to jobs in the same stage as the job you are configuring.
Use `needs:` to execute jobs out-of-order. Relationships between jobs
that use `needs` can be visualized as a [directed acyclic graph](../directed_acyclic_graph/index.md).
@ -1531,15 +1532,10 @@ production:
#### Requirements and limitations
- In [GitLab 14.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/30632) you
can refer to jobs in the same stage as the job you are configuring. This feature is:
- [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
- Disabled on GitLab.com.
- Not recommended for production use.
For GitLab self-managed instances, GitLab administrators can choose to
[enable it](#enable-or-disable-needs-for-jobs-in-the-same-stage).
can refer to jobs in the same stage as the job you are configuring. This feature is
enabled on GitLab.com and ready for production use. On self-managed [GitLab 14.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/30632)
this feature is available by default. To hide the feature, ask an administrator to
[disable the `ci_same_stage_job_needs` flag](../../administration/feature_flags.md).
- In GitLab 14.0 and older, you can only refer to jobs in earlier stages.
- In GitLab 13.9 and older, if `needs:` refers to a job that might not be added to
a pipeline because of `only`, `except`, or `rules`, the pipeline might fail to create.
@ -1557,22 +1553,6 @@ production:
- Stages must be explicitly defined for all jobs
that have the keyword `needs:` or are referred to by one.
##### Enable or disable `needs` for jobs in the same stage **(FREE SELF)**
`needs` for jobs in the same stage is under development but ready for production use.
It is deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails
console](../../administration/feature_flags.md)
can opt to disable it.
To enable it:
`Feature.enable(:ci_same_stage_job_needs)`
To disable it:
`Feature.disable(:ci_same_stage_job_needs)`
##### Changing the `needs:` job limit **(FREE SELF)**
The maximum number of jobs that can be defined in `needs:` defaults to 50.

View File

@ -122,7 +122,7 @@ Title case.
## GitLab
Do not make possessive (GitLab's). This guidance follows [GitLab Brand Guidelines](https://about.gitlab.com/handbook/marketing/corporate-marketing/brand-activation/brand-guidelines/#trademark).
Do not make possessive (GitLab's). This guidance follows [GitLab Trademark Guidelines](https://about.gitlab.com/handbook/marketing/corporate-marketing/brand-activation/trademark-guidelines/).
## GitLab.com

View File

@ -338,6 +338,16 @@ Page::Project::Pipeline::Show.perform do |pipeline|
end
```
### Use `eventually_` matchers for expectations that require waiting
When something requires waiting to be matched, use `eventually_` matchers with clear wait duration definition.
`Eventually` matchers use the following naming pattern: `eventually_${rspec_matcher_name}`. They are defined in [eventually_matcher.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/spec/support/matchers/eventually_matcher.rb).
```ruby
expect { async_value }.to eventually_eq(value).within(max_duration: 120, max_attempts: 60, reload_page: page)
```
### Create negatable matchers to speed `expect` checks
However, sometimes we want to check that something is _not_ as we _don't_ want it to be. In other

View File

@ -85,11 +85,15 @@ entire content by selecting **Show file contents**.
## Ignore whitespace changes in Merge Request diff view
If you select the **Hide whitespace changes** button, you can see the diff
without whitespace changes (if there are any). This is also working when on a
specific commit page.
Whitespace changes can make it more difficult to see the substantive changes in
a merge request. You can choose to hide or show whitespace changes:
![MR diff](img/merge_request_diff.png)
1. Go to your merge request, and select the **Changes** tab.
1. Above the list of changed files, select **(settings)** **Preferences** to
display the preferences menu.
1. Select or deselect **Show whitespace changes**:
![MR diff](img/merge_request_diff_v14_2.png)
## Mark files as viewed

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -16,11 +16,8 @@ module Gitlab
environment coverage retry parallel interruptible timeout
release dast_configuration secrets].freeze
REQUIRED_BY_NEEDS = %i[stage].freeze
validations do
validates :config, allowed_keys: ALLOWED_KEYS + PROCESSABLE_ALLOWED_KEYS
validates :config, required_keys: REQUIRED_BY_NEEDS, if: :has_needs?
validates :script, presence: true
with_options allow_nil: true do

View File

@ -9198,7 +9198,7 @@ msgstr ""
msgid "CorpusManagement|Latest Job:"
msgstr ""
msgid "CorpusManagement|New corpus needs to be a upload in *.zip format. Maximum 10Gib"
msgid "CorpusManagement|New corpus needs to be a upload in *.zip format. Maximum 10GB"
msgstr ""
msgid "CorpusManagement|New upload"
@ -10737,6 +10737,9 @@ msgstr ""
msgid "Delete comment"
msgstr ""
msgid "Delete corpus"
msgstr ""
msgid "Delete domain"
msgstr ""

View File

@ -81,7 +81,9 @@ module QA
Page::Group::BulkImport.perform do |import_page|
imported_group
expect { imported_group.import_status }.to eventually_eq('finished').within(duration: 300, interval: 2)
expect { imported_group.import_status }.to(
eventually_eq('finished').within(max_duration: 300, sleep_interval: 2)
)
aggregate_failures do
expect(imported_group.reload!).to eq(source_group)

View File

@ -33,7 +33,7 @@ module QA
it 'imports Github repo via api', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1858' do
imported_project # import the project
expect { imported_project.reload!.import_status }.to eventually_eq('finished').within(duration: 90)
expect { imported_project.reload!.import_status }.to eventually_eq('finished').within(max_duration: 90)
aggregate_failures do
verify_repository_import

View File

@ -138,7 +138,7 @@ module QA
raise "Import of '#{imported_project.name}' failed!" if status == 'failed'
end
end
expect(import_status).to eventually_eq('finished').within(duration: import_max_duration, interval: 30)
expect(import_status).to eventually_eq('finished').within(max_duration: import_max_duration, sleep_interval: 30)
@import_time = Time.now - start
aggregate_failures do

View File

@ -2,7 +2,7 @@
module QA
RSpec.describe 'Configure', except: { job: 'review-qa-*' } do
describe 'Kubernetes Cluster Integration', :requires_admin, :skip_live_env do
describe 'Kubernetes Cluster Integration', :requires_admin, :skip_live_env, :smoke do
context 'Project Clusters' do
let!(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3s).create! }
let(:project) do

View File

@ -9,7 +9,7 @@
# expect { Something.that.takes.time.to_appear }.not_to eventually_eq(expected_result)
#
# With duration and attempts override
# expect { Something.that.takes.time.to_appear }.to eventually_eq(expected_result).within(duration: 10, attempts: 5)
# expect { Something.that.takes.time.to_appear }.to eventually_eq(expected_result).within(max_duration: 10, max_attempts: 5)
module Matchers
%w[
@ -21,11 +21,9 @@ module Matchers
be_empty
].each do |op|
RSpec::Matchers.define(:"eventually_#{op}") do |*expected|
chain(:within) do |options = {}|
@duration = options[:duration]
@attempts = options[:attempts]
@interval = options[:interval]
@reload_page = options[:reload_page]
chain(:within) do |kwargs = {}|
@retry_args = kwargs
@retry_args[:sleep_interval] = 0.5 unless @retry_args[:sleep_interval]
end
def supports_block_expectations?
@ -57,12 +55,7 @@ module Matchers
attempt = 0
QA::Runtime::Logger.debug("Running eventually matcher with '#{operator_msg}' operator")
QA::Support::Retrier.retry_until(
max_attempts: @attempts,
max_duration: @duration,
sleep_interval: @interval || 0.5,
reload_page: @reload_page
) do
QA::Support::Retrier.retry_until(**@retry_args) do
QA::Runtime::Logger.debug("evaluating expectation, attempt: #{attempt += 1}")
public_send(expectation_name, actual)

View File

@ -99,6 +99,48 @@
"status": {
"type": ["string"],
"enum": ["DEFAULT", "HIDDEN", "PROCESSING", "ERROR"]
},
"dependencyLinks": {
"type": "object",
"additionalProperties": false,
"properties": {
"pageInfo": { "type": "object" },
"edges": { "type": "array" },
"nodes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"dependencyType": {
"type": "string"
},
"dependency": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"versionPattern": {
"type": "string"
}
}
},
"metadata": {
"anyOf": [
{ "$ref": "./package_nuget_dependency_link_metadata.json" },
{ "type": "null" }
]
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,12 @@
{
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"targetFramework": {
"type": "string"
}
}
}

View File

@ -23,66 +23,73 @@ describe('Confirm Rollback Modal Component', () => {
commitUrl: 'test/-/commit/abc0123',
};
const retryPath = 'test/-/jobs/123/retry';
describe.each`
hasMultipleCommits | environmentData
${true} | ${envWithLastDeployment}
${false} | ${envWithoutLastDeployment}
`('when hasMultipleCommits=$hasMultipleCommits', ({ hasMultipleCommits, environmentData }) => {
beforeEach(() => {
environment = environmentData;
});
hasMultipleCommits | environmentData | retryUrl | primaryPropsAttrs
${true} | ${envWithLastDeployment} | ${null} | ${[{ variant: 'danger' }]}
${false} | ${envWithoutLastDeployment} | ${retryPath} | ${[{ variant: 'danger' }, { 'data-method': 'post' }, { href: retryPath }]}
`(
'when hasMultipleCommits=$hasMultipleCommits',
({ hasMultipleCommits, environmentData, retryUrl, primaryPropsAttrs }) => {
beforeEach(() => {
environment = environmentData;
});
it('should show "Rollback" when isLastDeployment is false', () => {
const component = shallowMount(ConfirmRollbackModal, {
propsData: {
environment: {
...environment,
isLastDeployment: false,
it('should show "Rollback" when isLastDeployment is false', () => {
const component = shallowMount(ConfirmRollbackModal, {
propsData: {
environment: {
...environment,
isLastDeployment: false,
},
hasMultipleCommits,
retryUrl,
},
hasMultipleCommits,
},
});
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);
expect(modal.text()).toContain('commit abc0123');
expect(modal.text()).toContain('Are you sure you want to continue?');
});
const modal = component.find(GlModal);
expect(modal.attributes('title')).toContain('Rollback');
expect(modal.attributes('title')).toContain('test');
expect(modal.attributes('ok-title')).toBe('Rollback');
expect(modal.text()).toContain('commit abc0123');
expect(modal.text()).toContain('Are you sure you want to continue?');
});
it('should show "Re-deploy" when isLastDeployment is true', () => {
const component = shallowMount(ConfirmRollbackModal, {
propsData: {
environment: {
...environment,
isLastDeployment: true,
it('should show "Re-deploy" when isLastDeployment is true', () => {
const component = shallowMount(ConfirmRollbackModal, {
propsData: {
environment: {
...environment,
isLastDeployment: true,
},
hasMultipleCommits,
},
hasMultipleCommits,
},
});
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');
expect(modal.text()).toContain('commit abc0123');
expect(modal.text()).toContain('Are you sure you want to continue?');
});
const modal = component.find(GlModal);
expect(modal.attributes('title')).toContain('Re-deploy');
expect(modal.attributes('title')).toContain('test');
expect(modal.attributes('ok-title')).toBe('Re-deploy');
expect(modal.text()).toContain('commit abc0123');
expect(modal.text()).toContain('Are you sure you want to continue?');
});
it('should emit the "rollback" event when "ok" is clicked', () => {
const env = { ...environmentData, isLastDeployment: true };
const component = shallowMount(ConfirmRollbackModal, {
propsData: {
environment: env,
hasMultipleCommits,
},
});
const eventHubSpy = jest.spyOn(eventHub, '$emit');
const modal = component.find(GlModal);
modal.vm.$emit('ok');
it('should emit the "rollback" event when "ok" is clicked', () => {
const env = { ...environmentData, isLastDeployment: true };
const component = shallowMount(ConfirmRollbackModal, {
propsData: {
environment: env,
hasMultipleCommits,
},
expect(eventHubSpy).toHaveBeenCalledWith('rollbackEnvironment', env);
});
const eventHubSpy = jest.spyOn(eventHub, '$emit');
const modal = component.find(GlModal);
modal.vm.$emit('ok');
expect(eventHubSpy).toHaveBeenCalledWith('rollbackEnvironment', env);
});
});
},
);
});

View File

@ -0,0 +1,101 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VersionRow renders 1`] = `
<div
class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100"
>
<div
class="gl-display-flex gl-align-items-center gl-py-3 gl-px-5"
>
<!---->
<div
class="gl-display-flex gl-xs-flex-direction-column gl-justify-content-space-between gl-align-items-stretch gl-flex-grow-1"
>
<div
class="gl-display-flex gl-flex-direction-column gl-xs-mb-3 gl-min-w-0 gl-flex-grow-1"
>
<div
class="gl-display-flex gl-align-items-center gl-text-body gl-font-weight-bold gl-min-h-6 gl-min-w-0"
>
<div
class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0"
>
<gl-link-stub
class="gl-text-body gl-min-w-0"
href="243"
>
<span
class="gl-truncate"
title="@gitlab-org/package-15"
>
<span
class="gl-truncate-end"
>
@gitlab-org/package-15
</span>
</span>
</gl-link-stub>
<package-tags-stub
class="gl-ml-3"
hidelabel="true"
tagdisplaylimit="1"
tags="[object Object],[object Object],[object Object]"
/>
</div>
<!---->
</div>
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-min-h-6 gl-min-w-0 gl-flex-grow-1"
>
1.0.1
</div>
</div>
<div
class="gl-display-flex gl-flex-direction-column gl-sm-align-items-flex-end gl-justify-content-space-between gl-text-gray-500 gl-flex-shrink-0"
>
<div
class="gl-display-flex gl-align-items-center gl-sm-text-body gl-sm-font-weight-bold gl-min-h-6"
>
<publish-method-stub
packageentity="[object Object]"
/>
</div>
<div
class="gl-display-flex gl-align-items-center gl-min-h-6"
>
Created
<time-ago-tooltip-stub
cssclass=""
time="2021-08-10T09:33:54Z"
tooltipplacement="top"
/>
</div>
</div>
</div>
<!---->
</div>
<div
class="gl-display-flex"
>
<div
class="gl-w-7"
/>
<!---->
<div
class="gl-w-9"
/>
</div>
</div>
`;

View File

@ -14,6 +14,7 @@ import InstallationCommands from '~/packages_and_registries/package_registry/com
import PackageFiles from '~/packages_and_registries/package_registry/components/details/package_files.vue';
import PackageHistory from '~/packages_and_registries/package_registry/components/details/package_history.vue';
import PackageTitle from '~/packages_and_registries/package_registry/components/details/package_title.vue';
import VersionRow from '~/packages_and_registries/package_registry/components/details/version_row.vue';
import {
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
DELETE_PACKAGE_ERROR_MESSAGE,
@ -28,6 +29,7 @@ import getPackageDetails from '~/packages_and_registries/package_registry/graphq
import {
packageDetailsQuery,
packageData,
packageVersions,
emptyPackageDetailsQuery,
packageDestroyMutation,
packageDestroyMutationError,
@ -96,6 +98,8 @@ describe('PackagesApp', () => {
const findDeleteButton = () => wrapper.findByTestId('delete-package');
const findPackageFiles = () => wrapper.findComponent(PackageFiles);
const findDeleteFileModal = () => wrapper.findByTestId('delete-file-modal');
const findVersionRows = () => wrapper.findAllComponents(VersionRow);
const noVersionsMessage = () => wrapper.findByTestId('no-versions-message');
afterEach(() => {
wrapper.destroy();
@ -362,4 +366,39 @@ describe('PackagesApp', () => {
});
});
});
describe('versions', () => {
it('displays the correct version count when the package has versions', async () => {
createComponent();
await waitForPromises();
expect(findVersionRows()).toHaveLength(packageVersions().length);
});
it('binds the correct props', async () => {
const [versionPackage] = packageVersions();
// eslint-disable-next-line no-underscore-dangle
delete versionPackage.__typename;
delete versionPackage.tags;
createComponent();
await waitForPromises();
expect(findVersionRows().at(0).props()).toMatchObject({
packageEntity: expect.objectContaining(versionPackage),
});
});
it('displays the no versions message when there are none', async () => {
createComponent({
resolver: jest.fn().mockResolvedValue(packageDetailsQuery({ versions: { nodes: [] } })),
});
await waitForPromises();
expect(noVersionsMessage().exists()).toBe(true);
});
});
});

View File

@ -0,0 +1,89 @@
import { GlLink, GlSprintf, GlTruncate } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import PublishMethod from '~/packages/shared/components/publish_method.vue';
import VersionRow from '~/packages_and_registries/package_registry/components/details/version_row.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { packageVersions } from '../../mock_data';
const packageVersion = packageVersions()[0];
describe('VersionRow', () => {
let wrapper;
const findListItem = () => wrapper.findComponent(ListItem);
const findLink = () => wrapper.findComponent(GlLink);
const findPackageTags = () => wrapper.findComponent(PackageTags);
const findPublishMethod = () => wrapper.findComponent(PublishMethod);
const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip);
function createComponent(packageEntity = packageVersion) {
wrapper = shallowMountExtended(VersionRow, {
propsData: {
packageEntity,
},
stubs: {
ListItem,
GlSprintf,
GlTruncate,
},
});
}
afterEach(() => {
wrapper.destroy();
});
it('renders', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
it('has a link to the version detail', () => {
createComponent();
expect(findLink().attributes('href')).toBe(`${getIdFromGraphQLId(packageVersion.id)}`);
expect(findLink().text()).toBe(packageVersion.name);
});
it('has the version of the package', () => {
createComponent();
expect(wrapper.text()).toContain(packageVersion.version);
});
it('has a package tags component', () => {
createComponent();
expect(findPackageTags().props('tags')).toBe(packageVersion.tags.nodes);
});
it('has a publish method component', () => {
createComponent();
expect(findPublishMethod().props('packageEntity')).toBe(packageVersion);
});
it('has a time-ago tooltip', () => {
createComponent();
expect(findTimeAgoTooltip().props('time')).toBe(packageVersion.createdAt);
});
describe('disabled status', () => {
it('disables the list item', () => {
createComponent({ ...packageVersion, status: 'something' });
expect(findListItem().props('disabled')).toBe(true);
});
it('disables the link', () => {
createComponent({ ...packageVersion, status: 'something' });
expect(findLink().attributes('disabled')).toBe('true');
});
});
});

View File

@ -51,6 +51,27 @@ export const packageFiles = () => [
},
];
export const packageVersions = () => [
{
createdAt: '2021-08-10T09:33:54Z',
id: 'gid://gitlab/Packages::Package/243',
name: '@gitlab-org/package-15',
status: 'DEFAULT',
tags: { nodes: packageTags() },
version: '1.0.1',
__typename: 'Package',
},
{
createdAt: '2021-08-10T09:33:54Z',
id: 'gid://gitlab/Packages::Package/244',
name: '@gitlab-org/package-15',
status: 'DEFAULT',
tags: { nodes: packageTags() },
version: '1.0.2',
__typename: 'Package',
},
];
export const packageData = (extend) => ({
id: 'gid://gitlab/Packages::Package/111',
name: '@gitlab-org/package-15',
@ -105,6 +126,9 @@ export const packageDetailsQuery = (extendPackage) => ({
...mavenMetadata(),
...nugetMetadata(),
},
project: {
path: 'projectPath',
},
tags: {
nodes: packageTags(),
__typename: 'PackageTagConnection',
@ -117,6 +141,10 @@ export const packageDetailsQuery = (extendPackage) => ({
nodes: packageFiles(),
__typename: 'PackageFileConnection',
},
versions: {
nodes: packageVersions(),
__typename: 'PackageConnection',
},
__typename: 'PackageDetailsType',
...extendPackage,
},

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['NugetDependencyLinkMetadata'] do
it 'includes nuget dependency link metadatum fields' do
expected_fields = %w[
id target_framework
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDependencyLink'] do
it 'includes package file fields' do
expected_fields = %w[
id dependency_type dependency metadata
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDependencyType'] do
it 'exposes all depeendency type values' do
expect(described_class.values.keys).to contain_exactly(*%w[DEPENDENCIES DEV_DEPENDENCIES BUNDLE_DEPENDENCIES PEER_DEPENDENCIES])
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDependency'] do
it 'includes package file fields' do
expected_fields = %w[
id name version_pattern
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end

View File

@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDetailsType'] do
it 'includes all the package fields' do
expected_fields = %w[
id name version created_at updated_at package_type tags project pipelines versions package_files
id name version created_at updated_at package_type tags project pipelines versions package_files dependency_links
]
expect(described_class).to include_graphql_fields(*expected_fields)

View File

@ -7,6 +7,15 @@ RSpec.describe 'Rails asset host initializer' do
load Rails.root.join('config/initializers/rails_asset_host.rb')
end
around do |example|
old_asset_host = Rails.application.config.action_controller.asset_host
example.run
Rails.application.config.action_controller.asset_host = old_asset_host
ActionController::Base.asset_host = old_asset_host
end
subject { Rails.application.config.action_controller.asset_host }
it 'uses no asset host by default' do

View File

@ -434,20 +434,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
expect(entry.errors).to include 'job dependencies the another-job should be part of needs'
end
end
context 'when stage: is missing' do
let(:config) do
{
script: 'echo',
needs: ['build-job']
}
end
it 'returns error about invalid data' do
expect(entry).not_to be_valid
expect(entry.errors).to include 'job config missing required keys: stage'
end
end
end
context 'when timeout value is not correct' do

View File

@ -6,8 +6,11 @@ RSpec.describe 'nuget package details' do
include_context 'package details setup'
let_it_be(:package) { create(:nuget_package, :with_metadatum, project: project) }
let_it_be(:dependency_link) { create(:packages_dependency_link, :with_nuget_metadatum, package: package) }
let(:metadata) { query_graphql_fragment('NugetMetadata') }
let(:dependency_link_response) { graphql_data_at(:package, :dependency_links, :nodes, 0) }
let(:dependency_response) { graphql_data_at(:package, :dependency_links, :nodes, 0, :dependency) }
subject { post_graphql(query, current_user: user) }
@ -26,4 +29,34 @@ RSpec.describe 'nuget package details' do
'iconUrl' => package.nuget_metadatum.icon_url
)
end
it 'has dependency links' do
expect(dependency_link_response).to include(
'id' => global_id_of(dependency_link),
'dependencyType' => dependency_link.dependency_type.upcase
)
expect(dependency_response).to include(
'id' => global_id_of(dependency_link.dependency),
'name' => dependency_link.dependency.name,
'versionPattern' => dependency_link.dependency.version_pattern
)
end
it 'avoids N+1 queries' do
first_user = create(:user)
second_user = create(:user)
control_count = ActiveRecord::QueryRecorder.new do
post_graphql(query, current_user: first_user)
end
create_list(:packages_dependency_link, 10, :with_nuget_metadatum, package: package)
expect do
post_graphql(query, current_user: second_user)
end.not_to exceed_query_limit(control_count)
expect(response).to have_gitlab_http_status(:ok)
end
end

View File

@ -9,6 +9,7 @@ RSpec.shared_context 'package details setup' do
let(:depth) { 3 }
let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
let(:package_files) { all_graphql_fields_for('PackageFile') }
let(:dependency_links) { all_graphql_fields_for('PackageDependencyLink') }
let(:user) { project.owner }
let(:package_details) { graphql_data_at(:package) }
let(:metadata_response) { graphql_data_at(:package, :metadata) }
@ -28,6 +29,11 @@ RSpec.shared_context 'package details setup' do
#{package_files}
}
}
dependencyLinks {
nodes {
#{dependency_links}
}
}
FIELDS
end
end