NAME
Dist::Conformance::Check::SystemPackages - The system packages that a repository declares in metamerge.yml, validated, and resolved for a host
VERSION
version 0.003
SYNOPSIS
use 5.040;
use Dist::Conformance::Check::SystemPackages;
my $declared = Dist::Conformance::Check::SystemPackages->declared('/bogus/checkout');
my $host = Dist::Conformance::Check::SystemPackages->resolve(
$declared,
packager => 'deb',
distro => 'ubuntu',
release => '24.04',
codename => 'noble',
);
die "No package for @{ $host->{unresolved} }\n" if @{ $host->{unresolved} };
say for @{ $host->{packages} };
DESCRIPTION
CPAN has no field for a package of the operating system, such as ffmpeg or libmagic-dev, that a distribution needs. So a repository declares them in metamerge.yml, the file that [MetaMergeFile] of Dist::Zilla merges into META.json. x_system_packages names each dependency, and says which package gives it for each packager. x_system_sources names the third-party archives that some of the packages come from:
x_system_packages:
FooLib:
deb:
name: libfoo-devel
ubuntu/24.04: libfoo-dev
debian/13: ~
rpm: libfoo-devel
ffmpeg:
deb:
name: ffmpeg
source: jellyfin
test: ffmpeg -version
x_system_sources:
deb:
- name: jellyfin
uri: https://repo.test.test/ubuntu
suites: ['{codename}']
components: [main]
key: https://repo.test.test/key.gpg
fingerprint: 0123456789ABCDEF0123456789ABCDEF01234567
distros: [ubuntu/24.04]
The declaration says what a host must install. This check does not ask what a host has installed. It reads a snapshot, which can be of a repository that is not on this machine, and does not run the tests. Other code installs the packages: it reads them with "declared" and "resolve", or with conformance packages (see conformance).
A dependency
A key of x_system_packages is the name of a dependency: a letter or a digit, then letters, digits, _, ., +, : and -, such as Foo::Lib or ffmpeg. The name is for people and for reports. No packager reads it.
Its value is a mapping. Each key other than test is the name of a packager: a lowercase letter, then lowercase letters and digits, as Provisioner::Packager->named takes it. A dependency has one packager at least. test is optional. It is one command line that must exit 0 once the package is installed, because a package that is installed is not yet a program that runs. It is one line that is not blank.
An entry
The value of a packager is the entry of the dependency for that packager. It is a package name, which applies to every host, or a mapping with these keys:
- name
-
The package on a host that no other key matches.
DISTRO,DISTRO/RELEASE-
The package on that distro, or on that release of it.
DISTROis theIDof /etc/os-release, such asubuntuordebian, andRELEASEis itsVERSION_ID, such as24.04or13. Both are lowercase letters, digits,.,_and-. A null value says that the host needs no package for the dependency. - source
-
Optional. The name of the archive in
x_system_sourcesof the same packager that the package comes from.
The mapping has name, or a DISTRO or DISTRO/RELEASE key, or both. For a host, the most specific key wins: DISTRO/RELEASE, then DISTRO, then name.
What a package name is
A package name must be a plain name, and not an expression that the packager reads in some other way, such as foo=1.2, foo/sid or two names. Each packager has its own rule:
- deb
-
The rule of the Debian policy: lowercase letters, digits,
+,-and., at least two characters, starting with a letter or a digit. The name must not end in-, becauseapt-get install foo-removes foo. - any other packager
-
Letters, digits,
+,.,_and-, starting with a letter or a digit, and not ending in-, until the packager has a rule of its own here.
A source
x_system_sources has a list of sources for each packager. Only deb takes sources. A source of deb is an apt archive, with the keys of a source of Provisioner::Packager::Deb, and two more:
- name
-
The name that an entry gives as
source, and the base name of the files of the source: lowercase letters, digits,.and-, from a letter or a digit. Two sources of a packager do not have the same name. uri,key-
The archive, and the URL of its signing key, as
httporhttps. - suites
-
At least one suite.
{codename}in a suite is replaced by theVERSION_CODENAMEof the host. No other placeholder is allowed. - components
-
A list of components.
- architectures
-
Optional. A list of architectures: lowercase letters and digits.
- pin
-
Optional. A mapping with an integer
priority, andpackages, a glob of package names. - fingerprint
-
Required. The full fingerprint of the signing key, as 40 hex digits. The archive gets the trust of root on the host, and a key URL can serve another key later, so the installer refuses a key with another fingerprint.
- distros
-
Optional. A list of
DISTROandDISTRO/RELEASE, as in "An entry". The source applies only to a host that one of them matches. Default every host.
Each source is the source of an entry of the same packager. A source that no entry names is not valid, because nothing would install from it.
METHODS
declared
($from), a class method. The validated declaration of a repository, as it is written: a hash reference with packages, the mapping of x_system_packages, and sources, the mapping of x_system_sources, such as
{
packages => { ffmpeg => { deb => { name => 'ffmpeg', source => 'jellyfin' }, test => 'ffmpeg -version' } },
sources => { deb => [ { name => 'jellyfin', uri => ..., fingerprint => ... } ] },
}
A repository that declares nothing gives { packages => {}, sources => {} }. "resolve" gives what one host installs.
$from is a Dist::Conformance::Snapshot, such as a Dist::Conformance::Snapshot::Tree of a remote repository, or the directory of a checkout. Only metamerge.yml is read.
Dies when metamerge.yml does not parse, and when the declaration is not valid, as "DESCRIPTION" says. Then the message starts with metamerge.yml is not valid:, and gives each error on a line of its own, as "validate" in Dist::Conformance::Schema does.
resolve
($declared, %host), a class method. What one host installs, from $declared as "declared" returns it. %host has:
- packager
-
Required. The packager of the host, such as
deb. - distro, release
-
The
IDand theVERSION_IDof /etc/os-release. Without them, onlynamematches an entry, and only a source withoutdistrosapplies. - codename
-
The
VERSION_CODENAMEof /etc/os-release, for{codename}in a suite.
Returns a hash reference:
{
needs => [ { name => 'FooLib', package => 'libfoo-dev', source => undef, test => undef }, ... ],
packages => [ 'libfoo-dev', 'ffmpeg' ],
sources => [ { name => 'jellyfin', suites => ['noble'], ... } ],
programs => [ 'ffmpeg -version' ],
unresolved => [ 'BarLib' ],
}
- needs
-
One item for each dependency that resolves on the host, by the sorted name of the dependency, because a YAML mapping has no order.
packageis the package for the host, orundefwhen the most specific key is null, so that the host needs nothing.sourceis the name of its source, andtestthe test of the dependency, orundef. - packages
-
The packages of
needs, in their order, without duplicates. - sources
-
The sources that the packages of
needscome from, in their order, once each, with{codename}filled in and withoutdistros. - programs
-
The tests of
needs, in their order. - unresolved
-
The sorted names of the dependencies that do not resolve on the host: those without an entry for the packager, those whose entry has no key that matches the host, and those whose source is not for the host. A host must not go on without them, so the caller decides what to do, and
resolvedoes not die.
Dies without a packager, and on a source that a package needs and that needs a codename that %host does not give.
check
FUNCTIONS
os_release
($text). The variables of the text of an /etc/os-release, as a hash reference from each name to its value, such as { ID => 'ubuntu', VERSION_ID => '24.04', VERSION_CODENAME => 'noble' }. A value can be bare, in single quotes, or in double quotes with \ escapes, as the shell reads it. Blank lines and comments are skipped.
IN A FLAVOR
checks:
SystemPackages: {}
STATUSES
The check passes when there is no metamerge.yml, when it declares nothing, and when the declaration is valid, because most repositories need no system packages. It fails when metamerge.yml does not parse, or when the declaration is not valid. The update changes nothing: what a repository needs is its own choice.
CONFIGURATION
None, other than "THE KEYS THAT EVERY CHECK TAKES" in Dist::Conformance::Check.
SEE ALSO
Please see those modules/websites for more information related to this module.
BUGS
Please report any bugs or feature requests on the bugtracker website https://github.com/Troglodyne-Internet-Widgets/Dist-Conformance/issues
When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.
AUTHORS
Current Maintainers:
George S. Baugh <george@troglodyne.net>
COPYRIGHT AND LICENSE
Copyright (c) 2026 Troglodyne LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.