diff --git a/app/models/ci/catalog/resource.rb b/app/models/ci/catalog/resource.rb index 38603ddfe59a4d5e0effce10d0d54c6177f3a8be..442851f577780a1c7909d2c7bfb78baa49672797 100644 --- a/app/models/ci/catalog/resource.rb +++ b/app/models/ci/catalog/resource.rb @@ -11,6 +11,7 @@ class Resource < ::ApplicationRecord self.table_name = 'catalog_resources' belongs_to :project + has_many :versions, class_name: 'Ci::Catalog::Resources::Version', inverse_of: :catalog_resource scope :for_projects, ->(project_ids) { where(project_id: project_ids) } scope :order_by_created_at_desc, -> { reorder(created_at: :desc) } diff --git a/app/models/ci/catalog/resources/version.rb b/app/models/ci/catalog/resources/version.rb new file mode 100644 index 0000000000000000000000000000000000000000..bef1b6e64cb499a40a3cf08e5570a0c4fb94db51 --- /dev/null +++ b/app/models/ci/catalog/resources/version.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Ci + module Catalog + module Resources + # This class represents a CI/CD Catalog resource version. + # Only versions which contain valid CI components are included in this table. + class Version < ::ApplicationRecord + self.table_name = 'catalog_resource_versions' + + belongs_to :release, inverse_of: :catalog_resource_version + belongs_to :catalog_resource, class_name: 'Ci::Catalog::Resource', inverse_of: :versions + belongs_to :project, inverse_of: :catalog_resource_versions + + validates :release, :catalog_resource, :project, presence: true + end + end + end +end + +Ci::Catalog::Resources::Version.prepend_mod_with('Ci::Catalog::Resources::Version') diff --git a/app/models/project.rb b/app/models/project.rb index cd107fb8c35ff2a4a755dc8c26ba027623d482c0..f8c38c1587d062c81785b51488dce8b556712b6a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -170,6 +170,8 @@ class Project < ApplicationRecord alias_attribute :parent_id, :namespace_id has_one :catalog_resource, class_name: 'Ci::Catalog::Resource', inverse_of: :project + has_many :catalog_resource_versions, class_name: 'Ci::Catalog::Resources::Version', inverse_of: :project + has_one :last_event, -> { order 'events.created_at DESC' }, class_name: 'Event' has_many :boards diff --git a/app/models/release.rb b/app/models/release.rb index f0ba56390ab7550b6ae517cad934531303c4eae9..6830f6e8480407fe3e8ae4064e003511ac7ebbcd 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -20,6 +20,8 @@ class Release < ApplicationRecord has_many :milestones, through: :milestone_releases has_many :evidences, inverse_of: :release, class_name: 'Releases::Evidence' + has_one :catalog_resource_version, class_name: 'Ci::Catalog::Resources::Version', inverse_of: :release + accepts_nested_attributes_for :links, allow_destroy: true before_create :set_released_at diff --git a/db/docs/catalog_resource_versions.yml b/db/docs/catalog_resource_versions.yml new file mode 100644 index 0000000000000000000000000000000000000000..f01dcd8a2d66372e1dd57ae99200232e45ab054b --- /dev/null +++ b/db/docs/catalog_resource_versions.yml @@ -0,0 +1,8 @@ +--- +table_name: catalog_resource_versions +feature_categories: +- pipeline_composition +description: Catalog resource versions that contain valid CI components. +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124668 +milestone: '16.2' +gitlab_schema: gitlab_main diff --git a/db/migrate/20230626211305_create_catalog_resource_versions.rb b/db/migrate/20230626211305_create_catalog_resource_versions.rb new file mode 100644 index 0000000000000000000000000000000000000000..9a9aa615ea54a5046a2769eab2ad3aef91754916 --- /dev/null +++ b/db/migrate/20230626211305_create_catalog_resource_versions.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreateCatalogResourceVersions < Gitlab::Database::Migration[2.1] + def change + create_table :catalog_resource_versions do |t| + t.bigint :release_id, null: false, index: { unique: true } + t.bigint :catalog_resource_id, null: false, index: true + t.bigint :project_id, null: false, index: true + + t.datetime_with_timezone :created_at, null: false + end + end +end diff --git a/db/migrate/20230626215602_add_release_fk_to_catalog_resource_versions.rb b/db/migrate/20230626215602_add_release_fk_to_catalog_resource_versions.rb new file mode 100644 index 0000000000000000000000000000000000000000..43dda42f09ef91264f6f1853f4c35c7ab1fe4d04 --- /dev/null +++ b/db/migrate/20230626215602_add_release_fk_to_catalog_resource_versions.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddReleaseFkToCatalogResourceVersions < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :catalog_resource_versions, :releases, column: :release_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :catalog_resource_versions, column: :release_id + end + end +end diff --git a/db/migrate/20230626215614_add_project_fk_to_catalog_resource_versions.rb b/db/migrate/20230626215614_add_project_fk_to_catalog_resource_versions.rb new file mode 100644 index 0000000000000000000000000000000000000000..115b13700ad5ebe047e409cb2b777c4536451da5 --- /dev/null +++ b/db/migrate/20230626215614_add_project_fk_to_catalog_resource_versions.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddProjectFkToCatalogResourceVersions < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :catalog_resource_versions, :projects, column: :project_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :catalog_resource_versions, column: :project_id + end + end +end diff --git a/db/migrate/20230626215638_add_catalog_resource_fk_to_catalog_resource_versions.rb b/db/migrate/20230626215638_add_catalog_resource_fk_to_catalog_resource_versions.rb new file mode 100644 index 0000000000000000000000000000000000000000..844fb96cf870b70f37d9400172a77a0948d8f67c --- /dev/null +++ b/db/migrate/20230626215638_add_catalog_resource_fk_to_catalog_resource_versions.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddCatalogResourceFkToCatalogResourceVersions < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :catalog_resource_versions, :catalog_resources, + column: :catalog_resource_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :catalog_resource_versions, column: :catalog_resource_id + end + end +end diff --git a/db/schema_migrations/20230626211305 b/db/schema_migrations/20230626211305 new file mode 100644 index 0000000000000000000000000000000000000000..bb9cf36e48dffd8d9e0e0974d20135c94ef0067b --- /dev/null +++ b/db/schema_migrations/20230626211305 @@ -0,0 +1 @@ +f4e628f28e4d2ad11bcbee48aed5146a0dfdf2745911db3f63de2dca455e69a5 \ No newline at end of file diff --git a/db/schema_migrations/20230626215602 b/db/schema_migrations/20230626215602 new file mode 100644 index 0000000000000000000000000000000000000000..fb64b8c7f66ff3630fe9aef71a83073e43792646 --- /dev/null +++ b/db/schema_migrations/20230626215602 @@ -0,0 +1 @@ +6a4ecb8e9f8855cb44b425e16cc89c9146fd4709b5daf322747c3034f11c4cf2 \ No newline at end of file diff --git a/db/schema_migrations/20230626215614 b/db/schema_migrations/20230626215614 new file mode 100644 index 0000000000000000000000000000000000000000..479153c6451ed409450bdc3e01fd3aed11716907 --- /dev/null +++ b/db/schema_migrations/20230626215614 @@ -0,0 +1 @@ +5c8dbf1b5ad9d41014330eeb07a7daed135a0e9a579d2c15c1f9f0cba83f0bcd \ No newline at end of file diff --git a/db/schema_migrations/20230626215638 b/db/schema_migrations/20230626215638 new file mode 100644 index 0000000000000000000000000000000000000000..b93870e7d7e7b54454185d6c677158e1c31c0b52 --- /dev/null +++ b/db/schema_migrations/20230626215638 @@ -0,0 +1 @@ +e72aae56b010d9c04fb6a8cc799ae39a817eb4a755f162d2a1157d6d5a8a3131 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index b69f0da4a549c61dd4ea57f7a362ada21c9383d7..7da785a45d772429ff20949ef68e25d4fff19e3a 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -12934,6 +12934,23 @@ CREATE SEQUENCE bulk_imports_id_seq ALTER SEQUENCE bulk_imports_id_seq OWNED BY bulk_imports.id; +CREATE TABLE catalog_resource_versions ( + id bigint NOT NULL, + release_id bigint NOT NULL, + catalog_resource_id bigint NOT NULL, + project_id bigint NOT NULL, + created_at timestamp with time zone NOT NULL +); + +CREATE SEQUENCE catalog_resource_versions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE catalog_resource_versions_id_seq OWNED BY catalog_resource_versions.id; + CREATE TABLE catalog_resources ( id bigint NOT NULL, project_id bigint NOT NULL, @@ -25137,6 +25154,8 @@ ALTER TABLE ONLY bulk_import_trackers ALTER COLUMN id SET DEFAULT nextval('bulk_ ALTER TABLE ONLY bulk_imports ALTER COLUMN id SET DEFAULT nextval('bulk_imports_id_seq'::regclass); +ALTER TABLE ONLY catalog_resource_versions ALTER COLUMN id SET DEFAULT nextval('catalog_resource_versions_id_seq'::regclass); + ALTER TABLE ONLY catalog_resources ALTER COLUMN id SET DEFAULT nextval('catalog_resources_id_seq'::regclass); ALTER TABLE ONLY chat_names ALTER COLUMN id SET DEFAULT nextval('chat_names_id_seq'::regclass); @@ -26963,6 +26982,9 @@ ALTER TABLE ONLY bulk_import_trackers ALTER TABLE ONLY bulk_imports ADD CONSTRAINT bulk_imports_pkey PRIMARY KEY (id); +ALTER TABLE ONLY catalog_resource_versions + ADD CONSTRAINT catalog_resource_versions_pkey PRIMARY KEY (id); + ALTER TABLE ONLY catalog_resources ADD CONSTRAINT catalog_resources_pkey PRIMARY KEY (id); @@ -30392,6 +30414,12 @@ CREATE INDEX index_bulk_import_failures_on_correlation_id_value ON bulk_import_f CREATE INDEX index_bulk_imports_on_user_id ON bulk_imports USING btree (user_id); +CREATE INDEX index_catalog_resource_versions_on_catalog_resource_id ON catalog_resource_versions USING btree (catalog_resource_id); + +CREATE INDEX index_catalog_resource_versions_on_project_id ON catalog_resource_versions USING btree (project_id); + +CREATE UNIQUE INDEX index_catalog_resource_versions_on_release_id ON catalog_resource_versions USING btree (release_id); + CREATE UNIQUE INDEX index_catalog_resources_on_project_id ON catalog_resources USING btree (project_id); CREATE INDEX index_chat_names_on_team_id_and_chat_id ON chat_names USING btree (team_id, chat_id); @@ -35410,6 +35438,9 @@ ALTER TABLE ONLY vulnerabilities ALTER TABLE ONLY vulnerabilities ADD CONSTRAINT fk_131d289c65 FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE SET NULL; +ALTER TABLE ONLY catalog_resource_versions + ADD CONSTRAINT fk_15376d917e FOREIGN KEY (release_id) REFERENCES releases(id) ON DELETE CASCADE; + ALTER TABLE ONLY sbom_occurrences ADD CONSTRAINT fk_157506c0e2 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; @@ -35815,6 +35846,9 @@ ALTER TABLE ONLY scan_result_policies ALTER TABLE ONLY vulnerabilities ADD CONSTRAINT fk_7ac31eacb9 FOREIGN KEY (updated_by_id) REFERENCES users(id) ON DELETE SET NULL; +ALTER TABLE ONLY catalog_resource_versions + ADD CONSTRAINT fk_7ad8849db4 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY issue_customer_relations_contacts ADD CONSTRAINT fk_7b92f835bb FOREIGN KEY (contact_id) REFERENCES customer_relations_contacts(id) ON DELETE CASCADE; @@ -36052,6 +36086,9 @@ ALTER TABLE ONLY issues ALTER TABLE ONLY protected_tag_create_access_levels ADD CONSTRAINT fk_b4eb82fe3c FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; +ALTER TABLE ONLY catalog_resource_versions + ADD CONSTRAINT fk_b670eae96b FOREIGN KEY (catalog_resource_id) REFERENCES catalog_resources(id) ON DELETE CASCADE; + ALTER TABLE ONLY bulk_import_entities ADD CONSTRAINT fk_b69fa2b2df FOREIGN KEY (bulk_import_id) REFERENCES bulk_imports(id) ON DELETE CASCADE; diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 981802ad09d2fcbf05d53d5da9d3fb77f5d56b6a..7dc969f274a9111abe4fd8e86f62aa4e2028993b 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -162,6 +162,7 @@ releases: - milestone_releases - milestones - evidences +- catalog_resource_version links: - release project_members: @@ -516,6 +517,7 @@ container_repositories: - name project: - catalog_resource +- catalog_resource_versions - external_status_checks - base_tags - project_topics @@ -1039,6 +1041,11 @@ iterations_cadence: - iterations catalog_resource: - project + - catalog_resource_versions +catalog_resource_versions: + - project + - release + - catalog_resource approval_rules: - users - groups diff --git a/spec/models/ci/catalog/resource_spec.rb b/spec/models/ci/catalog/resource_spec.rb index 45d49d65b024268e6900f4c5838290808c24f91d..7af3369ae7b61f0fd3b2240b1b5b0dd8f1b43ce3 100644 --- a/spec/models/ci/catalog/resource_spec.rb +++ b/spec/models/ci/catalog/resource_spec.rb @@ -15,6 +15,7 @@ let_it_be(:release3) { create(:release, project: project, released_at: Time.zone.now) } it { is_expected.to belong_to(:project) } + it { is_expected.to have_many(:versions).class_name('Ci::Catalog::Resources::Version') } it { is_expected.to delegate_method(:avatar_path).to(:project) } it { is_expected.to delegate_method(:description).to(:project) } diff --git a/spec/models/ci/catalog/resources/version_spec.rb b/spec/models/ci/catalog/resources/version_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f6abb112820d8113ee24e8a71a0d4176ac279762 --- /dev/null +++ b/spec/models/ci/catalog/resources/version_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::Catalog::Resources::Version, type: :model, feature_category: :pipeline_composition do + it { is_expected.to belong_to(:release) } + it { is_expected.to belong_to(:catalog_resource).class_name('Ci::Catalog::Resource') } + it { is_expected.to belong_to(:project) } + + describe 'validations' do + it { is_expected.to validate_presence_of(:release) } + it { is_expected.to validate_presence_of(:catalog_resource) } + it { is_expected.to validate_presence_of(:project) } + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 1a7a0688abe98a8a23acd6454c7cf68ae5f4d938..302fe4098b27b76c2f9bcd8594da037a9e82a133 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -46,6 +46,7 @@ it { is_expected.to have_one(:design_management_repository).class_name('DesignManagement::Repository').inverse_of(:project) } it { is_expected.to have_one(:slack_integration) } it { is_expected.to have_one(:catalog_resource) } + it { is_expected.to have_many(:catalog_resource_versions).class_name('Ci::Catalog::Resources::Version') } it { is_expected.to have_one(:microsoft_teams_integration) } it { is_expected.to have_one(:mattermost_integration) } it { is_expected.to have_one(:hangouts_chat_integration) } diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 446ef4180d25e03cd5f1ad7a302b4aae33b10105..164cef95cb6cdc2fd3ea6d9c2f5ef33db26ecf9c 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -17,6 +17,7 @@ it { is_expected.to have_many(:milestones) } it { is_expected.to have_many(:milestone_releases) } it { is_expected.to have_many(:evidences).class_name('Releases::Evidence') } + it { is_expected.to have_one(:catalog_resource_version).class_name('Ci::Catalog::Resources::Version') } end describe 'validation' do