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

Debian Publications

parent 7a75ec47
......@@ -5,5 +5,8 @@ class Packages::Debian::ProjectDistribution < ApplicationRecord
:project
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
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
has_one :composer_metadatum, inverse_of: :package, class_name: 'Packages::Composer::Metadatum'
has_many :build_infos, inverse_of: :package
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 :debian_publication
accepts_nested_attributes_for :maven_metadatum
delegate :recipe, :recipe_path, to: :conan_metadatum, prefix: :conan
delegate :codename, :suite, to: :debian_distribution, prefix: :debian_distribution
validates :project, presence: true
validates :name, presence: true
......@@ -31,7 +35,8 @@ class Packages::Package < ApplicationRecord
validates :name, format: { with: Gitlab::Regex.package_name_regex }, unless: -> { conan? || generic? || debian? }
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_npm_package_name, if: :npm?
......@@ -251,6 +256,18 @@ class Packages::Package < ApplicationRecord
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
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
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 (
id bigint NOT NULL,
name character varying(255) NOT NULL,
......@@ -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_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_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
ALTER TABLE ONLY packages_debian_project_distributions
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
ADD CONSTRAINT packages_dependencies_pkey PRIMARY KEY (id);
......@@ -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_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 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
ALTER TABLE ONLY security_scans
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
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
ALTER TABLE ONLY pages_domain_acme_orders
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
ADD CONSTRAINT fk_rails_76c4e9732d FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
......
......@@ -9290,6 +9290,9 @@ msgstr ""
msgid "Dear Administrator,"
msgstr ""
msgid "Debian package already exists in Distribution"
msgstr ""
msgid "Debug"
msgstr ""
......@@ -34940,6 +34943,9 @@ msgstr ""
msgid "mrWidget|to start a merge train when the pipeline succeeds"
msgstr ""
msgid "must be a Debian package"
msgstr ""
msgid "must be a boolean value"
msgstr ""
......@@ -35316,6 +35322,9 @@ msgstr ""
msgid "two-factor authentication settings"
msgstr ""
msgid "type must be Debian"
msgstr ""
msgid "unicode domains should use IDNA encoding"
msgstr ""
......
......@@ -29,6 +29,15 @@ FactoryBot.define do
transient do
without_package_files { false }
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
after :create do |package, evaluator|
......@@ -50,6 +59,7 @@ FactoryBot.define do
transient do
without_package_files { false }
file_metadatum_trait { :unknown }
published_in { nil }
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
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(: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) }
end
......@@ -374,7 +376,28 @@ RSpec.describe Packages::Package, type: :model do
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
let(:package) { create("#{pt}_package") }
......
......@@ -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(: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
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