Additional repositories can now be configured for Debian and Ubuntu.
Because APT can only be configured with a single path for
`Dir::Etc::sourceparts`, the content of the directories listed in
`repo_dirs` are copied in a `apt/sources.list.d` directory managed by
mkosi outside the image root. Therefore, only one file will be kept if
several files have the same name.
Like the main `sources.list` generated by mkosi, the additional
repositories are copied in the image if APT is installed.
`RepositoryDirectories`, `--repo-dir=`
-: This option can (for now) only be used with RPM-based distributions and Arch
- Linux. It takes a comma separated list of directories containing extra repository
- definitions that will be used when installing packages. The files are passed
- directly to the corresponding package manager and should be written in the format
- expected by the package manager of the image's distro.
+: This option can (for now) only be used with RPM-based distributions,
+ Debian-based distributions and Arch Linux. It takes a comma separated list of
+ directories containing extra repository definitions that will be used when
+ installing packages. The files are passed directly to the corresponding
+ package manager and should be written in the format expected by the package
+ manager of the image's distro.
`Architecture=`, `--architecture=`
current_user,
detect_distribution,
flatten,
+ is_apt_distribution,
is_dnf_distribution,
prepend_to_environ_path,
qemu_check_kvm_support,
if args.compress_output != Compression.none:
die(f"Sorry, can't {opname} with a compressed image.")
- if args.repo_dirs and not (is_dnf_distribution(args.distribution) or args.distribution == Distribution.arch):
- die("--repo-dir is only supported on DNF based distributions and Arch")
+ if args.repo_dirs and not (
+ is_dnf_distribution(args.distribution)
+ or is_apt_distribution(args.distribution)
+ or args.distribution == Distribution.arch
+ ):
+ die("--repo-dir is only supported on DNF/Debian based distributions and Arch")
if args.qemu_kvm is True and not qemu_check_kvm_support():
die("Sorry, the host machine does not support KVM acceleration.")
if args.qemu_kvm is None:
args.qemu_kvm = qemu_check_kvm_support()
- if args.repositories and not is_dnf_distribution(args.distribution) and args.distribution not in (Distribution.debian, Distribution.ubuntu):
+ if args.repositories and not (is_dnf_distribution(args.distribution) or is_apt_distribution(args.distribution)):
die("Sorry, the --repositories option is only supported on DNF/Debian based distributions")
if args.initrds:
die("This unprivileged build configuration requires at least Linux v5.11")
return MkosiConfig(**vars(args))
-
setup_apt(state, cls.repositories(state))
invoke_apt(state, "update", apivfs=False)
invoke_apt(state, "install", packages, apivfs=apivfs)
+ install_apt_sources(state, cls.repositories(state, local=False))
policyrcd.unlink()
- sources = state.root / "etc/apt/sources.list"
- if not sources.exists() and state.root.joinpath("usr/bin/apt").exists():
- with sources.open("w") as f:
- for repo in cls.repositories(state, local=False):
- f.write(f"{repo}\n")
-
@classmethod
def remove_packages(cls, state: MkosiState, packages: Sequence[str]) -> None:
invoke_apt(state, "purge", packages)
state.workspace.joinpath("apt").mkdir(exist_ok=True)
state.workspace.joinpath("apt/apt.conf.d").mkdir(exist_ok=True)
state.workspace.joinpath("apt/preferences.d").mkdir(exist_ok=True)
+ state.workspace.joinpath("apt/sources.list.d").mkdir(exist_ok=True)
state.workspace.joinpath("apt/log").mkdir(exist_ok=True)
# TODO: Drop once apt 2.5.4 is widely available.
for repo in repos:
f.write(f"{repo}\n")
+ for repo_dir in state.config.repo_dirs:
+ for src in repo_dir.iterdir():
+ if not src.is_file():
+ continue
+ if src.suffix in (".list", ".sources"):
+ shutil.copyfile(src, state.workspace.joinpath("apt/sources.list.d", src.name))
+
def invoke_apt(
state: MkosiState,
)
return bwrap(["apt-get", operation, *extra], apivfs=state.root if apivfs else None, env=env | state.environment)
+
+
+def install_apt_sources(state: MkosiState, repos: Sequence[str]) -> None:
+ if not state.root.joinpath("usr/bin/apt").exists():
+ return
+
+ sources = state.root / "etc/apt/sources.list"
+ if not sources.exists():
+ with sources.open("w") as f:
+ for repo in repos:
+ f.write(f"{repo}\n")
+
+ # Already contains a merged tree of repo_dirs after setup_apt
+ for src in state.workspace.joinpath("apt/sources.list.d").iterdir():
+ dst = state.root.joinpath("etc/apt/sources.list.d", src.name)
+ if dst.exists():
+ continue
+ shutil.copyfile(src, dst)
os.close(fd)
-def copy_path(src: Path, dst: Path, preserve_owner: bool = True) -> None:
+def copy_path(
+ src: Path,
+ dst: Path,
+ *,
+ dereference: bool = False,
+ preserve_owner: bool = True,
+) -> None:
run([
"cp",
"--recursive",
- "--no-dereference",
+ f"--{'' if dereference else 'no-'}dereference",
f"--preserve=mode,timestamps,links,xattr{',ownership' if preserve_owner else ''}",
"--no-target-directory",
"--reflink=auto",
)
+def is_apt_distribution(d: Distribution) -> bool:
+ return d in (Distribution.debian, Distribution.ubuntu)
+
+
class OutputFormat(str, enum.Enum):
directory = "directory"
subvolume = "subvolume"