Commit 91daa6a8 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '239518-package-multiple-build-infos' into 'master'

Multiple package pipelines for package history

See merge request gitlab-org/gitlab!44348
parents a79aca71 a1f81a88
# frozen_string_literal: true
class Packages::BuildInfo < ApplicationRecord
belongs_to :package, inverse_of: :build_info
belongs_to :package, inverse_of: :build_infos
belongs_to :pipeline, class_name: 'Ci::Pipeline'
end
......@@ -3,6 +3,7 @@ class Packages::Package < ApplicationRecord
include Sortable
include Gitlab::SQL::Pattern
include UsageStatistics
include Gitlab::Utils::StrongMemoize
belongs_to :project
belongs_to :creator, class_name: 'User'
......@@ -16,7 +17,8 @@ class Packages::Package < ApplicationRecord
has_one :maven_metadatum, inverse_of: :package, class_name: 'Packages::Maven::Metadatum'
has_one :nuget_metadatum, inverse_of: :package, class_name: 'Packages::Nuget::Metadatum'
has_one :composer_metadatum, inverse_of: :package, class_name: 'Packages::Composer::Metadatum'
has_one :build_info, inverse_of: :package
has_many :build_infos, inverse_of: :package
has_many :pipelines, through: :build_infos
accepts_nested_attributes_for :conan_metadatum
accepts_nested_attributes_for :maven_metadatum
......@@ -59,7 +61,7 @@ class Packages::Package < ApplicationRecord
scope :with_version, ->(version) { where(version: version) }
scope :without_version_like, -> (version) { where.not(arel_table[:version].matches(version)) }
scope :with_package_type, ->(package_type) { where(package_type: package_type) }
scope :including_build_info, -> { includes(build_info: { pipeline: :user }) }
scope :including_build_info, -> { includes(pipelines: :user) }
scope :including_project_route, -> { includes(project: { namespace: :route }) }
scope :including_tags, -> { includes(:tags) }
......@@ -166,8 +168,16 @@ class Packages::Package < ApplicationRecord
.order(:version)
end
# Technical debt: to be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/281937
def original_build_info
strong_memoize(:original_build_info) do
build_infos.first
end
end
# Technical debt: to be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/281937
def pipeline
build_info&.pipeline
original_build_info&.pipeline
end
def tag_names
......
......@@ -8,6 +8,8 @@ class Packages::PackageFile < ApplicationRecord
belongs_to :package
has_one :conan_file_metadatum, inverse_of: :package_file, class_name: 'Packages::Conan::FileMetadatum'
has_many :package_file_build_infos, inverse_of: :package_file, class_name: 'Packages::PackageFileBuildInfo'
has_many :pipelines, through: :package_file_build_infos
accepts_nested_attributes_for :conan_file_metadatum
......
# frozen_string_literal: true
class Packages::PackageFileBuildInfo < ApplicationRecord
belongs_to :package_file, inverse_of: :package_file_build_infos
belongs_to :pipeline, class_name: 'Ci::Pipeline'
end
......@@ -29,7 +29,8 @@ module Packages
package_detail[:composer_metadatum] = @package.composer_metadatum if @package.composer_metadatum
package_detail[:conan_metadatum] = @package.conan_metadatum if @package.conan_metadatum
package_detail[:dependency_links] = @package.dependency_links.map(&method(:build_dependency_links))
package_detail[:pipeline] = build_pipeline_info(@package.build_info.pipeline) if @package.build_info
package_detail[:pipeline] = build_pipeline_info(@package.pipeline) if @package.pipeline
package_detail[:pipelines] = build_pipeline_infos(@package.pipelines) if @package.pipelines.present?
package_detail
end
......@@ -37,12 +38,20 @@ module Packages
private
def build_package_file_view(package_file)
{
file_view = {
created_at: package_file.created_at,
download_path: package_file.download_path,
file_name: package_file.file_name,
size: package_file.size
}
file_view[:pipelines] = build_pipeline_infos(package_file.pipelines) if package_file.pipelines.present?
file_view
end
def build_pipeline_infos(pipeline_infos)
pipeline_infos.map { |pipeline_info| build_pipeline_info(pipeline_info) }
end
def build_pipeline_info(pipeline_info)
......
......@@ -9,7 +9,7 @@ module Packages
end
def execute
package.package_files.create!(
package_file = package.package_files.build(
file: params[:file],
size: params[:size],
file_name: params[:file_name],
......@@ -17,6 +17,13 @@ module Packages
file_sha256: params[:file_sha256],
file_md5: params[:file_md5]
)
if params[:build].present?
package_file.package_file_build_infos << package_file.package_file_build_infos.build(pipeline: params[:build].pipeline)
end
package_file.save!
package_file
end
end
end
......@@ -28,7 +28,8 @@ module Packages
file: params[:file],
size: params[:file].size,
file_sha256: params[:file].sha256,
file_name: params[:file_name]
file_name: params[:file_name],
build: params[:build]
}
::Packages::CreatePackageFileService.new(package, file_params).execute
......
......@@ -6,7 +6,7 @@ module Packages
def execute
find_or_create_package!(::Packages::Package.package_types['generic']) do |package|
if params[:build].present?
package.build_info = Packages::BuildInfo.new(pipeline: params[:build].pipeline)
package.build_infos.new(pipeline: params[:build].pipeline)
end
end
end
......
......@@ -6,7 +6,7 @@ module Packages
app_group, _, app_name = params[:name].rpartition('/')
app_group.tr!('/', '.')
package = create_package!(:maven,
create_package!(:maven,
maven_metadatum_attributes: {
path: params[:path],
app_group: app_group,
......@@ -14,11 +14,6 @@ module Packages
app_version: params[:version]
}
)
build = params[:build]
package.create_build_info!(pipeline: build.pipeline) if build.present?
package
end
end
end
......
......@@ -38,8 +38,7 @@ module Packages
package_params = {
name: package_name,
path: params[:path],
version: version,
build: params[:build]
version: version
}
package =
......@@ -47,6 +46,8 @@ module Packages
.execute
end
package.build_infos.create!(pipeline: params[:build].pipeline) if params[:build].present?
package
end
end
......
......@@ -18,7 +18,7 @@ module Packages
package = create_package!(:npm, name: name, version: version)
if build.present?
package.create_build_info!(pipeline: build.pipeline)
package.build_infos.create!(pipeline: build.pipeline)
end
::Packages::CreatePackageFileService.new(package, file_params).execute
......@@ -75,7 +75,8 @@ module Packages
file: CarrierWaveStringFile.new(Base64.decode64(attachment['data'])),
size: attachment['length'],
file_sha1: version_data[:dist][:shasum],
file_name: package_file_name
file_name: package_file_name,
build: params[:build]
}
end
......
---
title: Associate multiple pipelines with packages and package files
merge_request: 44348
author:
type: added
# frozen_string_literal: true
class CreatePackagesPackageFileBuildInfos < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
unless table_exists?(:packages_package_file_build_infos)
with_lock_retries do
create_table :packages_package_file_build_infos do |t|
t.references :package_file, index: true,
null: false,
foreign_key: { to_table: :packages_package_files, on_delete: :cascade },
type: :bigint
t.references :pipeline, index: true,
null: true,
foreign_key: { to_table: :ci_pipelines, on_delete: :nullify },
type: :bigint
end
end
end
end
def down
with_lock_retries do
drop_table :packages_package_file_build_infos
end
end
end
# frozen_string_literal: true
class UpdatePackagesBuildInfosIndex < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
NEW_INDEX_NAME = 'idx_packages_build_infos_on_package_id'
OLD_INDEX_NAME = 'index_packages_build_infos_on_package_id'
def up
add_concurrent_index :packages_build_infos, :package_id, name: NEW_INDEX_NAME
remove_concurrent_index_by_name :packages_build_infos, OLD_INDEX_NAME
end
def down
# No op. It is possible records would validate the UNIQUE constraint, so it
# cannot be added back to the index.
end
end
a3117169b472fffd6302a4da17850a11474c196fd06ebeb3f6b28705d59c74ba
\ No newline at end of file
af006a3c01620c258a253b5c9ccca6faa4cb46a97dddbfeecc0ecc4454eb46e6
\ No newline at end of file
......@@ -14451,6 +14451,21 @@ CREATE TABLE packages_nuget_metadata (
CONSTRAINT packages_nuget_metadata_project_url_constraint CHECK ((char_length(project_url) <= 255))
);
CREATE TABLE packages_package_file_build_infos (
id bigint NOT NULL,
package_file_id bigint NOT NULL,
pipeline_id bigint
);
CREATE SEQUENCE packages_package_file_build_infos_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE packages_package_file_build_infos_id_seq OWNED BY packages_package_file_build_infos.id;
CREATE TABLE packages_package_files (
id bigint NOT NULL,
package_id bigint NOT NULL,
......@@ -18057,6 +18072,8 @@ ALTER TABLE ONLY packages_events ALTER COLUMN id SET DEFAULT nextval('packages_e
ALTER TABLE ONLY packages_maven_metadata ALTER COLUMN id SET DEFAULT nextval('packages_maven_metadata_id_seq'::regclass);
ALTER TABLE ONLY packages_package_file_build_infos ALTER COLUMN id SET DEFAULT nextval('packages_package_file_build_infos_id_seq'::regclass);
ALTER TABLE ONLY packages_package_files ALTER COLUMN id SET DEFAULT nextval('packages_package_files_id_seq'::regclass);
ALTER TABLE ONLY packages_packages ALTER COLUMN id SET DEFAULT nextval('packages_packages_id_seq'::regclass);
......@@ -19342,6 +19359,9 @@ ALTER TABLE ONLY packages_nuget_dependency_link_metadata
ALTER TABLE ONLY packages_nuget_metadata
ADD CONSTRAINT packages_nuget_metadata_pkey PRIMARY KEY (package_id);
ALTER TABLE ONLY packages_package_file_build_infos
ADD CONSTRAINT packages_package_file_build_infos_pkey PRIMARY KEY (id);
ALTER TABLE ONLY packages_package_files
ADD CONSTRAINT packages_package_files_pkey PRIMARY KEY (id);
......@@ -20015,6 +20035,8 @@ CREATE INDEX idx_mr_cc_diff_files_on_mr_cc_id_and_sha ON merge_request_context_c
CREATE UNIQUE INDEX idx_on_compliance_management_frameworks_namespace_id_name ON compliance_management_frameworks USING btree (namespace_id, name);
CREATE INDEX idx_packages_build_infos_on_package_id ON packages_build_infos USING btree (package_id);
CREATE INDEX idx_packages_packages_on_project_id_name_version_package_type ON packages_packages USING btree (project_id, name, version, package_type);
CREATE UNIQUE INDEX idx_pkgs_dep_links_on_pkg_id_dependency_id_dependency_type ON packages_dependency_links USING btree (package_id, dependency_id, dependency_type);
......@@ -21407,8 +21429,6 @@ CREATE UNIQUE INDEX index_ops_feature_flags_issues_on_feature_flag_id_and_issue_
CREATE UNIQUE INDEX index_ops_strategies_user_lists_on_strategy_id_and_user_list_id ON operations_strategies_user_lists USING btree (strategy_id, user_list_id);
CREATE UNIQUE INDEX index_packages_build_infos_on_package_id ON packages_build_infos USING btree (package_id);
CREATE INDEX index_packages_build_infos_on_pipeline_id ON packages_build_infos USING btree (pipeline_id);
CREATE UNIQUE INDEX index_packages_composer_metadata_on_package_id_and_target_sha ON packages_composer_metadata USING btree (package_id, target_sha);
......@@ -21429,6 +21449,10 @@ CREATE INDEX index_packages_nuget_dl_metadata_on_dependency_link_id ON packages_
CREATE UNIQUE INDEX index_packages_on_project_id_name_version_unique_when_generic ON packages_packages USING btree (project_id, name, version) WHERE (package_type = 7);
CREATE INDEX index_packages_package_file_build_infos_on_package_file_id ON packages_package_file_build_infos USING btree (package_file_id);
CREATE INDEX index_packages_package_file_build_infos_on_pipeline_id ON packages_package_file_build_infos USING btree (pipeline_id);
CREATE INDEX index_packages_package_files_on_file_store ON packages_package_files USING btree (file_store);
CREATE INDEX index_packages_package_files_on_package_id_and_file_name ON packages_package_files USING btree (package_id, file_name);
......@@ -23589,6 +23613,9 @@ ALTER TABLE ONLY snippet_user_mentions
ALTER TABLE ONLY clusters_applications_helm
ADD CONSTRAINT fk_rails_3e2b1c06bc FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_package_file_build_infos
ADD CONSTRAINT fk_rails_3e3f630188 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE SET NULL;
ALTER TABLE ONLY epic_user_mentions
ADD CONSTRAINT fk_rails_3eaf4d88cc FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
......@@ -23973,6 +24000,9 @@ ALTER TABLE ONLY merge_request_diff_details
ALTER TABLE ONLY clusters_applications_crossplane
ADD CONSTRAINT fk_rails_87186702df FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_package_file_build_infos
ADD CONSTRAINT fk_rails_871ca3ae21 FOREIGN KEY (package_file_id) REFERENCES packages_package_files(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_runner_namespaces
ADD CONSTRAINT fk_rails_8767676b7a FOREIGN KEY (runner_id) REFERENCES ci_runners(id) ON DELETE CASCADE;
......
......@@ -93,9 +93,10 @@ GET /groups/:id/packages
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/packages?exclude_subgroups=true"
```
CAUTION: **Deprecation:**
> The `build_info` attribute in the response is deprecated in favour of `pipeline`.
> Introduced [GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28040).
> **Deprecation:**
>
> The `pipeline` attribute in the response is deprecated in favor of `pipelines`, which was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44348) in GitLab 13.6. Both are available until 13.7.
> The `build_info` attribute in the response is deprecated in favor of `pipeline`, which was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28040) in GitLab 12.10.
Example response:
......@@ -111,19 +112,21 @@ Example response:
"delete_api_path": "/namespace1/project1/-/packages/1"
},
"created_at": "2019-11-27T03:37:38.711Z",
"pipeline": {
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
"pipelines": [
{
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
}
}
}
]
},
{
"id": 2,
......@@ -135,19 +138,21 @@ Example response:
"delete_api_path": "/namespace1/project1/-/packages/1"
},
"created_at": "2019-11-27T03:37:38.711Z",
"pipeline": {
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
"pipelines": [
{
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
}
}
}
]
}
]
```
......@@ -178,9 +183,10 @@ GET /projects/:id/packages/:package_id
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/:id/packages/:package_id"
```
CAUTION: **Deprecation:**
> The `build_info` attribute in the response is deprecated in favour of `pipeline`.
> Introduced [GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28040).
> **Deprecation:**
>
> The `pipeline` attribute in the response is deprecated in favor of `pipelines`, which was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44348) in GitLab 13.6. Both are available until 13.7.
> The `build_info` attribute in the response is deprecated in favor of `pipeline`, which was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28040) in GitLab 12.10.
Example response:
......@@ -195,37 +201,41 @@ Example response:
"delete_api_path": "/namespace1/project1/-/packages/1"
},
"created_at": "2019-11-27T03:37:38.711Z",
"pipeline": {
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
"pipelines": [
{
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
}
}
},
],
"versions": [
{
"id":2,
"version":"2.0-SNAPSHOT",
"created_at":"2020-04-28T04:42:11.573Z",
"pipeline": {
"id": 234,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/58",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
"pipelines": [
{
"id": 234,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/58",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
}
}
}
]
}
]
}
......@@ -266,7 +276,22 @@ Example response:
"file_name": "my-app-1.5-20181107.152550-1.jar",
"size": 2421,
"file_md5": "58e6a45a629910c6ff99145a688971ac",
"file_sha1": "ebd193463d3915d7e22219f52740056dfd26cbfe"
"file_sha1": "ebd193463d3915d7e22219f52740056dfd26cbfe",
"pipelines": [
{
"id": 123,
"status": "pending",
"ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47",
"created_at": "2016-08-11T11:28:34.085Z",
"updated_at": "2016-08-11T11:32:35.169Z",
"user": {
"name": "Administrator",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
}
}
]
},
{
"id": 26,
......
......@@ -38,7 +38,8 @@ module API
expose :project_path, if: ->(obj, opts) { opts[:group] && Ability.allowed?(opts[:user], :read_project, obj.project) }
expose :tags
expose :pipeline, if: ->(package) { package.build_info }, using: Package::Pipeline
expose :pipeline, if: ->(package) { package.original_build_info }, using: Package::Pipeline
expose :pipelines, if: ->(package) { package.pipelines.present? }, using: Package::Pipeline
expose :versions, using: ::API::Entities::PackageVersion, unless: ->(_, opts) { opts[:collection] }
......
......@@ -6,6 +6,7 @@ module API
expose :id, :package_id, :created_at
expose :file_name, :size
expose :file_md5, :file_sha1
expose :pipelines, if: ->(package_file) { package_file.pipelines.present? }, using: Package::Pipeline
end
end
end
......@@ -8,7 +8,7 @@ module API
expose :created_at
expose :tags
expose :pipeline, if: ->(package) { package.build_info }, using: Package::Pipeline
expose :pipeline, if: ->(package) { package.original_build_info }, using: Package::Pipeline
end
end
end
......@@ -246,7 +246,7 @@ module API
file_md5: params['file.md5']
}
::Packages::CreatePackageFileService.new(package, file_params).execute
::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job)).execute
end
end
end
......
......@@ -147,10 +147,6 @@ FactoryBot.define do
composer_json { { name: 'foo' } }
end
factory :package_build_info, class: 'Packages::BuildInfo' do
package
end
factory :maven_metadatum, class: 'Packages::Maven::Metadatum' do
association :package, package_type: :maven
path { 'my/company/app/my-app/1.0-SNAPSHOT' }
......
# frozen_string_literal: true
FactoryBot.define do
factory :package_build_info, class: 'Packages::BuildInfo' do
package
trait :with_pipeline do
association :pipeline, factory: [:ci_pipeline, :with_job]
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :package_file_build_info, class: 'Packages::PackageFileBuildInfo' do
package_file
trait :with_pipeline do
association :pipeline, factory: [:ci_pipeline, :with_job]
end
end
end
......@@ -7,7 +7,10 @@
"id": { "type": "integer" },
"package_id": { "type": "integer" },
"file_name": { "type": "string" },
"file_sha1": { "type": "string" }
"file_sha1": { "type": "string" },
"pipelines": {
"items": { "$ref": "../pipeline.json" }
}
}
}
}
......@@ -5,6 +5,9 @@
"name": { "type": "string" },
"version": { "type": "string" },
"package_type": { "type": "string" },
"pipeline": { "$ref": "../pipeline.json" }
"pipeline": { "$ref": "../pipeline.json" },
"pipelines": {
"items": { "$ref": "../pipeline.json" }
}
}
}
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::BuildInfo, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:package) }
it { is_expected.to belong_to(:pipeline) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::PackageFileBuildInfo, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:package_file) }
it { is_expected.to belong_to(:pipeline) }
end
end
......@@ -5,6 +5,8 @@ RSpec.describe Packages::PackageFile, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:package) }
it { is_expected.to have_one(:conan_file_metadatum) }
it { is_expected.to have_many(:package_file_build_infos).inverse_of(:package_file) }
it { is_expected.to have_many(:pipelines).through(:package_file_build_infos) }
end
describe 'validations' do
......
......@@ -10,6 +10,8 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to have_many(:package_files).dependent(:destroy) }
it { is_expected.to have_many(:dependency_links).inverse_of(:package) }
it { is_expected.to have_many(:tags).inverse_of(:package) }
it { is_expected.to have_many(:build_infos).inverse_of(:package) }
it { is_expected.to have_many(:pipelines).through(:build_infos) }
it { is_expected.to have_one(:conan_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:maven_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) }
......@@ -580,7 +582,7 @@ RSpec.describe Packages::Package, type: :model do
end
describe '#pipeline' do
let_it_be(:package) { create(:maven_package) }
let_it_be_with_refind(:package) { create(:maven_package) }
context 'package without pipeline' do
it 'returns nil if there is no pipeline' do
......@@ -592,7 +594,7 @@ RSpec.describe Packages::Package, type: :model do
let_it_be(:pipeline) { create(:ci_pipeline) }
before do
package.create_build_info!(pipeline: pipeline)
package.build_infos.create!(pipeline: pipeline)
end
it 'returns the pipeline' do
......@@ -637,4 +639,23 @@ RSpec.describe Packages::Package, type: :model do
end
end
end
describe '#original_build_info' do
let_it_be_with_refind(:package) { create(:npm_package) }
context 'without build_infos' do
it 'returns nil' do
expect(package.original_build_info).to be_nil
end
end
context 'with build_infos' do
let_it_be(:first_build_info) { create(:package_build_info, :with_pipeline, package: package) }
let_it_be(:second_build_info) { create(:package_build_info, :with_pipeline, package: package) }
it 'returns the first build info' do
expect(package.original_build_info).to eq(first_build_info)
end
end
end
end
......@@ -22,7 +22,7 @@ RSpec.describe ::Packages::Detail::PackagePresenter do
end
let(:pipeline_info) do
pipeline = package.build_info.pipeline
pipeline = package.original_build_info.pipeline
{
created_at: pipeline.created_at,
id: pipeline.id,
......@@ -56,16 +56,44 @@ RSpec.describe ::Packages::Detail::PackagePresenter do
}
end
context 'detail_view' do
describe '#detail_view' do
context 'with build_info' do
let_it_be(:package) { create(:npm_package, :with_build, project: project) }
let(:expected_package_details) { super().merge(pipeline: pipeline_info) }
let(:expected_package_details) do
super().merge(
pipeline: pipeline_info,
pipelines: [pipeline_info]
)
end
it 'returns details with pipeline' do
expect(presenter.detail_view).to match expected_package_details
end
end
context 'with multiple build_infos' do
let_it_be(:package) { create(:npm_package, :with_build, project: project) }
let_it_be(:build_info2) { create(:package_build_info, :with_pipeline, package: package) }
it 'returns details with two pipelines' do
expect(presenter.detail_view[:pipelines].size).to eq(2)
end
end
context 'with package_file_build_infos' do
let_it_be(:package) { create(:npm_package, :with_build, project: project) }
let_it_be(:package_file_build_info) do
create(:package_file_build_info, package_file: package.package_files.first,
pipeline: package.pipelines.first)
end
it 'returns details with package_file pipeline' do
expect(presenter.detail_view[:package_files].first[:pipelines].size).to eq(1)
end
end
context 'without build info' do
let_it_be(:package) { create(:npm_package, project: project) }
......
......@@ -195,7 +195,7 @@ RSpec.describe API::GenericPackages do
package = project.packages.generic.last
expect(package.name).to eq('mypackage')
expect(package.version).to eq('0.0.1')
expect(package.build_info).to be_nil
expect(package.original_build_info).to be_nil
package_file = package.package_files.last
expect(package_file.file_name).to eq('myfile.tar.gz')
......@@ -215,7 +215,7 @@ RSpec.describe API::GenericPackages do
package = project.packages.generic.last
expect(package.name).to eq('mypackage')
expect(package.version).to eq('0.0.1')
expect(package.build_info.pipeline).to eq(ci_build.pipeline)
expect(package.original_build_info.pipeline).to eq(ci_build.pipeline)
package_file = package.package_files.last
expect(package_file.file_name).to eq('myfile.tar.gz')
......
......@@ -625,7 +625,7 @@ RSpec.describe API::MavenPackages do
upload_file(params: params.merge(job_token: job.token))
expect(response).to have_gitlab_http_status(:ok)
expect(project.reload.packages.last.build_info.pipeline).to eq job.pipeline
expect(project.reload.packages.last.original_build_info.pipeline).to eq job.pipeline
end
it 'rejects upload without running job token' do
......
......@@ -211,7 +211,7 @@ RSpec.describe API::NpmProjectPackages do
upload_package_with_token(package_name, params)
expect(response).to have_gitlab_http_status(:ok)
expect(project.reload.packages.find(json_response['id']).build_info.pipeline).to eq job.pipeline
expect(project.reload.packages.find(json_response['id']).original_build_info.pipeline).to eq job.pipeline
end
end
end
......
......@@ -2,7 +2,10 @@
require 'spec_helper'
RSpec.describe Packages::CreatePackageFileService do
let(:package) { create(:maven_package) }
let_it_be(:package) { create(:maven_package) }
let_it_be(:user) { create(:user) }
subject { described_class.new(package, params) }
describe '#execute' do
context 'with valid params' do
......@@ -14,7 +17,7 @@ RSpec.describe Packages::CreatePackageFileService do
end
it 'creates a new package file' do
package_file = described_class.new(package, params).execute
package_file = subject.execute
expect(package_file).to be_valid
expect(package_file.file_name).to eq('foo.jar')
......@@ -29,9 +32,17 @@ RSpec.describe Packages::CreatePackageFileService do
end
it 'raises an error' do
service = described_class.new(package, params)
expect { subject.execute }.to raise_error(ActiveRecord::RecordInvalid)
end
end
context 'with a build' do
let_it_be(:pipeline) { create(:ci_pipeline, user: user) }
let(:build) { double('build', pipeline: pipeline) }
let(:params) { { file: Tempfile.new, file_name: 'foo.jar', build: build } }
expect { service.execute }.to raise_error(ActiveRecord::RecordInvalid)
it 'creates a build_info' do
expect { subject.execute }.to change { Packages::PackageFileBuildInfo.count }.by(1)
end
end
end
......
......@@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe Packages::Generic::CreatePackageFileService do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:pipeline) { create(:ci_pipeline, user: user) }
let(:build) { double('build', pipeline: pipeline) }
describe '#execute' do
let(:sha256) { '440e5e148a25331bbd7991575f7d54933c0ebf6cc735a18ee5066ac1381bb590' }
......@@ -16,7 +18,8 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
package_name: 'mypackage',
package_version: '0.0.1',
file: file,
file_name: 'myfile.tar.gz.1'
file_name: 'myfile.tar.gz.1',
build: build
}
end
......@@ -41,6 +44,7 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
service = described_class.new(project, user, params)
expect { service.execute }.to change { package.package_files.count }.by(1)
.and change { Packages::PackageFileBuildInfo.count }.by(1)
package_file = package.package_files.last
aggregate_failures do
......
......@@ -27,7 +27,7 @@ RSpec.describe Packages::Generic::FindOrCreatePackageService do
expect(package.creator).to eq(user)
expect(package.name).to eq('mypackage')
expect(package.version).to eq('0.0.1')
expect(package.build_info).to be_nil
expect(package.original_build_info).to be_nil
end
end
......@@ -42,7 +42,7 @@ RSpec.describe Packages::Generic::FindOrCreatePackageService do
expect(package.creator).to eq(user)
expect(package.name).to eq('mypackage')
expect(package.version).to eq('0.0.1')
expect(package.build_info.pipeline).to eq(ci_build.pipeline)
expect(package.original_build_info.pipeline).to eq(ci_build.pipeline)
end
end
end
......@@ -60,7 +60,7 @@ RSpec.describe Packages::Generic::FindOrCreatePackageService do
expect(found_package).to eq(package)
end.not_to change { project.packages.generic.count }
expect(package.reload.build_info).to be_nil
expect(package.reload.original_build_info).to be_nil
end
end
......@@ -68,7 +68,7 @@ RSpec.describe Packages::Generic::FindOrCreatePackageService do
let(:pipeline) { create(:ci_pipeline, project: project) }
before do
package.create_build_info!(pipeline: pipeline)
package.build_infos.create!(pipeline: pipeline)
end
it 'finds the package and does not change package build info even if build is provided' do
......@@ -80,7 +80,7 @@ RSpec.describe Packages::Generic::FindOrCreatePackageService do
expect(found_package).to eq(package)
end.not_to change { project.packages.generic.count }
expect(package.reload.build_info.pipeline).to eq(pipeline)
expect(package.reload.original_build_info.pipeline).to eq(pipeline)
end
end
end
......
......@@ -33,8 +33,6 @@ RSpec.describe Packages::Maven::CreatePackageService do
expect(package.maven_metadatum.app_version).to eq(version)
end
it_behaves_like 'assigns build to package'
it_behaves_like 'assigns the package creator'
end
......
......@@ -10,11 +10,12 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
let(:version) { '1.0.0' }
let(:file_name) { 'test.jar' }
let(:param_path) { "#{path}/#{version}" }
let(:params) { { path: param_path, file_name: file_name } }
describe '#execute' do
using RSpec::Parameterized::TableSyntax
subject { described_class.new(project, user, { path: param_path, file_name: file_name }).execute }
subject { described_class.new(project, user, params).execute }
RSpec.shared_examples 'reuse existing package' do
it { expect { subject}.not_to change { Packages::Package.count } }
......@@ -23,7 +24,7 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
end
RSpec.shared_examples 'create package' do
it { expect { subject}.to change { Packages::Package.count }.by(1) }
it { expect { subject }.to change { Packages::Package.count }.by(1) }
it 'sets the proper name and version' do
pkg = subject
......@@ -31,6 +32,8 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
expect(pkg.name).to eq(path)
expect(pkg.version).to eq(version)
end
it_behaves_like 'assigns build to package'
end
context 'path with version' do
......@@ -77,5 +80,15 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
end
end
end
context 'with a build' do
let_it_be(:pipeline) { create(:ci_pipeline, user: user) }
let(:build) { double('build', pipeline: pipeline) }
let(:params) { { path: param_path, file_name: file_name, build: build } }
it 'creates a build_info' do
expect { subject }.to change { Packages::BuildInfo.count }.by(1)
end
end
end
end
......@@ -48,7 +48,16 @@ RSpec.describe Packages::Npm::CreatePackageService do
context 'scoped package' do
it_behaves_like 'valid package'
it_behaves_like 'assigns build to package'
context 'with build info' do
let(:job) { create(:ci_build, user: user) }
let(:params) { super().merge(build: job) }
it_behaves_like 'assigns build to package'
it 'creates a package file build info' do
expect { subject }.to change { Packages::PackageFileBuildInfo.count }.by(1)
end
end
end
context 'invalid package name' do
......
......@@ -8,8 +8,8 @@ RSpec.shared_examples 'assigns build to package' do
it 'assigns the pipeline to the package' do
package = subject
expect(package.build_info).to be_present
expect(package.build_info.pipeline).to eq job.pipeline
expect(package.original_build_info).to be_present
expect(package.original_build_info.pipeline).to eq job.pipeline
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment