Commit 7eb04fa1 authored by Mathieu Parent's avatar Mathieu Parent

Debian Publications

parent 7a75ec47
...@@ -5,5 +5,8 @@ class Packages::Debian::ProjectDistribution < ApplicationRecord ...@@ -5,5 +5,8 @@ class Packages::Debian::ProjectDistribution < ApplicationRecord
:project :project
end end
has_many :publications, class_name: 'Packages::Debian::Publication', inverse_of: :distribution, foreign_key: :distribution_id
has_many :packages, class_name: 'Packages::Package', through: :publications
include Packages::Debian::Distribution include Packages::Debian::Distribution
end end
# frozen_string_literal: true
class Packages::Debian::Publication < ApplicationRecord
belongs_to :package,
-> { where(package_type: :debian).where.not(version: nil) },
inverse_of: :debian_publication,
class_name: 'Packages::Package'
belongs_to :distribution,
inverse_of: :publications,
class_name: 'Packages::Debian::ProjectDistribution',
foreign_key: :distribution_id
validates :package, presence: true
validate :valid_debian_package_type
validates :distribution, presence: true
private
def valid_debian_package_type
return errors.add(:package, _('type must be Debian')) unless package&.debian?
return errors.add(:package, _('must be a Debian package')) unless package.debian_package?
end
end
...@@ -19,11 +19,15 @@ class Packages::Package < ApplicationRecord ...@@ -19,11 +19,15 @@ class Packages::Package < ApplicationRecord
has_one :composer_metadatum, inverse_of: :package, class_name: 'Packages::Composer::Metadatum' has_one :composer_metadatum, inverse_of: :package, class_name: 'Packages::Composer::Metadatum'
has_many :build_infos, inverse_of: :package has_many :build_infos, inverse_of: :package
has_many :pipelines, through: :build_infos has_many :pipelines, through: :build_infos
has_one :debian_publication, inverse_of: :package, class_name: 'Packages::Debian::Publication'
has_one :debian_distribution, through: :debian_publication, source: :distribution, inverse_of: :packages, class_name: 'Packages::Debian::ProjectDistribution'
accepts_nested_attributes_for :conan_metadatum accepts_nested_attributes_for :conan_metadatum
accepts_nested_attributes_for :debian_publication
accepts_nested_attributes_for :maven_metadatum accepts_nested_attributes_for :maven_metadatum
delegate :recipe, :recipe_path, to: :conan_metadatum, prefix: :conan delegate :recipe, :recipe_path, to: :conan_metadatum, prefix: :conan
delegate :codename, :suite, to: :debian_distribution, prefix: :debian_distribution
validates :project, presence: true validates :project, presence: true
validates :name, presence: true validates :name, presence: true
...@@ -31,7 +35,8 @@ class Packages::Package < ApplicationRecord ...@@ -31,7 +35,8 @@ class Packages::Package < ApplicationRecord
validates :name, format: { with: Gitlab::Regex.package_name_regex }, unless: -> { conan? || generic? || debian? } validates :name, format: { with: Gitlab::Regex.package_name_regex }, unless: -> { conan? || generic? || debian? }
validates :name, validates :name,
uniqueness: { scope: %i[project_id version package_type] }, unless: :conan? uniqueness: { scope: %i[project_id version package_type] }, unless: -> { conan? || debian_package? }
validate :unique_debian_package_name, if: :debian_package?
validate :valid_conan_package_recipe, if: :conan? validate :valid_conan_package_recipe, if: :conan?
validate :valid_npm_package_name, if: :npm? validate :valid_npm_package_name, if: :npm?
...@@ -251,6 +256,18 @@ class Packages::Package < ApplicationRecord ...@@ -251,6 +256,18 @@ class Packages::Package < ApplicationRecord
end end
end end
def unique_debian_package_name
return unless debian_publication&.distribution
package_exists = debian_publication.distribution.packages
.with_name(name)
.with_version(version)
.id_not_in(id)
.exists?
errors.add(:base, _('Debian package already exists in Distribution')) if package_exists
end
def forbidden_debian_changes def forbidden_debian_changes
return unless persisted? return unless persisted?
......
---
title: Debian Publications
merge_request: 52916
author: Mathieu Parent
type: added
# frozen_string_literal: true
class CreatePackagesDebianPublications < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :packages_debian_publications do |t|
t.references :package,
index: { unique: true },
null: false,
foreign_key: { to_table: :packages_packages, on_delete: :cascade }
t.references :distribution,
null: false,
foreign_key: { to_table: :packages_debian_project_distributions, on_delete: :cascade }
end
end
end
51967d740ce184b27d0d9417fc86cb896fd3e3aa8a5e40759b290f47b9f3e99b
\ No newline at end of file
...@@ -15030,6 +15030,21 @@ CREATE SEQUENCE packages_debian_project_distributions_id_seq ...@@ -15030,6 +15030,21 @@ CREATE SEQUENCE packages_debian_project_distributions_id_seq
ALTER SEQUENCE packages_debian_project_distributions_id_seq OWNED BY packages_debian_project_distributions.id; ALTER SEQUENCE packages_debian_project_distributions_id_seq OWNED BY packages_debian_project_distributions.id;
CREATE TABLE packages_debian_publications (
id bigint NOT NULL,
package_id bigint NOT NULL,
distribution_id bigint NOT NULL
);
CREATE SEQUENCE packages_debian_publications_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE packages_debian_publications_id_seq OWNED BY packages_debian_publications.id;
CREATE TABLE packages_dependencies ( CREATE TABLE packages_dependencies (
id bigint NOT NULL, id bigint NOT NULL,
name character varying(255) NOT NULL, name character varying(255) NOT NULL,
...@@ -19015,6 +19030,8 @@ ALTER TABLE ONLY packages_debian_project_components ALTER COLUMN id SET DEFAULT ...@@ -19015,6 +19030,8 @@ ALTER TABLE ONLY packages_debian_project_components ALTER COLUMN id SET DEFAULT
ALTER TABLE ONLY packages_debian_project_distributions ALTER COLUMN id SET DEFAULT nextval('packages_debian_project_distributions_id_seq'::regclass); ALTER TABLE ONLY packages_debian_project_distributions ALTER COLUMN id SET DEFAULT nextval('packages_debian_project_distributions_id_seq'::regclass);
ALTER TABLE ONLY packages_debian_publications ALTER COLUMN id SET DEFAULT nextval('packages_debian_publications_id_seq'::regclass);
ALTER TABLE ONLY packages_dependencies ALTER COLUMN id SET DEFAULT nextval('packages_dependencies_id_seq'::regclass); ALTER TABLE ONLY packages_dependencies ALTER COLUMN id SET DEFAULT nextval('packages_dependencies_id_seq'::regclass);
ALTER TABLE ONLY packages_dependency_links ALTER COLUMN id SET DEFAULT nextval('packages_dependency_links_id_seq'::regclass); ALTER TABLE ONLY packages_dependency_links ALTER COLUMN id SET DEFAULT nextval('packages_dependency_links_id_seq'::regclass);
...@@ -20398,6 +20415,9 @@ ALTER TABLE ONLY packages_debian_project_components ...@@ -20398,6 +20415,9 @@ ALTER TABLE ONLY packages_debian_project_components
ALTER TABLE ONLY packages_debian_project_distributions ALTER TABLE ONLY packages_debian_project_distributions
ADD CONSTRAINT packages_debian_project_distributions_pkey PRIMARY KEY (id); ADD CONSTRAINT packages_debian_project_distributions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY packages_debian_publications
ADD CONSTRAINT packages_debian_publications_pkey PRIMARY KEY (id);
ALTER TABLE ONLY packages_dependencies ALTER TABLE ONLY packages_dependencies
ADD CONSTRAINT packages_dependencies_pkey PRIMARY KEY (id); ADD CONSTRAINT packages_dependencies_pkey PRIMARY KEY (id);
...@@ -22623,6 +22643,10 @@ CREATE INDEX index_packages_debian_project_distributions_on_creator_id ON packag ...@@ -22623,6 +22643,10 @@ CREATE INDEX index_packages_debian_project_distributions_on_creator_id ON packag
CREATE INDEX index_packages_debian_project_distributions_on_project_id ON packages_debian_project_distributions USING btree (project_id); CREATE INDEX index_packages_debian_project_distributions_on_project_id ON packages_debian_project_distributions USING btree (project_id);
CREATE INDEX index_packages_debian_publications_on_distribution_id ON packages_debian_publications USING btree (distribution_id);
CREATE UNIQUE INDEX index_packages_debian_publications_on_package_id ON packages_debian_publications USING btree (package_id);
CREATE UNIQUE INDEX index_packages_dependencies_on_name_and_version_pattern ON packages_dependencies USING btree (name, version_pattern); CREATE UNIQUE INDEX index_packages_dependencies_on_name_and_version_pattern ON packages_dependencies USING btree (name, version_pattern);
CREATE INDEX index_packages_dependency_links_on_dependency_id ON packages_dependency_links USING btree (dependency_id); CREATE INDEX index_packages_dependency_links_on_dependency_id ON packages_dependency_links USING btree (dependency_id);
...@@ -25032,6 +25056,9 @@ ALTER TABLE ONLY aws_roles ...@@ -25032,6 +25056,9 @@ ALTER TABLE ONLY aws_roles
ALTER TABLE ONLY security_scans ALTER TABLE ONLY security_scans
ADD CONSTRAINT fk_rails_4ef1e6b4c6 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_4ef1e6b4c6 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_debian_publications
ADD CONSTRAINT fk_rails_4fc8ebd03e FOREIGN KEY (distribution_id) REFERENCES packages_debian_project_distributions(id) ON DELETE CASCADE;
ALTER TABLE ONLY merge_request_diff_files ALTER TABLE ONLY merge_request_diff_files
ADD CONSTRAINT fk_rails_501aa0a391 FOREIGN KEY (merge_request_diff_id) REFERENCES merge_request_diffs(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_501aa0a391 FOREIGN KEY (merge_request_diff_id) REFERENCES merge_request_diffs(id) ON DELETE CASCADE;
...@@ -25275,6 +25302,9 @@ ALTER TABLE ONLY x509_certificates ...@@ -25275,6 +25302,9 @@ ALTER TABLE ONLY x509_certificates
ALTER TABLE ONLY pages_domain_acme_orders ALTER TABLE ONLY pages_domain_acme_orders
ADD CONSTRAINT fk_rails_76581b1c16 FOREIGN KEY (pages_domain_id) REFERENCES pages_domains(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_76581b1c16 FOREIGN KEY (pages_domain_id) REFERENCES pages_domains(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_debian_publications
ADD CONSTRAINT fk_rails_7668c1d606 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
ALTER TABLE ONLY boards_epic_user_preferences ALTER TABLE ONLY boards_epic_user_preferences
ADD CONSTRAINT fk_rails_76c4e9732d FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_76c4e9732d FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
......
...@@ -9290,6 +9290,9 @@ msgstr "" ...@@ -9290,6 +9290,9 @@ msgstr ""
msgid "Dear Administrator," msgid "Dear Administrator,"
msgstr "" msgstr ""
msgid "Debian package already exists in Distribution"
msgstr ""
msgid "Debug" msgid "Debug"
msgstr "" msgstr ""
...@@ -34940,6 +34943,9 @@ msgstr "" ...@@ -34940,6 +34943,9 @@ msgstr ""
msgid "mrWidget|to start a merge train when the pipeline succeeds" msgid "mrWidget|to start a merge train when the pipeline succeeds"
msgstr "" msgstr ""
msgid "must be a Debian package"
msgstr ""
msgid "must be a boolean value" msgid "must be a boolean value"
msgstr "" msgstr ""
...@@ -35316,6 +35322,9 @@ msgstr "" ...@@ -35316,6 +35322,9 @@ msgstr ""
msgid "two-factor authentication settings" msgid "two-factor authentication settings"
msgstr "" msgstr ""
msgid "type must be Debian"
msgstr ""
msgid "unicode domains should use IDNA encoding" msgid "unicode domains should use IDNA encoding"
msgstr "" msgstr ""
......
...@@ -29,6 +29,15 @@ FactoryBot.define do ...@@ -29,6 +29,15 @@ FactoryBot.define do
transient do transient do
without_package_files { false } without_package_files { false }
file_metadatum_trait { :keep } file_metadatum_trait { :keep }
published_in { :create }
end
after :build do |package, evaluator|
if evaluator.published_in == :create
create(:debian_publication, package: package)
elsif !evaluator.published_in.nil?
create(:debian_publication, package: package, distribution: evaluator.published_in)
end
end end
after :create do |package, evaluator| after :create do |package, evaluator|
...@@ -50,6 +59,7 @@ FactoryBot.define do ...@@ -50,6 +59,7 @@ FactoryBot.define do
transient do transient do
without_package_files { false } without_package_files { false }
file_metadatum_trait { :unknown } file_metadatum_trait { :unknown }
published_in { nil }
end end
end end
end end
......
# frozen_string_literal: true
FactoryBot.define do
factory :debian_publication, class: 'Packages::Debian::Publication' do
package { association(:debian_package, published_in: nil) }
distribution { association(:debian_project_distribution, project: package.project) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::Debian::Publication, type: :model do
let_it_be_with_reload(:publication) { create(:debian_publication) }
subject { publication }
describe 'relationships' do
it { is_expected.to belong_to(:package).inverse_of(:debian_publication).class_name('Packages::Package') }
it { is_expected.to belong_to(:distribution).inverse_of(:publications).class_name('Packages::Debian::ProjectDistribution').with_foreign_key(:distribution_id) }
end
describe 'validations' do
describe '#package' do
it { is_expected.to validate_presence_of(:package) }
end
describe '#valid_debian_package_type' do
context 'with package type not being Debian' do
before do
publication.package.package_type = 'generic'
end
it 'will not allow package type not being Debian' do
expect(publication).not_to be_valid
expect(publication.errors.to_a).to eq(['Package type must be Debian'])
end
end
context 'with package not being a Debian package' do
before do
publication.package.version = nil
end
it 'will not allow package not being a distribution' do
expect(publication).not_to be_valid
expect(publication.errors.to_a).to eq(['Package must be a Debian package'])
end
end
end
describe '#distribution' do
it { is_expected.to validate_presence_of(:distribution) }
end
end
end
...@@ -14,6 +14,8 @@ RSpec.describe Packages::Package, type: :model do ...@@ -14,6 +14,8 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to have_many(:pipelines).through(:build_infos) } 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(:conan_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:maven_metadatum).inverse_of(:package) } it { is_expected.to have_one(:maven_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:debian_publication).inverse_of(:package).class_name('Packages::Debian::Publication') }
it { is_expected.to have_one(:debian_distribution).through(:debian_publication).source(:distribution).inverse_of(:packages).class_name('Packages::Debian::ProjectDistribution') }
it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) } it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) }
end end
...@@ -374,7 +376,28 @@ RSpec.describe Packages::Package, type: :model do ...@@ -374,7 +376,28 @@ RSpec.describe Packages::Package, type: :model do
end end
end end
Packages::Package.package_types.keys.without('conan').each do |pt| describe "#unique_debian_package_name" do
let!(:package) { create(:debian_package) }
it "will allow a Debian package with same project, name and version, but different distribution" do
new_package = build(:debian_package, project: package.project, name: package.name, version: package.version)
expect(new_package).to be_valid
end
it "will not allow a Debian package with same project, name, version and distribution" do
new_package = build(:debian_package, project: package.project, name: package.name, version: package.version)
new_package.debian_publication.distribution = package.debian_publication.distribution
expect(new_package).not_to be_valid
expect(new_package.errors.to_a).to include('Debian package already exists in Distribution')
end
it "will allow a Debian package with same project, name, version, but no distribution" do
new_package = build(:debian_package, project: package.project, name: package.name, version: package.version, published_in: nil)
expect(new_package).to be_valid
end
end
Packages::Package.package_types.keys.without('conan', 'debian').each do |pt|
context "project id, name, version and package type uniqueness for package type #{pt}" do context "project id, name, version and package type uniqueness for package type #{pt}" do
let(:package) { create("#{pt}_package") } let(:package) { create("#{pt}_package") }
......
...@@ -19,6 +19,11 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze| ...@@ -19,6 +19,11 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
it { is_expected.to have_many(:components).class_name("Packages::Debian::#{container.capitalize}Component").inverse_of(:distribution) } it { is_expected.to have_many(:components).class_name("Packages::Debian::#{container.capitalize}Component").inverse_of(:distribution) }
it { is_expected.to have_many(:architectures).class_name("Packages::Debian::#{container.capitalize}Architecture").inverse_of(:distribution) } it { is_expected.to have_many(:architectures).class_name("Packages::Debian::#{container.capitalize}Architecture").inverse_of(:distribution) }
if container != :group
it { is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution).with_foreign_key(:distribution_id) }
it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
end
end end
describe 'validations' do describe 'validations' do
......
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