Skip to content

Distribution Support

Rubhan edited this page Aug 27, 2026 · 9 revisions

The whole feature sits behind the :enhanced_distribution_support feature toggle.

Architectural overview

Distribution Support architecture


Components

The organisation that publishes distributions — openSUSE, SUSE, Debian.

A Vendor belongs to exactly one Project, and a Project has at most one Vendor (has_one :vendor, dependent: :destroy). That project is the vendor's home in OBS and the source of all authorization for the subtree beneath it. The one-to-one is enforced by a UNIQUE index on vendors.project_id.

Fields: name, description, url.

belongs_to :project, optional: false
has_many :distros, dependent: :destroy

A product line published by a vendor — Leap, Tumbleweed, SLES, Debian.

Names are unique per vendor, not globally, so two vendors may both ship something called "Micro". Enforced both in the model and by a composite UNIQUE index on (vendor_id, name).

Fields: name, description, url.

belongs_to :vendor, optional: false
has_many :distro_releases, dependent: :destroy
has_one :project, through: :vendor

A specific shipped version of a distro — Leap 15.6, SLES 15 SP6, Debian 13.

Names are unique per distro, via (distro_id, name). Fields: name, description, url.

belongs_to :distro, optional: false
has_many :distro_release_repository_architectures, dependent: :destroy
has_many :repository_architectures, through: :distro_release_repository_architectures
has_many :repositories, through: :repository_architectures
has_many :projects, through: :repositories

The join model that says what a release is actually made of. A release links to many RepositoryArchitectures, and one RepositoryArchitecture may serve many releases. A UNIQUE index on (distro_release_id, repository_architecture_id) keeps the pairing from being recorded twice.


Some design decisions

Why not the existing Distribution model?

OBS already has a Distribution model, and the new hierarchy deliberately does not reuse or extend it. The two solve different problems:

Distribution (legacy) Distribution Support
Shape Flat — vendor is a string column on each row Three linked models
Ownership Admin-only (DistributionsController restricts create/update/destroy) Project maintainers, via the policy chain
Purpose Populates the "Add repository" wizard Describes what a vendor ships, and what it is built from

Note the naming: the new model is Distro, not Distribution, precisely so the two can coexist. There is no namespace involved; the class names are distinct.


Why RepositoryArchitecture and not Project or Repository?

This is easy to get wrong in either direction.

Not projects. The obvious framing is "a release is a set of projects". This cannot express reality, because projects are shared across releases. openSUSE:Maintenance feeds several Leap versions simultaneously. Given only a project, there is no way to say which release a given piece of content belongs to.

Not repositories alone. Repository is still a set of architectures, and a release does not necessarily ship all of them. A repository may build x86_64, aarch64 and i586 while the release in question only ships the first two.

RepositoryArchitecture is the finest grain that is still a real, existing OBS object — it is the row that a build actually targets. Anchoring there means the composition of a release is exact, and the coarser views come for free by traversal.

Gotcha: the derived associations return duplicates

repositories and projects on DistroRelease are conveniences reached through the join. If a release links three architectures of the same repository, that repository appears three times:

release.repositories.count          # => 3, for one repository
release.repositories.distinct.count # => 1

The same applies to projects, compounded — a project with two repositories at three arches each yields six rows. Always add .distinct when you want the set rather than the multiset, and treat both associations as read-only views; writes belong on repository_architectures.


Data integrity

Constraints are declared at both layers — model validation for usable error messages, database index for the actual guarantee.

Table Unique index Foreign keys
vendors (project_id) projects
distros (vendor_id, name) vendors
distro_releases (distro_id, name) distros
distro_release_repository_architectures (distro_release_id, repository_architecture_id) distro_releases, repository_architectures

Deletion cascades through dependent: :destroy at every level, so destroying a vendor removes its distros, their releases, and the join rows but never touches the RepositoryArchitecture records themselves, which belong to the repositories.

Clone this wiki locally