env = dict(
ARCHITECTURE=str(context.config.architecture),
BUILDROOT=str(context.root),
- CHROOT_SCRIPT="/work/prepare",
+ SRCDIR="/work/src",
CHROOT_SRCDIR="/work/src",
+ PACKAGEDIR="/work/packages",
+ SCRIPT="/work/prepare",
+ CHROOT_SCRIPT="/work/prepare",
MKOSI_UID=str(INVOKING_USER.uid),
MKOSI_GID=str(INVOKING_USER.gid),
- SCRIPT="/work/prepare",
- SRCDIR="/work/src",
WITH_DOCS=one_zero(context.config.with_docs),
WITH_NETWORK=one_zero(context.config.with_network),
WITH_TESTS=one_zero(context.config.with_tests),
CHROOT_OUTPUTDIR="/work/out",
SRCDIR="/work/src",
CHROOT_SRCDIR="/work/src",
+ PACKAGEDIR="/work/packages",
SCRIPT="/work/build-script",
CHROOT_SCRIPT="/work/build-script",
MKOSI_UID=str(INVOKING_USER.uid),
) + (chroot if script.suffix == ".chroot" else []),
)
+ if any(context.packages.iterdir()):
+ with complete_step("Rebuilding local package repository"):
+ context.config.distribution.createrepo(context)
+
def run_postinst_scripts(context: Context) -> None:
if not context.config.postinst_scripts:
CHROOT_SCRIPT="/work/postinst",
SRCDIR="/work/src",
CHROOT_SRCDIR="/work/src",
+ PACKAGEDIR="/work/packages",
MKOSI_UID=str(INVOKING_USER.uid),
MKOSI_GID=str(INVOKING_USER.gid),
)
CHROOT_OUTPUTDIR="/work/out",
SRCDIR="/work/src",
CHROOT_SRCDIR="/work/src",
+ PACKAGEDIR="/work/packages",
SCRIPT="/work/finalize",
CHROOT_SCRIPT="/work/finalize",
MKOSI_UID=str(INVOKING_USER.uid),
install_tree(context, tree.source, context.pkgmngr, target=tree.target, preserve=False)
+def install_package_directories(context: Context) -> None:
+ if not context.config.package_directories:
+ return
+
+ with complete_step("Copying in extra packages…"):
+ for d in context.config.package_directories:
+ install_tree(context, d, context.packages)
+
+ if any(context.packages.iterdir()):
+ with complete_step("Initializing local package repository…"):
+ context.config.distribution.createrepo(context)
+
+
def install_extra_trees(context: Context) -> None:
if not context.config.extra_trees:
return
"--incremental", str(context.config.incremental),
"--acl", str(context.config.acl),
*(f"--package={package}" for package in context.config.initrd_packages),
+ "--package-directory", str(context.packages),
"--output", f"{context.config.output}-initrd",
*(["--image-id", context.config.image_id] if context.config.image_id else []),
*(["--image-version", context.config.image_version] if context.config.image_version else []),
with setup_workspace(args, config) as workspace:
context = Context(args, config, workspace)
install_package_manager_trees(context)
+ install_package_directories(context)
with mount_base_trees(context):
install_base_trees(context)
packages: list[str]
build_packages: list[str]
+ package_directories: list[Path]
with_recommends: bool
with_docs: bool
parse=config_make_list_parser(delimiter=","),
help="Additional packages needed for build scripts",
),
+ ConfigSetting(
+ dest="package_directories",
+ long="--package-directory",
+ metavar="PATH",
+ section="Content",
+ parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
+ help="Specify a directory containing extra packages",
+ ),
ConfigSetting(
dest="with_recommends",
metavar="BOOL",
self.staging.mkdir()
self.pkgmngr.mkdir()
+ self.packages.mkdir()
self.install_dir.mkdir(exist_ok=True)
self.cache_dir.mkdir(parents=True, exist_ok=True)
def pkgmngr(self) -> Path:
return self.workspace / "pkgmngr"
+ @property
+ def packages(self) -> Path:
+ return self.workspace / "packages"
+
@property
def cache_dir(self) -> Path:
return self.config.cache_dir or (self.workspace / "cache")
def grub_prefix(cls) -> str:
return "grub"
+ @classmethod
+ def createrepo(cls, context: "Context") -> None:
+ raise NotImplementedError
+
class Distribution(StrEnum):
# Please consult docs/distribution-policy.md and contact one
def grub_prefix(self) -> str:
return self.installer().grub_prefix()
+ def createrepo(self, context: "Context") -> None:
+ return self.installer().createrepo(context)
+
def installer(self) -> type[DistributionInstaller]:
modname = str(self).replace('-', '_')
mod = importlib.import_module(f"mkosi.distributions.{modname}")
from mkosi.config import Architecture
from mkosi.context import Context
from mkosi.distributions import Distribution, DistributionInstaller, PackageType
-from mkosi.installer.pacman import PacmanRepository, invoke_pacman, setup_pacman
+from mkosi.installer.pacman import (
+ PacmanRepository,
+ createrepo_pacman,
+ invoke_pacman,
+ setup_pacman,
+)
from mkosi.log import die
def default_tools_tree_distribution(cls) -> Distribution:
return Distribution.arch
+ @classmethod
+ def createrepo(cls, context: "Context") -> None:
+ return createrepo_pacman(context)
+
@classmethod
def setup(cls, context: Context) -> None:
if context.config.local_mirror:
PackageType,
join_mirror,
)
-from mkosi.installer.dnf import invoke_dnf, setup_dnf
+from mkosi.installer.dnf import createrepo_dnf, invoke_dnf, setup_dnf
from mkosi.installer.rpm import RpmRepository, find_rpm_gpgkey
from mkosi.log import complete_step, die
from mkosi.tree import rmtree
def grub_prefix(cls) -> str:
return "grub2"
+ @classmethod
+ def createrepo(cls, context: Context) -> None:
+ return createrepo_dnf(context)
+
@classmethod
def setup(cls, context: Context) -> None:
if GenericVersion(context.config.release) <= 7:
from mkosi.config import Architecture
from mkosi.context import Context
from mkosi.distributions import Distribution, DistributionInstaller, PackageType
-from mkosi.installer.apt import invoke_apt, setup_apt
+from mkosi.installer.apt import createrepo_apt, invoke_apt, setup_apt
from mkosi.log import die
from mkosi.run import run
from mkosi.util import umask
def setup(cls, context: Context) -> None:
setup_apt(context, cls.repositories(context))
+ @classmethod
+ def createrepo(cls, context: "Context") -> None:
+ return createrepo_apt(context)
+
@classmethod
def install(cls, context: Context) -> None:
# Instead of using debootstrap, we replicate its core functionality here. Because dpkg does not have
PackageType,
join_mirror,
)
-from mkosi.installer.dnf import invoke_dnf, setup_dnf
+from mkosi.installer.dnf import createrepo_dnf, invoke_dnf, setup_dnf
from mkosi.installer.rpm import RpmRepository, find_rpm_gpgkey
from mkosi.log import die
def grub_prefix(cls) -> str:
return "grub2"
+ @classmethod
+ def createrepo(cls, context: Context) -> None:
+ return createrepo_dnf(context)
+
@classmethod
def setup(cls, context: Context) -> None:
gpgurls = (
PackageType,
join_mirror,
)
-from mkosi.installer.dnf import invoke_dnf, setup_dnf
+from mkosi.installer.dnf import createrepo_dnf, invoke_dnf, setup_dnf
from mkosi.installer.rpm import RpmRepository, find_rpm_gpgkey
from mkosi.log import die
def default_tools_tree_distribution(cls) -> Distribution:
return Distribution.mageia
+ @classmethod
+ def createrepo(cls, context: "Context") -> None:
+ return createrepo_dnf(context)
+
@classmethod
def setup(cls, context: Context) -> None:
gpgurls = (
PackageType,
join_mirror,
)
-from mkosi.installer.dnf import invoke_dnf, setup_dnf
+from mkosi.installer.dnf import createrepo_dnf, invoke_dnf, setup_dnf
from mkosi.installer.rpm import RpmRepository, find_rpm_gpgkey
from mkosi.log import die
def default_tools_tree_distribution(cls) -> Distribution:
return Distribution.openmandriva
+ @classmethod
+ def createrepo(cls, context: "Context") -> None:
+ return createrepo_dnf(context)
+
@classmethod
def setup(cls, context: Context) -> None:
mirror = context.config.mirror or "http://mirror.openmandriva.org"
from mkosi.config import Architecture
from mkosi.context import Context
from mkosi.distributions import Distribution, DistributionInstaller, PackageType
-from mkosi.installer.dnf import invoke_dnf, setup_dnf
+from mkosi.installer.dnf import createrepo_dnf, invoke_dnf, setup_dnf
from mkosi.installer.rpm import RpmRepository
-from mkosi.installer.zypper import invoke_zypper, setup_zypper
+from mkosi.installer.zypper import createrepo_zypper, invoke_zypper, setup_zypper
from mkosi.log import die
from mkosi.run import find_binary, run
from mkosi.sandbox import finalize_crypto_mounts
def grub_prefix(cls) -> str:
return "grub2"
+ @classmethod
+ def createrepo(cls, context: "Context") -> None:
+ if find_binary("zypper", root=context.config.tools()):
+ createrepo_zypper(context)
+ else:
+ createrepo_dnf(context)
+
@classmethod
def setup(cls, context: Context) -> None:
release = context.config.release
*(["--ro-bind", m, m] if (m := context.config.local_mirror) else []),
*(["--ro-bind", os.fspath(p), os.fspath(p)] if (p := context.workspace / "apt.conf").exists() else []),
*finalize_crypto_mounts(tools=context.config.tools()),
+ "--bind", context.packages, "/work/packages",
]
mounts += flatten(
),
env=context.config.environment,
)
+
+
+def createrepo_apt(context: Context) -> None:
+ with (context.packages / "Packages").open("w") as f:
+ run(["dpkg-scanpackages", context.packages],
+ stdout=f, sandbox=context.sandbox(options=["--ro-bind", context.packages, context.packages]))
+
+ (context.pkgmngr / "etc/apt/sources.list.d").mkdir(parents=True, exist_ok=True)
+ (context.pkgmngr / "etc/apt/sources.list.d/mkosi-packages.sources").write_text(
+ f"""\
+ Enabled: yes
+ Types: deb
+ URIs: file:///work/packages
+ Suites: {context.config.release}
+ Components: main
+ Trusted: yes
+ """
+ )
for p in (context.root / "var/log").iterdir():
if any(p.name.startswith(prefix) for prefix in ("dnf", "hawkey", "yum")):
p.unlink()
+
+
+def createrepo_dnf(context: Context) -> None:
+ run(["createrepo_c", context.packages],
+ sandbox=context.sandbox(options=["--bind", context.packages, context.packages]))
+
+ (context.pkgmngr / "etc/yum.repos.d").mkdir(parents=True, exist_ok=True)
+ (context.pkgmngr / "etc/yum.repos.d/mkosi-packages.repo").write_text(
+ textwrap.dedent(
+ """\
+ [mkosi-packages]
+ name=mkosi-packages
+ gpgcheck=0
+ enabled=1
+ baseurl=file:///work/packages
+ metadata_expire=0
+ priority=50
+ """
+ )
+ )
),
env=context.config.environment,
)
+
+
+def createrepo_pacman(context: Context) -> None:
+ run(["repo-add", context.packages / "mkosi-packages.db.tar", *context.packages.glob("*.pkg.tar*")])
+
+ with (context.pkgmngr / "etc/pacman.conf").open("a") as f:
+ f.write(
+ textwrap.dedent(
+ """\
+ [mkosi-packages]
+ Server = file:///work/packages
+ """
+ )
+ )
)
fixup_rpmdb_location(context)
+
+
+def createrepo_zypper(context: Context) -> None:
+ run(["createrepo_c", context.packages],
+ sandbox=context.sandbox(options=["--bind", context.packages, context.packages]))
+
+ (context.pkgmngr / "etc/zypp/repos.d").mkdir(parents=True, exist_ok=True)
+ (context.pkgmngr / "etc/zypp/repos.d/mkosi-packages.repo").write_text(
+ textwrap.dedent(
+ """\
+ [mkosi-packages]
+ name=mkosi-packages
+ gpgcheck=0
+ enabled=1
+ baseurl=file:///work/packages
+ autorefresh=0
+ priority=50
+ """
+ )
+ )
btrfs-progs
curl
debian-archive-keyring
+ dpkg
edk2-ovmf
erofs-utils
grub
[Content]
Packages=
apt
+ createrepo_c
curl-minimal
debian-keyring
distribution-gpg-keys
dnf-plugins-core
+ dpkg-dev
grub2-tools
openssh-clients
policycoreutils
apt
archlinux-keyring
btrfs-progs
+ createrepo-c
curl
debian-archive-keyring
+ dpkg-dev
erofs-utils
grub2
libtss2-dev
Packages=
btrfs-progs
ca-certificates-mozilla
+ createrepo_c
curl
distribution-gpg-keys
dnf-plugins-core
`mkosi.build` scripts require to operate. Note that packages listed
here will be absent in the final image.
+`PackageDirectories=`, `--package-directory=`
+
+: Specify directories containing extra packages to be made available during
+ the build. `mkosi` will create a local repository containing all
+ packages in these directories and make it available when installing packages or
+ running scripts.
+
+: Note that this local repository is also made available when running
+ scripts. Build scripts can add more packages to the local repository
+ by placing the built packages in `$PACKAGEDIR`.
+
`WithRecommends=`, `--with-recommends=`
: Configures whether to install recommended or weak dependencies,
artifacts generated during the build. `$CHROOT_OUTPUTDIR` contains the
value that `$OUTPUTDIR` will have after invoking `mkosi-chroot`.
+* `$PACKAGEDIR` points to the directory containing the local package
+ repository. Build scripts can add more packages to the local
+ repository by writing the packages to `$PACKAGEDIR`.
+
* `$BUILDROOT` is the root directory of the image being built,
optionally with the build overlay mounted on top depending on the
script that's being executed.
"Output": "outfile",
"OutputDirectory": "/your/output/here",
"Overlay": true,
+ "PackageDirectories": [],
"PackageManagerTrees": [
{
"source": "/foo/bar",
output_dir = Path("/your/output/here"),
output_format = OutputFormat.uki,
overlay = True,
+ package_directories = [],
package_manager_trees = [ConfigTree(Path("/foo/bar"), None)],
packages = [],
passphrase = None,