diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 8091d4f2e52..75d6d38b91e 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -517,8 +517,6 @@ RSpec/EmptyLineAfterFinalLetItBe: - ee/spec/policies/merge_request_policy_spec.rb - ee/spec/policies/project_policy_spec.rb - ee/spec/policies/vulnerabilities/issue_link_policy_spec.rb - - ee/spec/presenters/approval_rule_presenter_spec.rb - - ee/spec/presenters/ci/pipeline_presenter_spec.rb - ee/spec/requests/api/analytics/code_review_analytics_spec.rb - ee/spec/requests/api/audit_events_spec.rb - ee/spec/requests/api/commits_spec.rb diff --git a/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue b/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue index 21708c6c9db..1060b37067e 100644 --- a/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue +++ b/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue @@ -91,7 +91,7 @@ export default { }, computed: { - availablePanels() { + decoratedPanels() { const PANEL_TITLES = experiment(NEW_REPO_EXPERIMENT, { use: () => ({ blank: s__('ProjectsNew|Create blank project'), @@ -103,20 +103,22 @@ export default { }), }); - const updatedPanels = PANELS.map(({ key, title, ...el }) => ({ + return PANELS.map(({ key, title, ...el }) => ({ ...el, title: PANEL_TITLES[key] !== undefined ? PANEL_TITLES[key] : title, })); + }, + availablePanels() { if (this.isCiCdAvailable) { - return updatedPanels; + return this.decoratedPanels; } - return updatedPanels.filter((p) => p.name !== CI_CD_PANEL); + return this.decoratedPanels.filter((p) => p.name !== CI_CD_PANEL); }, activePanel() { - return PANELS.find((p) => p.name === this.activeTab); + return this.decoratedPanels.find((p) => p.name === this.activeTab); }, breadcrumbs() { diff --git a/app/graphql/resolvers/package_details_resolver.rb b/app/graphql/resolvers/package_details_resolver.rb index e688e34599a..89d79747732 100644 --- a/app/graphql/resolvers/package_details_resolver.rb +++ b/app/graphql/resolvers/package_details_resolver.rb @@ -2,12 +2,20 @@ module Resolvers class PackageDetailsResolver < BaseResolver - type ::Types::Packages::PackageType, null: true + type ::Types::Packages::PackageDetailsType, null: true argument :id, ::Types::GlobalIDType[::Packages::Package], required: true, description: 'The global ID of the package.' + def ready?(**args) + context[self.class] ||= { executions: 0 } + context[self.class][:executions] += 1 + raise GraphQL::ExecutionError, "Package details can be requested only for one package at a time" if context[self.class][:executions] > 1 + + super + end + def resolve(id:) # TODO: remove this line when the compatibility layer is removed # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb index 04a7698e323..56b4f248697 100644 --- a/app/graphql/types/ci/stage_type.rb +++ b/app/graphql/types/ci/stage_type.rb @@ -36,6 +36,34 @@ module Types jobs_for_pipeline(pl, indexed.keys, include_needs).each do |stage_id, statuses| key = indexed[stage_id] groups = ::Ci::Group.fabricate(project, key.stage, statuses) + + if Feature.enabled?(:ci_no_empty_groups, project) + groups.each do |group| + rejected = group.jobs.reject { |job| Ability.allowed?(current_user, :read_commit_status, job) } + group.jobs.select! { |job| Ability.allowed?(current_user, :read_commit_status, job) } + next unless group.jobs.empty? + + exc = StandardError.new('Empty Ci::Group') + traces = rejected.map do |job| + trace = [] + policy = Ability.policy_for(current_user, job) + policy.debug(:read_commit_status, trace) + trace + end + extra = { + current_user_id: current_user&.id, + project_id: project.id, + pipeline_id: pl.id, + stage_id: stage_id, + group_name: group.name, + rejected_job_ids: rejected.map(&:id), + rejected_traces: traces + } + Gitlab::ErrorTracking.track_exception(exc, extra) + end + groups.reject! { |group| group.jobs.empty? } + end + loader.call(key, groups) end end diff --git a/app/graphql/types/packages/conan/file_metadatum_type.rb b/app/graphql/types/packages/conan/file_metadatum_type.rb new file mode 100644 index 00000000000..97d5abe6ba4 --- /dev/null +++ b/app/graphql/types/packages/conan/file_metadatum_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + module Packages + module Conan + class FileMetadatumType < BaseObject + graphql_name 'ConanFileMetadata' + description 'Conan file metadata' + + implements Types::Packages::FileMetadataType + + authorize :read_package + + field :id, ::Types::GlobalIDType[::Packages::Conan::FileMetadatum], null: false, description: 'ID of the metadatum.' + field :recipe_revision, GraphQL::STRING_TYPE, null: false, description: 'Revision of the Conan recipe.' + field :package_revision, GraphQL::STRING_TYPE, null: true, description: 'Revision of the package.' + field :conan_package_reference, GraphQL::STRING_TYPE, null: true, description: 'Reference of the Conan package.' + field :conan_file_type, ::Types::Packages::Conan::MetadatumFileTypeEnum, null: false, description: 'Type of the Conan file.' + end + end + end +end diff --git a/app/graphql/types/packages/conan/metadatum_file_type_enum.rb b/app/graphql/types/packages/conan/metadatum_file_type_enum.rb new file mode 100644 index 00000000000..d8ec3a44d4d --- /dev/null +++ b/app/graphql/types/packages/conan/metadatum_file_type_enum.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + module Packages + module Conan + class MetadatumFileTypeEnum < BaseEnum + graphql_name 'ConanMetadatumFileTypeEnum' + description 'Conan file types' + + ::Packages::Conan::FileMetadatum.conan_file_types.keys.each do |file| + value file.upcase, value: file, description: "A #{file.humanize(capitalize: false)} type." + end + end + end + end +end diff --git a/app/graphql/types/packages/conan/metadatum_type.rb b/app/graphql/types/packages/conan/metadatum_type.rb new file mode 100644 index 00000000000..00b84235d27 --- /dev/null +++ b/app/graphql/types/packages/conan/metadatum_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + module Packages + module Conan + class MetadatumType < BaseObject + graphql_name 'ConanMetadata' + description 'Conan metadata' + + authorize :read_package + + field :id, ::Types::GlobalIDType[::Packages::Conan::Metadatum], null: false, description: 'ID of the metadatum.' + field :created_at, Types::TimeType, null: false, description: 'Date of creation.' + field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' + field :package_username, GraphQL::STRING_TYPE, null: false, description: 'Username of the Conan package.' + field :package_channel, GraphQL::STRING_TYPE, null: false, description: 'Channel of the Conan package.' + field :recipe, GraphQL::STRING_TYPE, null: false, description: 'Recipe of the Conan package.' + field :recipe_path, GraphQL::STRING_TYPE, null: false, description: 'Recipe path of the Conan package.' + end + end + end +end diff --git a/app/graphql/types/packages/file_metadata_type.rb b/app/graphql/types/packages/file_metadata_type.rb new file mode 100644 index 00000000000..46ccb424218 --- /dev/null +++ b/app/graphql/types/packages/file_metadata_type.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Types + module Packages + module FileMetadataType + include ::Types::BaseInterface + graphql_name 'PackageFileMetadata' + description 'Represents metadata associated with a Package file' + + field :created_at, ::Types::TimeType, null: false, description: 'Date of creation.' + field :updated_at, ::Types::TimeType, null: false, description: 'Date of most recent update.' + + def self.resolve_type(object, context) + case object + when ::Packages::Conan::FileMetadatum + ::Types::Packages::Conan::FileMetadatumType + else + # NOTE: This method must be kept in sync with `PackageFileType#file_metadata`, + # which must never produce data that this discriminator cannot handle. + raise 'Unsupported file metadata type' + end + end + + orphan_types Types::Packages::Conan::FileMetadatumType + end + end +end diff --git a/app/graphql/types/packages/metadata_type.rb b/app/graphql/types/packages/metadata_type.rb index 26c43b51a69..4ab6707df88 100644 --- a/app/graphql/types/packages/metadata_type.rb +++ b/app/graphql/types/packages/metadata_type.rb @@ -6,12 +6,14 @@ module Types graphql_name 'PackageMetadata' description 'Represents metadata associated with a Package' - possible_types ::Types::Packages::Composer::MetadatumType + possible_types ::Types::Packages::Composer::MetadatumType, ::Types::Packages::Conan::MetadatumType def self.resolve_type(object, context) case object when ::Packages::Composer::Metadatum ::Types::Packages::Composer::MetadatumType + when ::Packages::Conan::Metadatum + ::Types::Packages::Conan::MetadatumType else # NOTE: This method must be kept in sync with `PackageWithoutVersionsType#metadata`, # which must never produce data that this discriminator cannot handle. diff --git a/app/graphql/types/packages/package_details_type.rb b/app/graphql/types/packages/package_details_type.rb new file mode 100644 index 00000000000..510b7e2ba41 --- /dev/null +++ b/app/graphql/types/packages/package_details_type.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + module Packages + class PackageDetailsType < PackageType + graphql_name 'PackageDetailsType' + description 'Represents a package details in the Package Registry. Note that this type is in beta and susceptible to changes' + authorize :read_package + + field :versions, ::Types::Packages::PackageType.connection_type, null: true, + description: 'The other versions of the package.' + + field :package_files, Types::Packages::PackageFileType.connection_type, null: true, description: 'Package files.' + + def versions + object.versions + end + end + end +end diff --git a/app/graphql/types/packages/package_file_type.rb b/app/graphql/types/packages/package_file_type.rb new file mode 100644 index 00000000000..e9e38559626 --- /dev/null +++ b/app/graphql/types/packages/package_file_type.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Types + module Packages + class PackageFileType < BaseObject + graphql_name 'PackageFile' + description 'Represents a package file' + authorize :read_package + + field :id, ::Types::GlobalIDType[::Packages::PackageFile], null: false, description: 'ID of the file.' + field :created_at, Types::TimeType, null: false, description: 'The created date.' + field :updated_at, Types::TimeType, null: false, description: 'The updated date.' + field :size, GraphQL::STRING_TYPE, null: false, description: 'Size of the package file.' + field :file_name, GraphQL::STRING_TYPE, null: false, description: 'Name of the package file.' + field :download_path, GraphQL::STRING_TYPE, null: false, description: 'Download path of the package file.' + field :file_md5, GraphQL::STRING_TYPE, null: true, description: 'Md5 of the package file.' + field :file_sha1, GraphQL::STRING_TYPE, null: true, description: 'Sha1 of the package file.' + field :file_sha256, GraphQL::STRING_TYPE, null: true, description: 'Sha256 of the package file.' + field :file_metadata, Types::Packages::FileMetadataType, null: true, + description: 'File metadata.' + + # NOTE: This method must be kept in sync with the union + # type: `Types::Packages::FileMetadataType`. + # + # `Types::Packages::FileMetadataType.resolve_type(metadata, ctx)` must never raise. + def file_metadata + case object.package.package_type + when 'conan' + object.conan_file_metadatum + else + nil + end + end + end + end +end diff --git a/app/graphql/types/packages/package_type.rb b/app/graphql/types/packages/package_type.rb index 331898a1e84..a263ca1577a 100644 --- a/app/graphql/types/packages/package_type.rb +++ b/app/graphql/types/packages/package_type.rb @@ -2,13 +2,52 @@ module Types module Packages - class PackageType < PackageWithoutVersionsType + class PackageType < ::Types::BaseObject graphql_name 'Package' - description 'Represents a package in the Package Registry' + description 'Represents a package in the Package Registry. Note that this type is in beta and susceptible to changes' + authorize :read_package - field :versions, ::Types::Packages::PackageWithoutVersionsType.connection_type, null: true, - description: 'The other versions of the package.' + field :id, ::Types::GlobalIDType[::Packages::Package], null: false, + description: 'ID of the package.' + + field :name, GraphQL::STRING_TYPE, null: false, description: 'Name of the package.' + field :created_at, Types::TimeType, null: false, description: 'Date of creation.' + field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' + field :version, GraphQL::STRING_TYPE, null: true, description: 'Version string.' + field :package_type, Types::Packages::PackageTypeEnum, null: false, description: 'Package type.' + 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.' + field :metadata, Types::Packages::MetadataType, null: true, + description: 'Package metadata.' + field :versions, ::Types::Packages::PackageType.connection_type, null: true, + description: 'The other versions of the package.', + deprecated: { reason: 'This field is now only returned in the PackageDetailsType', milestone: '13.11' } + + def project + Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find + end + + def versions + [] + end + + # NOTE: This method must be kept in sync with the union + # type: `Types::Packages::MetadataType`. + # + # `Types::Packages::MetadataType.resolve_type(metadata, ctx)` must never raise. + def metadata + case object.package_type + when 'composer' + object.composer_metadatum + when 'conan' + object.conan_metadatum + else + nil + end + end end end end diff --git a/app/graphql/types/packages/package_without_versions_type.rb b/app/graphql/types/packages/package_without_versions_type.rb deleted file mode 100644 index 9c6bb37e6cc..00000000000 --- a/app/graphql/types/packages/package_without_versions_type.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -module Types - module Packages - class PackageWithoutVersionsType < ::Types::BaseObject - graphql_name 'PackageWithoutVersions' - description 'Represents a version of a package in the Package Registry' - - authorize :read_package - - field :id, ::Types::GlobalIDType[::Packages::Package], null: false, - description: 'ID of the package.' - - field :name, GraphQL::STRING_TYPE, null: false, description: 'Name of the package.' - field :created_at, Types::TimeType, null: false, description: 'Date of creation.' - field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' - field :version, GraphQL::STRING_TYPE, null: true, description: 'Version string.' - field :package_type, Types::Packages::PackageTypeEnum, null: false, description: 'Package type.' - 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.' - field :metadata, Types::Packages::MetadataType, null: true, - description: 'Package metadata.' - - def project - Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find - end - - # NOTE: This method must be kept in sync with the union - # type: `Types::Packages::MetadataType`. - # - # `Types::Packages::MetadataType.resolve_type(metadata, ctx)` must never raise. - def metadata - case object.package_type - when 'composer' - object.composer_metadatum - else - nil - end - end - end - end -end diff --git a/app/policies/packages/conan/file_metadatum_policy.rb b/app/policies/packages/conan/file_metadatum_policy.rb new file mode 100644 index 00000000000..ac1ffb3ea93 --- /dev/null +++ b/app/policies/packages/conan/file_metadatum_policy.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +module Packages + module Conan + class FileMetadatumPolicy < BasePolicy + delegate { @subject.package_file.package } + end + end +end diff --git a/app/policies/packages/conan/metadatum_policy.rb b/app/policies/packages/conan/metadatum_policy.rb new file mode 100644 index 00000000000..8622da015c6 --- /dev/null +++ b/app/policies/packages/conan/metadatum_policy.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +module Packages + module Conan + class MetadatumPolicy < BasePolicy + delegate { @subject.package } + end + end +end diff --git a/app/policies/packages/package_file_policy.rb b/app/policies/packages/package_file_policy.rb new file mode 100644 index 00000000000..e98f74204e8 --- /dev/null +++ b/app/policies/packages/package_file_policy.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +module Packages + class PackageFilePolicy < BasePolicy + delegate { @subject.package } + end +end diff --git a/changelogs/unreleased/26327-include-whether-an-email-is-verified-in-the-user-emails-api-endpoin.yml b/changelogs/unreleased/26327-include-whether-an-email-is-verified-in-the-user-emails-api-endpoin.yml new file mode 100644 index 00000000000..7ead03d8c40 --- /dev/null +++ b/changelogs/unreleased/26327-include-whether-an-email-is-verified-in-the-user-emails-api-endpoin.yml @@ -0,0 +1,5 @@ +--- +title: Return email confirmation time from email entity +merge_request: 58957 +author: +type: changed diff --git a/changelogs/unreleased/285467-package-registry-graphql-api.yml b/changelogs/unreleased/285467-package-registry-graphql-api.yml new file mode 100644 index 00000000000..17c4c2b3344 --- /dev/null +++ b/changelogs/unreleased/285467-package-registry-graphql-api.yml @@ -0,0 +1,5 @@ +--- +title: Add Conan GraphQL type to package +merge_request: 57719 +author: +type: added diff --git a/config/feature_flags/development/ci_no_empty_groups.yml b/config/feature_flags/development/ci_no_empty_groups.yml new file mode 100644 index 00000000000..ef7d6459a5a --- /dev/null +++ b/config/feature_flags/development/ci_no_empty_groups.yml @@ -0,0 +1,8 @@ +--- +name: ci_no_empty_groups +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58789 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327139 +milestone: '13.11' +type: development +group: group::verify +default_enabled: false diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 67267223fd9..36774757ed3 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -215,7 +215,7 @@ Returns [`Namespace`](#namespace). Find a package. -Returns [`Package`](#package). +Returns [`PackageDetailsType`](#packagedetailstype). #### Arguments @@ -1548,6 +1548,34 @@ Composer metadata. | `composerJson` | [`PackageComposerJsonType!`](#packagecomposerjsontype) | Data of the Composer JSON file. | | `targetSha` | [`String!`](#string) | Target SHA of the package. | +### `ConanFileMetadata` + +Conan file metadata. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `conanFileType` | [`ConanMetadatumFileTypeEnum!`](#conanmetadatumfiletypeenum) | Type of the Conan file. | +| `conanPackageReference` | [`String`](#string) | Reference of the Conan package. | +| `createdAt` | [`Time!`](#time) | Date of creation. | +| `id` | [`PackagesConanFileMetadatumID!`](#packagesconanfilemetadatumid) | ID of the metadatum. | +| `packageRevision` | [`String`](#string) | Revision of the package. | +| `recipeRevision` | [`String!`](#string) | Revision of the Conan recipe. | +| `updatedAt` | [`Time!`](#time) | Date of most recent update. | + +### `ConanMetadata` + +Conan metadata. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `createdAt` | [`Time!`](#time) | Date of creation. | +| `id` | [`PackagesConanMetadatumID!`](#packagesconanmetadatumid) | ID of the metadatum. | +| `packageChannel` | [`String!`](#string) | Channel of the Conan package. | +| `packageUsername` | [`String!`](#string) | Username of the Conan package. | +| `recipe` | [`String!`](#string) | Recipe of the Conan package. | +| `recipePath` | [`String!`](#string) | Recipe path of the Conan package. | +| `updatedAt` | [`Time!`](#time) | Date of most recent update. | + ### `ConfigureSastPayload` Autogenerated return type of ConfigureSast. @@ -4501,7 +4529,7 @@ Autogenerated return type of OncallScheduleUpdate. ### `Package` -Represents a package in the Package Registry. +Represents a package in the Package Registry. Note that this type is in beta and susceptible to changes. | Field | Type | Description | | ----- | ---- | ----------- | @@ -4515,7 +4543,7 @@ Represents a package in the Package Registry. | `tags` | [`PackageTagConnection`](#packagetagconnection) | Package tags. | | `updatedAt` | [`Time!`](#time) | Date of most recent update. | | `version` | [`String`](#string) | Version string. | -| `versions` | [`PackageWithoutVersionsConnection`](#packagewithoutversionsconnection) | The other versions of the package. | +| `versions` **{warning-solid}** | [`PackageConnection`](#packageconnection) | **Deprecated** in 13.11. This field is now only returned in the PackageDetailsType. | ### `PackageComposerJsonType` @@ -4538,6 +4566,25 @@ The connection type for Package. | `nodes` | [`[Package]`](#package) | A list of nodes. | | `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | +### `PackageDetailsType` + +Represents a package details in the Package Registry. Note that this type is in beta and susceptible to changes. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `createdAt` | [`Time!`](#time) | Date of creation. | +| `id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. | +| `metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. | +| `name` | [`String!`](#string) | Name of the package. | +| `packageFiles` | [`PackageFileConnection`](#packagefileconnection) | Package files. | +| `packageType` | [`PackageTypeEnum!`](#packagetypeenum) | Package type. | +| `pipelines` | [`PipelineConnection`](#pipelineconnection) | Pipelines that built the package. | +| `project` | [`Project!`](#project) | Project where the package is stored. | +| `tags` | [`PackageTagConnection`](#packagetagconnection) | Package tags. | +| `updatedAt` | [`Time!`](#time) | Date of most recent update. | +| `version` | [`String`](#string) | Version string. | +| `versions` | [`PackageConnection`](#packageconnection) | The other versions of the package. | + ### `PackageEdge` An edge in a connection. @@ -4547,6 +4594,42 @@ An edge in a connection. | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`Package`](#package) | The item at the end of the edge. | +### `PackageFile` + +Represents a package file. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `createdAt` | [`Time!`](#time) | The created date. | +| `downloadPath` | [`String!`](#string) | Download path of the package file. | +| `fileMd5` | [`String`](#string) | Md5 of the package file. | +| `fileMetadata` | [`PackageFileMetadata`](#packagefilemetadata) | File metadata. | +| `fileName` | [`String!`](#string) | Name of the package file. | +| `fileSha1` | [`String`](#string) | Sha1 of the package file. | +| `fileSha256` | [`String`](#string) | Sha256 of the package file. | +| `id` | [`PackagesPackageFileID!`](#packagespackagefileid) | ID of the file. | +| `size` | [`String!`](#string) | Size of the package file. | +| `updatedAt` | [`Time!`](#time) | The updated date. | + +### `PackageFileConnection` + +The connection type for PackageFile. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `edges` | [`[PackageFileEdge]`](#packagefileedge) | A list of edges. | +| `nodes` | [`[PackageFile]`](#packagefile) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +### `PackageFileEdge` + +An edge in a connection. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`PackageFile`](#packagefile) | The item at the end of the edge. | + ### `PackageFileRegistry` Represents the Geo sync and verification state of a package file. @@ -4620,42 +4703,6 @@ An edge in a connection. | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`PackageTag`](#packagetag) | The item at the end of the edge. | -### `PackageWithoutVersions` - -Represents a version of a package in the Package Registry. - -| Field | Type | Description | -| ----- | ---- | ----------- | -| `createdAt` | [`Time!`](#time) | Date of creation. | -| `id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. | -| `metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. | -| `name` | [`String!`](#string) | Name of the package. | -| `packageType` | [`PackageTypeEnum!`](#packagetypeenum) | Package type. | -| `pipelines` | [`PipelineConnection`](#pipelineconnection) | Pipelines that built the package. | -| `project` | [`Project!`](#project) | Project where the package is stored. | -| `tags` | [`PackageTagConnection`](#packagetagconnection) | Package tags. | -| `updatedAt` | [`Time!`](#time) | Date of most recent update. | -| `version` | [`String`](#string) | Version string. | - -### `PackageWithoutVersionsConnection` - -The connection type for PackageWithoutVersions. - -| Field | Type | Description | -| ----- | ---- | ----------- | -| `edges` | [`[PackageWithoutVersionsEdge]`](#packagewithoutversionsedge) | A list of edges. | -| `nodes` | [`[PackageWithoutVersions]`](#packagewithoutversions) | A list of nodes. | -| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | - -### `PackageWithoutVersionsEdge` - -An edge in a connection. - -| Field | Type | Description | -| ----- | ---- | ----------- | -| `cursor` | [`String!`](#string) | A cursor for use in pagination. | -| `node` | [`PackageWithoutVersions`](#packagewithoutversions) | The item at the end of the edge. | - ### `PageInfo` Information about pagination in a connection. @@ -7536,6 +7583,15 @@ Mode of a commit action. | `BASE64` | Base64 encoding. | | `TEXT` | Text encoding. | +### `ConanMetadatumFileTypeEnum` + +Conan file types. + +| Value | Description | +| ----- | ----------- | +| `PACKAGE_FILE` | A package file type. | +| `RECIPE_FILE` | A recipe file type. | + ### `ContainerExpirationPolicyCadenceEnum` | Value | Description | @@ -8794,6 +8850,24 @@ A `NoteableID` is a global ID. It is encoded as a string. An example `NoteableID` is: `"gid://gitlab/Noteable/1"`. +### `PackagesConanFileMetadatumID` + +A `PackagesConanFileMetadatumID` is a global ID. It is encoded as a string. + +An example `PackagesConanFileMetadatumID` is: `"gid://gitlab/Packages::Conan::FileMetadatum/1"`. + +### `PackagesConanMetadatumID` + +A `PackagesConanMetadatumID` is a global ID. It is encoded as a string. + +An example `PackagesConanMetadatumID` is: `"gid://gitlab/Packages::Conan::Metadatum/1"`. + +### `PackagesPackageFileID` + +A `PackagesPackageFileID` is a global ID. It is encoded as a string. + +An example `PackagesPackageFileID` is: `"gid://gitlab/Packages::PackageFile/1"`. + ### `PackagesPackageID` A `PackagesPackageID` is a global ID. It is encoded as a string. @@ -8902,6 +8976,7 @@ Represents metadata associated with a Package. One of: - [`ComposerMetadata`](#composermetadata) +- [`ConanMetadata`](#conanmetadata) #### `VulnerabilityDetail` @@ -9054,6 +9129,19 @@ Implementations: | `discussions` | [`DiscussionConnection!`](#discussionconnection) | All discussions on this noteable. | | `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. | +#### `PackageFileMetadata` + +Represents metadata associated with a Package file. + +Implementations: + +- [`ConanFileMetadata`](#conanfilemetadata) + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `createdAt` | [`Time!`](#time) | Date of creation. | +| `updatedAt` | [`Time!`](#time) | Date of most recent update. | + #### `ResolvableInterface` Implementations: diff --git a/doc/api/packages/rubygems.md b/doc/api/packages/rubygems.md new file mode 100644 index 00000000000..426548d5ed2 --- /dev/null +++ b/doc/api/packages/rubygems.md @@ -0,0 +1,149 @@ +--- +stage: Package +group: Package +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Ruby gems API + +This is the API documentation for [Ruby gems](../../user/packages/rubygems_registry/index.md). + +WARNING: +This API is used by the [Ruby gems and Bundler package manager clients](https://maven.apache.org/) +and is generally not meant for manual consumption. This API is under development and is not ready +for production use due to limited functionality. + +For instructions on how to upload and install gems from the GitLab +package registry, see the [Ruby gems registry documentation](../../user/packages/rubygems_registry/index.md). + +NOTE: +These endpoints do not adhere to the standard API authentication methods. +See the [Ruby gems registry documentation](../../user/packages/rubygems_registry/index.md) +for details on which headers and token types are supported. + +## Enable the Ruby gems API + +The Ruby gems API for GitLab is behind a feature flag that is disabled by default. GitLab +administrators with access to the GitLab Rails console can enable this API for your instance. + +To enable it: + +```ruby +Feature.enable(:rubygem_packages) +``` + +To disable it: + +```ruby +Feature.disable(:rubygem_packages) +``` + +To enable or disable it for specific projects: + +```ruby +Feature.enable(:rubygem_packages, Project.find(1)) +Feature.disable(:rubygem_packages, Project.find(2)) +``` + +## Download a gem file + +> Introduced in GitLab 13.10. + +Download a gem: + +```plaintext +GET projects/:id/packages/rubygems/gems/:file_name +``` + +| Attribute | Type | Required | Description | +| ------------ | ------ | -------- | ----------- | +| `id` | string | yes | The ID or full path of the project. | +| `file_name` | string | yes | The name of the `.gem` file. | + +```shell +curl --header "Authorization:" "https://gitlab.example.com/api/v4/projects/1/packages/rubygems/gems/my_gem-1.0.0.gem" +``` + +Write the output to file: + +```shell +curl --header "Authorization:" "https://gitlab.example.com/api/v4/projects/1/packages/rubygems/gems/my_gem-1.0.0.gem" >> my_gem-1.0.0.gem +``` + +This writes the downloaded file to `my_gem-1.0.0.gem` in the current directory. + +## Fetch a list of dependencies + +> Introduced in GitLab 13.10. + +Fetch a list of dependencies for a list of gems: + +```plaintext +GET projects/:id/packages/rubygems/api/v1/dependencies +``` + +| Attribute | Type | Required | Description | +| --------- | ------ | -------- | ----------- | +| `id` | string | yes | The ID or full path of the project. | +| `gems` | string | no | Comma-separated list of gems to fetch dependencies for. | + +```shell +curl --header "Authorization:" "https://gitlab.example.com/api/v4/projects/1/packages/rubygems/api/v1/dependencies?gems=my_gem,foo" +``` + +This endpoint returns a marshalled array of hashes for all versions of the requested gems. Since the +response is marshalled, you can store it in a file. If Ruby is installed, you can use the following +Ruby command to read the response. For this to work, you must +[set your credentials in `~/.gem/credentials`](../../user/packages/rubygems_registry/index.md#authenticate-with-a-personal-access-token-or-deploy-token): + +```shell +$ ruby -ropen-uri -rpp -e \ + 'pp Marshal.load(open("https://gitlab.example.com/api/v4/projects/1/packages/rubygems/api/v1/dependencies?gems=my_gem,rails,foo"))' + +[{:name=>"my_gem", :number=>"0.0.1", :platform=>"ruby", :dependencies=>[]}, + {:name=>"my_gem", + :number=>"0.0.3", + :platform=>"ruby", + :dependencies=> + [["dependency_1", "~> 1.2.3"], + ["dependency_2", "= 3.0.0"], + ["dependency_3", ">= 1.0.0"], + ["dependency_4", ">= 0"]]}, + {:name=>"my_gem", + :number=>"0.0.2", + :platform=>"ruby", + :dependencies=> + [["dependency_1", "~> 1.2.3"], + ["dependency_2", "= 3.0.0"], + ["dependency_3", ">= 1.0.0"], + ["dependency_4", ">= 0"]]}, + {:name=>"foo", + :number=>"0.0.2", + :platform=>"ruby", + :dependencies=> + ["dependency_2", "= 3.0.0"], + ["dependency_4", ">= 0"]]}] +``` + +This writes the downloaded file to `mypkg-1.0-SNAPSHOT.jar` in the current directory. + +## Upload a gem + +> Introduced in GitLab 13.11. + +Upload a gem: + +```plaintext +POST projects/:id/packages/rubygems/api/v1/gems +``` + +| Attribute | Type | Required | Description | +| --------- | ------ | -------- | ----------- | +| `id` | string | yes | The ID or full path of the project. | + +```shell +curl --request POST \ + --upload-file path/to/my_gem_file.gem \ + --header "Authorization:" \ + "https://gitlab.example.com/api/v4/projects/1/packages/rubygems/api/v1/gems" +``` diff --git a/doc/api/repositories.md b/doc/api/repositories.md index ba0a200731b..857cd3883c8 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -397,6 +397,18 @@ these as the changelog entries. You can enrich entries with additional data, such as a link to the merge request or details about the commit author. You can [customize the format of a changelog](#customize-the-changelog-output) section with a template. +Trailers can be manually added while editing a commit message. To include a commit +using the default trailer of `Changelog` and categorize it as a feature, the +trailer could be added to a commit message like so: + +```plaintext + + + + +Changelog: feature +``` + ### Reverted commits > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55537) in GitLab 13.10. diff --git a/doc/api/users.md b/doc/api/users.md index 4c35ee0e531..86f1548dac4 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -1213,11 +1213,13 @@ GET /user/emails [ { "id": 1, - "email": "email@example.com" + "email": "email@example.com", + "confirmed_at" : "2021-03-26T19:07:56.248Z" }, { "id": 3, - "email": "email2@example.com" + "email": "email2@example.com", + "confirmed_at" : null } ] ``` @@ -1257,7 +1259,8 @@ Parameters: ```json { "id": 1, - "email": "email@example.com" + "email": "email@example.com", + "confirmed_at" : "2021-03-26T19:07:56.248Z" } ``` @@ -1276,7 +1279,8 @@ Parameters: ```json { "id": 4, - "email": "email@example.com" + "email": "email@example.com", + "confirmed_at" : "2021-03-26T19:07:56.248Z" } ``` diff --git a/doc/development/experiment_guide/gitlab_experiment.md b/doc/development/experiment_guide/gitlab_experiment.md index a01ad54b697..51328a13f27 100644 --- a/doc/development/experiment_guide/gitlab_experiment.md +++ b/doc/development/experiment_guide/gitlab_experiment.md @@ -49,7 +49,7 @@ graph TD Running? -->|No| Excluded[Control / No Tracking] Cached? -->|No| Excluded? Cached? -->|Yes| Cached[Cached Value] - Excluded? -->|Yes / Cached| Excluded + Excluded? -->|Yes| Excluded Excluded? -->|No| Segmented? Segmented? -->|Yes / Cached| VariantA Segmented? -->|No| Included?[Experiment Group?] @@ -92,7 +92,7 @@ end ``` When this code executes, the experiment is run, a variant is assigned, and (if within a -controller or view) a `window.gon.experiment.pillColor` object will be available in the +controller or view) a `window.gon.experiment.pill_color` object will be available in the client layer, with details like: - The assigned variant. diff --git a/doc/operations/incident_management/index.md b/doc/operations/incident_management/index.md index eb931b3eec5..ff5f41e59e9 100644 --- a/doc/operations/incident_management/index.md +++ b/doc/operations/incident_management/index.md @@ -13,6 +13,7 @@ generated by their application. By surfacing alerts and incidents where the code being developed, efficiency and awareness can be increased. Check out the following sections for more information: - [Integrate your monitoring tools](integrations.md). -- Receive [notifications](paging.md) for triggered alerts. +- Manage [on-call schedules](oncall_schedules.md) and receive [notifications](paging.md) for + triggered alerts. - Triage [Alerts](alerts.md) and [Incidents](incidents.md). - Inform stakeholders with [Status Page](status_page.md). diff --git a/doc/operations/incident_management/oncall_schedules.md b/doc/operations/incident_management/oncall_schedules.md index 5a5564e3ce9..87745639c69 100644 --- a/doc/operations/incident_management/oncall_schedules.md +++ b/doc/operations/incident_management/oncall_schedules.md @@ -37,7 +37,7 @@ create [rotations](#rotations) for your schedule. ![Schedule Empty Grid](img/oncall_schedule_empty_grid_v13_10.png) -### Update a schedule +### Edit a schedule Follow these steps to update a schedule: @@ -46,6 +46,9 @@ Follow these steps to update a schedule: 1. In the **Edit schedule** form, edit the information you wish to update. 1. Click the **Edit schedule** button to save your changes. +If you change the schedule's time zone, GitLab automatically updates the rotation's restricted time +interval (if one is set) to the corresponding times in the new time zone. + ### Delete a schedule Follow these steps to delete a schedule: @@ -70,8 +73,8 @@ Follow these steps to create a rotation: - **Starts on:** The date and time the rotation begins. - **Enable end date:** With the toggle set to on, you can select the date and time your rotation ends. - - **Restrict to time intervals:** With the toggle set to on, you can restrict your rotation to - the time period you select. + - **Restrict to time intervals:** With the toggle set to on, you can restrict your rotation to the + time period you select. ### Edit a rotation diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md index 6a976fca382..bc1e59e4ac2 100644 --- a/doc/user/packages/composer_repository/index.md +++ b/doc/user/packages/composer_repository/index.md @@ -295,3 +295,10 @@ Never commit the `auth.json` file to your repository. To install packages from a consider using the [`composer config`](https://getcomposer.org/doc/articles/handling-private-packages.md#satis) tool with your personal access token stored in a [GitLab CI/CD variable](../../../ci/variables/README.md) or in [HashiCorp Vault](../../../ci/secrets/index.md). + +## Supported CLI commands + +The GitLab Composer repository supports the following Composer CLI commands: + +- `composer install`: Install Composer dependencies. +- `composer update`: Install the latest version of Composer dependencies. diff --git a/doc/user/packages/index.md b/doc/user/packages/index.md index b35015d0b67..591bdca9353 100644 --- a/doc/user/packages/index.md +++ b/doc/user/packages/index.md @@ -24,6 +24,7 @@ The Package Registry supports the following formats: NuGet12.8+ PyPI12.10+ Generic packages13.5+ +RubyGems13.10+ @@ -49,7 +50,6 @@ guides you through the process. | P2 | [#36895](https://gitlab.com/gitlab-org/gitlab/-/issues/36895) | | Puppet | [#36897](https://gitlab.com/gitlab-org/gitlab/-/issues/36897) | | RPM | [#5932](https://gitlab.com/gitlab-org/gitlab/-/issues/5932) | -| RubyGems | [#803](https://gitlab.com/gitlab-org/gitlab/-/issues/803) | | SBT | [#36898](https://gitlab.com/gitlab-org/gitlab/-/issues/36898) | | Terraform | [Draft: Merge Request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18834) | | Vagrant | [#36899](https://gitlab.com/gitlab-org/gitlab/-/issues/36899) | diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md index 7104f4a02ed..d4dc9f0ae78 100644 --- a/doc/user/packages/maven_repository/index.md +++ b/doc/user/packages/maven_repository/index.md @@ -871,3 +871,11 @@ package: - 'mvn help:system' - 'mvn package' ``` + +## Supported CLI commands + +The GitLab Maven repository supports the following Maven CLI commands: + +- `mvn deploy`: Publish your package to the Package Registry. +- `mvn install`: Install packages specified in your Maven project. +- `mvn dependency:get`: Install a specific package. diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md index a997f2fbb08..b6312002184 100644 --- a/doc/user/packages/npm_registry/index.md +++ b/doc/user/packages/npm_registry/index.md @@ -515,4 +515,19 @@ This is usually a permissions issue with either: - The remote bucket if [object storage](../../../administration/packages/#using-object-storage) is used. -In the latter case, ensure the bucket exists and the GitLab has write access to it. +In the latter case, ensure the bucket exists and GitLab has write access to it. + +## Supported CLI commands + +The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI +(`yarn`): + +- `npm install`: Install npm packages. +- `npm publish`: Publish an npm package to the registry. +- `npm dist-tag add`: Add a dist-tag to an npm package. +- `npm dist-tag ls`: List dist-tags for a package. +- `npm dist-tag rm`: Delete a dist-tag. +- `npm ci`: Install npm packages directly from your `package-lock.json` file. +- `npm view`: Show package metadata. +- `yarn add`: Install an npm package. +- `yarn update`: Update your dependencies. diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md index 128b1daf9cb..7e59b19076a 100644 --- a/doc/user/packages/nuget_repository/index.md +++ b/doc/user/packages/nuget_repository/index.md @@ -393,3 +393,13 @@ dotnet add package \ - `` is the package ID. - `` is the package version. Optional. + +## Supported CLI commands + +The GitLab NuGet repository supports the following commands for the NuGet CLI (`nuget`) and the .NET +CLI (`dotnet`): + +- `nuget push`: Upload a package to the registry. +- `dotnet nuget push`: Upload a package to the registry. +- `nuget install`: Install a package from the registry. +- `dotnet add`: Install a package from the registry. diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md index a08b7b65145..17b51e313fa 100644 --- a/doc/user/packages/pypi_repository/index.md +++ b/doc/user/packages/pypi_repository/index.md @@ -359,3 +359,10 @@ characters are removed. A `pip install` request for `my.package` looks for packages that match any of the three characters, such as `my-package`, `my_package`, and `my....package`. + +## Supported CLI commands + +The GitLab PyPI repository supports the following CLI commands: + +- `twine upload`: Upload a package to the registry. +- `pip install`: Install a PyPI package from the registry. diff --git a/doc/user/packages/rubygems_registry/index.md b/doc/user/packages/rubygems_registry/index.md new file mode 100644 index 00000000000..2a94d2a3ccf --- /dev/null +++ b/doc/user/packages/rubygems_registry/index.md @@ -0,0 +1,137 @@ +--- +stage: Package +group: Package +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Ruby gems in the Package Registry + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/803) in [GitLab Free](https://about.gitlab.com/pricing/) 13.10. + +WARNING: +The Ruby gems registry for GitLab is under development and isn't ready for production use due to +limited functionality. + +You can publish Ruby gems in your project's Package Registry, then install the packages when you +need to use them as a dependency. Although you can push gems to the registry, you cannot install +them from the registry. However, you can download `gem` files directly from the Package Registry's +UI, or by using the [API](../../../api/packages/rubygems.md#download-a-gem-file). + +For documentation of the specific API endpoints that the Ruby gems and Bundler package manager +clients use, see the [Ruby gems API documentation](../../../api/packages/rubygems.md). + +## Enable the Ruby gems registry + +The Ruby gems registry for GitLab is behind a feature flag that is disabled by default. GitLab +administrators with access to the GitLab Rails console can enable this registry for your instance. + +To enable it: + +```ruby +Feature.enable(:rubygem_packages) +``` + +To disable it: + +```ruby +Feature.disable(:rubygem_packages) +``` + +To enable or disable it for specific projects: + +```ruby +Feature.enable(:rubygem_packages, Project.find(1)) +Feature.disable(:rubygem_packages, Project.find(2)) +``` + +## Create a Ruby Gem + +If you need help creating a Ruby gem, see the [RubyGems documentation](https://guides.rubygems.org/make-your-own-gem/). + +## Authenticate to the Package Registry + +Before you can push to the Package Registry, you must authenticate. + +To do this, you can use: + +- A [personal access token](../../../user/profile/personal_access_tokens.md) + with the scope set to `api`. +- A [deploy token](../../project/deploy_tokens/index.md) with the scope set to + `read_package_registry`, `write_package_registry`, or both. +- A [CI job token](#authenticate-with-a-ci-job-token). + +### Authenticate with a personal access token or deploy token + +To authenticate with a personal access token, create or edit the `~/.gem/credentials` file and add: + +```ini +--- +https://gitlab.example.com/api/v4/projects//packages/rubygems: '' +``` + +- `` must be the token value of either your personal access token or deploy token. +- Your project ID is on your project's home page. + +### Authenticate with a CI job token + +To work with RubyGems commands within [GitLab CI/CD](../../../ci/README.md), +you can use `CI_JOB_TOKEN` instead of a personal access token or deploy token. + +For example: + +```yaml +image: ruby:latest + +run: + script: +``` + +You can also use `CI_JOB_TOKEN` in a `~/.gem/credentials` file that you check in to +GitLab: + +```ini +--- +https://gitlab.example.com/api/v4/projects/${env.CI_PROJECT_ID}/packages/rubygems: '${env.CI_JOB_TOKEN}' +``` + +## Push a Ruby gem + +Prerequisites: + +- You must [authenticate to the Package Registry](#authenticate-to-the-package-registry). +- The maximum allowed gem size is 3 GB. + +To push your gem, run a command like this one: + +```shell +gem push my_gem-0.0.1.gem --host +``` + +Note that `` is the URL you used when setting up authentication. For example: + +```shell +gem push my_gem-0.0.1.gem --host https://gitlab.example.com/api/v4/projects/1/packages/rubygems +``` + +This message indicates that the gem uploaded successfully: + +```plaintext +Pushing gem to https://gitlab.example.com/api/v4/projects/1/packages/rubygems... +{"message":"201 Created"} +``` + +To view the published gem, go to your project's **Packages & Registries** page. Gems pushed to +GitLab aren't displayed in your project's Packages UI immediately. It can take up to 10 minutes to +process a gem. + +### Pushing gems with the same name or version + +You can push a gem if a package of the same name and version already exists. +Both are visible and accessible in the UI. However, only the most recently +pushed gem is used for installs. + +## Install a Ruby gem + +The Ruby gems registry for GitLab is under development, and isn't ready for production use. You +cannot install Gems from the registry. However, you can download `.gem` files directly from the UI +or by using the [API](../../../api/packages/rubygems.md#download-a-gem-file). diff --git a/lib/api/entities/email.rb b/lib/api/entities/email.rb index 5ba425def3d..46ebc458bcd 100644 --- a/lib/api/entities/email.rb +++ b/lib/api/entities/email.rb @@ -3,7 +3,7 @@ module API module Entities class Email < Grape::Entity - expose :id, :email + expose :id, :email, :confirmed_at end end end diff --git a/package.json b/package.json index 794e8cb6baa..868ac33d8e3 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@gitlab/favicon-overlay": "2.0.0", "@gitlab/svgs": "1.188.0", "@gitlab/tributejs": "1.0.0", - "@gitlab/ui": "29.2.1", + "@gitlab/ui": "29.3.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-4", "@rails/ujs": "^6.0.3-4", diff --git a/spec/fixtures/api/schemas/graphql/packages/package_conan_metadata.json b/spec/fixtures/api/schemas/graphql/packages/package_conan_metadata.json new file mode 100644 index 00000000000..31bb861ced5 --- /dev/null +++ b/spec/fixtures/api/schemas/graphql/packages/package_conan_metadata.json @@ -0,0 +1,37 @@ +{ + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "createdAt", + "updatedAt", + "packageUsername", + "packageChannel", + "recipe", + "recipePath", + "packageName" + ], + "properties": { + "id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "package_username": { + "type": "string" + }, + "package_channel": { + "type": "string" + }, + "recipe": { + "type": "string" + }, + "recipe_path": { + "type": "string" + } + } +} diff --git a/spec/fixtures/api/schemas/graphql/packages/package_details.json b/spec/fixtures/api/schemas/graphql/packages/package_details.json index cf624f9c644..87b173eefc7 100644 --- a/spec/fixtures/api/schemas/graphql/packages/package_details.json +++ b/spec/fixtures/api/schemas/graphql/packages/package_details.json @@ -2,8 +2,17 @@ "type": "object", "additionalProperties": false, "required": [ - "id", "name", "createdAt", "updatedAt", "version", "packageType", - "project", "tags", "pipelines", "versions", "metadata" + "id", + "name", + "createdAt", + "updatedAt", + "version", + "packageType", + "project", + "tags", + "pipelines", + "versions", + "metadata" ], "properties": { "id": { @@ -23,7 +32,18 @@ }, "packageType": { "type": ["string"], - "enum": ["MAVEN", "NPM", "CONAN", "NUGET", "PYPI", "COMPOSER", "GENERIC", "GOLANG", "RUBYGEMS", "DEBIAN"] + "enum": [ + "MAVEN", + "NPM", + "CONAN", + "NUGET", + "PYPI", + "COMPOSER", + "GENERIC", + "GOLANG", + "RUBYGEMS", + "DEBIAN" + ] }, "tags": { "type": "object", @@ -59,8 +79,18 @@ "metadata": { "anyOf": [ { "$ref": "./package_composer_metadata.json" }, + { "$ref": "./package_conan_metadata.json" }, { "type": "null" } ] + }, + "packageFiles": { + "type": "object", + "additionalProperties": false, + "properties": { + "pageInfo": { "type": "object" }, + "edges": { "type": "array" }, + "nodes": { "type": "array" } + } } } } diff --git a/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js b/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js index df1e38ec528..204e7a7c394 100644 --- a/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js +++ b/spec/frontend/projects/experiment_new_project_creation/components/app_spec.js @@ -23,6 +23,7 @@ describe('Experimental new project creation app', () => { findWelcomePage() .props() .panels.find((p) => p.name === panelName); + const findPanelHeader = () => wrapper.find('h4'); describe('new_repo experiment', () => { describe('when in the candidate variant', () => { @@ -33,6 +34,17 @@ describe('Experimental new project creation app', () => { expect(findPanel('blank_project').title).toBe('Create blank project/repository'); }); + + describe('when hash is not empty on load', () => { + beforeEach(() => { + window.location.hash = '#blank_project'; + createComponent(); + }); + + it('renders "project/repository"', () => { + expect(findPanelHeader().text()).toBe('Create blank project/repository'); + }); + }); }); describe('when in the control variant', () => { @@ -43,6 +55,17 @@ describe('Experimental new project creation app', () => { expect(findPanel('blank_project').title).toBe('Create blank project'); }); + + describe('when hash is not empty on load', () => { + beforeEach(() => { + window.location.hash = '#blank_project'; + createComponent(); + }); + + it('renders "project"', () => { + expect(findPanelHeader().text()).toBe('Create blank project'); + }); + }); }); }); diff --git a/spec/graphql/types/packages/conan/file_metadatum_type_spec.rb b/spec/graphql/types/packages/conan/file_metadatum_type_spec.rb new file mode 100644 index 00000000000..18b17286654 --- /dev/null +++ b/spec/graphql/types/packages/conan/file_metadatum_type_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ConanFileMetadata'] do + it 'includes conan file metadatum fields' do + expected_fields = %w[ + id created_at updated_at recipe_revision package_revision conan_package_reference conan_file_type + ] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb b/spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb new file mode 100644 index 00000000000..379cb5168a8 --- /dev/null +++ b/spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ConanMetadatumFileTypeEnum'] do + it 'uses all possible options from model' do + expected_keys = ::Packages::Conan::FileMetadatum.conan_file_types + .keys + .map(&:upcase) + + expect(described_class.values.keys).to contain_exactly(*expected_keys) + end +end diff --git a/spec/graphql/types/packages/conan/metadatum_type_spec.rb b/spec/graphql/types/packages/conan/metadatum_type_spec.rb new file mode 100644 index 00000000000..f8f24ffc95a --- /dev/null +++ b/spec/graphql/types/packages/conan/metadatum_type_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ConanMetadata'] do + it 'includes conan metadatum fields' do + expected_fields = %w[ + id created_at updated_at package_username package_channel recipe recipe_path + ] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/packages/package_without_versions_type_spec.rb b/spec/graphql/types/packages/package_details_type_spec.rb similarity index 73% rename from spec/graphql/types/packages/package_without_versions_type_spec.rb rename to spec/graphql/types/packages/package_details_type_spec.rb index faa79e588d5..06093813315 100644 --- a/spec/graphql/types/packages/package_without_versions_type_spec.rb +++ b/spec/graphql/types/packages/package_details_type_spec.rb @@ -2,10 +2,10 @@ require 'spec_helper' -RSpec.describe GitlabSchema.types['PackageWithoutVersions'] do +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 + id name version created_at updated_at package_type tags project pipelines versions package_files ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/packages/package_file_type_spec.rb b/spec/graphql/types/packages/package_file_type_spec.rb new file mode 100644 index 00000000000..8e20aea5220 --- /dev/null +++ b/spec/graphql/types/packages/package_file_type_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['PackageFile'] do + it 'includes package file fields' do + expected_fields = %w[ + id file_name created_at updated_at size file_name download_path file_md5 file_sha1 file_sha256 file_metadata + ] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/packages/package_type_spec.rb b/spec/graphql/types/packages/package_type_spec.rb index 43289a019b3..544d6ddc3af 100644 --- a/spec/graphql/types/packages/package_type_spec.rb +++ b/spec/graphql/types/packages/package_type_spec.rb @@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['Package'] do id name version package_type created_at updated_at project - tags pipelines versions + tags pipelines metadata versions ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index cb8e875dbf4..d3dcdd260b0 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -98,6 +98,6 @@ RSpec.describe GitlabSchema.types['Query'] do describe 'package field' do subject { described_class.fields['package'] } - it { is_expected.to have_graphql_type(Types::Packages::PackageType) } + it { is_expected.to have_graphql_type(Types::Packages::PackageDetailsType) } end end diff --git a/spec/helpers/projects/ci/pipeline_editor_helper_spec.rb b/spec/helpers/projects/ci/pipeline_editor_helper_spec.rb deleted file mode 100644 index 882b6548d64..00000000000 --- a/spec/helpers/projects/ci/pipeline_editor_helper_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe ::Ci::PipelineEditorHelper do - let_it_be(:project) { create(:project, :public, :repository) } - - describe '#js_pipeline_editor_data' do - before do - allow(helper).to receive(:namespace_project_new_merge_request_path).and_return('/mock/project/-/merge_requests/new') - end - - subject { helper.js_pipeline_editor_data(project) } - - it { - is_expected.to match({ - "ci-config-path": project.ci_config_path_or_default, - "commit-sha" => project.commit.sha, - "default-branch" => project.default_branch, - "empty-state-illustration-path" => match_asset_path("/assets/illustrations/empty-state/empty-dag-md.svg"), - "initial-branch-name": nil, - "lint-help-page-path" => help_page_path('ci/lint', anchor: 'validate-basic-logic-and-syntax'), - "new-merge-request-path" => '/mock/project/-/merge_requests/new', - "project-path" => project.path, - "project-full-path" => project.full_path, - "project-namespace" => project.namespace.full_path, - "yml-help-page-path" => help_page_path('ci/yaml/README') - }) - } - end -end diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb index 654215041cb..a0131c7733e 100644 --- a/spec/requests/api/graphql/packages/package_spec.rb +++ b/spec/requests/api/graphql/packages/package_spec.rb @@ -2,33 +2,47 @@ require 'spec_helper' RSpec.describe 'package details' do - using RSpec::Parameterized::TableSyntax include GraphqlHelpers let_it_be(:project) { create(:project) } - let_it_be(:package) { create(:composer_package, project: project) } + let_it_be(:composer_package) { create(:composer_package, project: project) } let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } } let_it_be(:composer_metadatum) do # we are forced to manually create the metadatum, without using the factory to force the sha to be a string # and avoid an error where gitaly can't find the repository - create(:composer_metadatum, package: package, target_sha: 'foo_sha', composer_json: composer_json) + create(:composer_metadatum, package: composer_package, target_sha: 'foo_sha', composer_json: composer_json) end let(:depth) { 3 } - let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline] } + let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } + let(:metadata) { query_graphql_fragment('ComposerMetadata') } + let(:package_files) {all_graphql_fields_for('PackageFile')} + let(:package_files_metadata) {query_graphql_fragment('ConanFileMetadata')} let(:query) do graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) - #{all_graphql_fields_for('Package', max_depth: depth, excluded: excluded)} + #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)} metadata { - #{query_graphql_fragment('ComposerMetadata')} + #{metadata} + } + packageFiles { + nodes { + #{package_files} + fileMetadata { + #{package_files_metadata} + } + } } FIELDS end let(:user) { project.owner } - let(:package_global_id) { global_id_of(package) } + let(:package_global_id) { global_id_of(composer_package) } let(:package_details) { graphql_data_at(:package) } + let(:metadata_response) { graphql_data_at(:package, :metadata) } + let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) } + let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)} + let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata)} subject { post_graphql(query, current_user: user) } @@ -40,15 +54,68 @@ RSpec.describe 'package details' do it 'matches the JSON schema' do expect(package_details).to match_schema('graphql/packages/package_details') end + end - it 'includes the fields of the correct package' do - expect(package_details).to include( - 'id' => package_global_id, - 'metadata' => { + describe 'Packages Metadata' do + before do + subject + end + + describe 'Composer' do + it 'has the correct metadata' do + expect(metadata_response).to include( 'targetSha' => 'foo_sha', 'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s) - } - ) + ) + end + + it 'does not have files' do + expect(package_files_response).to be_empty + end + end + + describe 'Conan' do + let_it_be(:conan_package) { create(:conan_package, project: project) } + + let(:package_global_id) { global_id_of(conan_package) } + let(:metadata) { query_graphql_fragment('ConanMetadata') } + let(:first_file) { conan_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } } + + it 'has the correct metadata' do + expect(metadata_response).to include( + 'id' => global_id_of(conan_package.conan_metadatum), + 'recipe' => conan_package.conan_metadatum.recipe, + 'packageChannel' => conan_package.conan_metadatum.package_channel, + 'packageUsername' => conan_package.conan_metadatum.package_username, + 'recipePath' => conan_package.conan_metadatum.recipe_path + ) + end + + it 'has the right amount of files' do + expect(package_files_response.length).to be(conan_package.package_files.length) + end + + it 'has the basic package files data' do + expect(first_file_response).to include( + 'id' => global_id_of(first_file), + 'fileName' => first_file.file_name, + 'size' => first_file.size.to_s, + 'downloadPath' => first_file.download_path, + 'fileSha1' => first_file.file_sha1, + 'fileMd5' => first_file.file_md5, + 'fileSha256' => first_file.file_sha256 + ) + end + + it 'has the correct file metadata' do + expect(first_file_response_metadata).to include( + 'id' => global_id_of(first_file.conan_file_metadatum), + 'packageRevision' => first_file.conan_file_metadatum.package_revision, + 'conanPackageReference' => first_file.conan_file_metadatum.conan_package_reference, + 'recipeRevision' => first_file.conan_file_metadatum.recipe_revision, + 'conanFileType' => first_file.conan_file_metadatum.conan_file_type.upcase + ) + end end end @@ -56,7 +123,7 @@ RSpec.describe 'package details' do let(:depth) { 3 } let(:excluded) { %w[metadata project tags pipelines] } # to limit the query complexity - let_it_be(:siblings) { create_list(:composer_package, 2, project: project, name: package.name) } + let_it_be(:siblings) { create_list(:composer_package, 2, project: project, name: composer_package.name) } it 'includes the sibling versions' do subject @@ -73,8 +140,32 @@ RSpec.describe 'package details' do subject expect(graphql_data_at(:package, :versions, :nodes, :version)).to be_present - expect(graphql_data_at(:package, :versions, :nodes, :versions)).not_to be_present + expect(graphql_data_at(:package, :versions, :nodes, :versions, :nodes)).to be_empty end end end + + context 'with a batched query' do + let_it_be(:conan_package) { create(:conan_package, project: project) } + + let(:batch_query) do + <<~QUERY + { + a: package(id: "#{global_id_of(composer_package)}") { name } + b: package(id: "#{global_id_of(conan_package)}") { name } + } + QUERY + end + + let(:a_packages_names) { graphql_data_at(:a, :packages, :nodes, :name) } + + it 'returns an error for the second package and data for the first' do + post_graphql(batch_query, current_user: user) + + expect(graphql_data_at(:a, :name)).to eq(composer_package.name) + + expect_graphql_errors_to_include [/Package details can be requested only for one package at a time/] + expect(graphql_data_at(:b)).to be(nil) + end + end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index c3ada36b611..01a24be9f20 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -1795,8 +1795,7 @@ RSpec.describe API::Users do post api("/users/#{user.id}/emails", admin), params: email_attrs end.to change { user.emails.count }.by(1) - email = Email.find_by(user_id: user.id, email: email_attrs[:email]) - expect(email).not_to be_confirmed + expect(json_response['confirmed_at']).to be_nil end it "returns a 400 for invalid ID" do @@ -1813,8 +1812,7 @@ RSpec.describe API::Users do expect(response).to have_gitlab_http_status(:created) - email = Email.find_by(user_id: user.id, email: email_attrs[:email]) - expect(email).to be_confirmed + expect(json_response['confirmed_at']).not_to be_nil end end diff --git a/yarn.lock b/yarn.lock index a30bd8dd13e..7d72d00c912 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,10 +907,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8" integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw== -"@gitlab/ui@29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.2.1.tgz#e6377131f3a22eebe07903198ec874ca5d26884c" - integrity sha512-vLzCMQsppGLpzWRaNxXpx86y6JjSOEnQXQxtLudnSJktWl0uHfglED2xe7vJQJo+qmzQf6T5m3gbVupj/pG4+A== +"@gitlab/ui@29.3.0": + version "29.3.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.3.0.tgz#e549b73341246bb9cd1005b2f4c10a599c680cb1" + integrity sha512-TWc3O3w7L+aCLC7Vp2JbYTFgCwseLExxjhwDfJuc2Iwkr+1k8k1ygctbid9XRX2Jcy3JPbL+o+m0K/ZXMJTWdg== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0"