diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..69dc4014 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2024 Lance Vick +# +# SPDX-License-Identifier: GPL-3.0-or-later + + +!target diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..01e8ad3e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,57 @@ +# SPDX-FileCopyrightText: 2024 Lance Vick +# SPDX-FileCopyrightText: 2025 Kevin Nause +# +# SPDX-License-Identifier: GPL-3.0-or-later + +ARG ARCH=x86 +ARG USER=user +ARG UID=1000 +ARG GID=1000 +ARG HOME=/home/${USER} + +FROM scratch AS build +ARG TARGET +ARG INIT +COPY ${TARGET} / + +FROM build AS build-aarch64 +RUN ["/bootstrap-seeds/POSIX/AArch64/kaem-optional-seed"] + +FROM build AS build-amd64 +RUN ["/bootstrap-seeds/POSIX/AMD64/kaem-optional-seed"] + +FROM build AS build-riscv32 +RUN ["/bootstrap-seeds/POSIX/riscv32/kaem-optional-seed"] + +FROM build AS build-riscv64 +RUN ["/bootstrap-seeds/POSIX/riscv64/kaem-optional-seed"] + +FROM build AS build-x86 +RUN ["/bootstrap-seeds/POSIX/x86/kaem-optional-seed"] + +FROM build-${ARCH} AS install +ARG USER +ARG UID +ARG GID +ARG HOME +ENV PATH=/bin:/usr/sbin:/usr/bin +RUN set -eu; \ + rm -rf /usr/lib/python*/__pycache__; \ + mkdir -p /rootfs/etc /rootfs/${HOME}; \ + cp -R $(ls -d /etc/* | grep -v '\(resolv.conf\|hosts\)') /rootfs/etc/; \ + cp -R lib usr bin var /rootfs/; \ + echo "${USER}:x:${GID}:" > /rootfs/etc/group; \ + echo "${USER}:x:${UID}:${GID}::${HOME}:/bin/bash" > /rootfs/etc/passwd; \ + find /rootfs -exec touch -hcd "@0" "{}" + + +FROM scratch AS package +ARG UID +ARG GID +COPY --from=install /rootfs / +USER ${UID}:${GID} +ENTRYPOINT ["/bin/bash"] +ENV TZ=UTC +ENV LANG=C.UTF-8 +ENV SOURCE_DATE_EPOCH=1 +ENV KCONFIG_NOTIMESTAMP=1 +ENV PS1="bootstrap$ " diff --git a/README.rst b/README.rst index 154c860c..4b5b7de8 100644 --- a/README.rst +++ b/README.rst @@ -33,8 +33,8 @@ Without using Python: passing it to ``rootfs.py```). 1. ``git clone https://github.com/fosslinux/live-bootstrap`` 2. ``git submodule update --init --recursive`` -3. Consider whether you are going to run this in a chroot, in QEMU, or on bare - metal. (All of this *can* be automated, but not in a trustable way. See +3. Consider whether you are going to run this in a chroot, in QEMU, on bare + metal, or docker. (All of this *can* be automated, but not in a trustable way. See further below.) a. **chroot:** Create a directory where the chroot will reside, run @@ -73,6 +73,15 @@ Without using Python: (``-nic user,model=e1000``), and ``-machine kernel-irqchip=split``. c. **Bare metal:** Follow the same steps as QEMU, but the disks need to be two different *physical* disks, and boot from the first disk. + d. **Docker:** Follow the same steps as chroot. To debug build errors, see + [docker buildx debug](https://docs.docker.com/reference/cli/docker/buildx/debug/). + + ```bash + DOCKER_BUILDKIT=1 BUILDX_EXPERIMENTAL=1 \ + docker buildx debug --invoke /bin/sh build \ + --build-arg=ARCH=x86 --build-arg=TARGET=target/ --build-arg=SOURCE_DATE_EPOCH=1 \ + --progress=auto --platform=linux/amd64 --target=install --tag=live-bootstrap-debug . + ``` Mirrors ------- diff --git a/rootfs.py b/rootfs.py index 7cbb4bc9..24b8c9fd 100755 --- a/rootfs.py +++ b/rootfs.py @@ -13,6 +13,8 @@ # SPDX-FileCopyrightText: 2021 Melg Eight # SPDX-FileCopyrightText: 2021-23 Samuel Tyler # SPDX-FileCopyrightText: 2023-24 Gábor Stefanik +# SPDX-FileCopyrightText: 2024 Lance Vick +# SPDX-FileCopyrightText: 2025 Kevin Nause import argparse import os @@ -34,7 +36,7 @@ def create_configuration_file(args): config.write(f"ARCH={args.arch}\n") config.write(f"ARCH_DIR={stage0_arch_map.get(args.arch, args.arch)}\n") config.write(f"FORCE_TIMESTAMPS={args.force_timestamps}\n") - config.write(f"CHROOT={args.chroot or args.bwrap}\n") + config.write(f"CHROOT={args.chroot or args.bwrap or args.docker}\n") config.write(f"UPDATE_CHECKSUMS={args.update_checksums}\n") config.write(f"JOBS={args.cores}\n") config.write(f"SWAP_SIZE={args.swap}\n") @@ -75,6 +77,8 @@ def main(): action="store_true") parser.add_argument("-bw", "--bwrap", help="Run inside a bwrap sandbox", action="store_true") + parser.add_argument("-do", "--docker", help="Run inside a docker build", + action="store_true") parser.add_argument("-t", "--target", help="Target directory", default="target") parser.add_argument("--tmpfs", help="Use a tmpfs on target", @@ -140,15 +144,17 @@ def check_types(): count += 1 if args.bwrap: count += 1 + if args.docker: + count += 1 if args.bare_metal: count += 1 return count if check_types() > 1: - raise ValueError("No more than one of qemu, chroot, bwrap, bare metal" + raise ValueError("No more than one of qemu, chroot, bwrap, docker, bare metal" "may be used.") if check_types() == 0: - raise ValueError("One of qemu, chroot, bwrap, or bare metal must be selected.") + raise ValueError("One of qemu, chroot, bwrap, docker, or bare metal must be selected.") # Arch validation if args.arch != "x86": @@ -171,6 +177,9 @@ def check_types(): else: args.target_size = 0 + if args.docker: + args.external_sources = True + # Swap file size validation if args.qemu or args.bare_metal: args.swap = (int(str(args.swap).rstrip('gGmM')) * @@ -250,6 +259,22 @@ def bootstrap(args, generator, target, size, cleanup): run_as_root('env', '-i', 'PATH=/bin', chroot_binary, generator.target_dir, init, cleanup=cleanup) + elif args.docker: + generator.prepare(target, using_kernel=False) + arch = stage0_arch_map.get(args.arch, args.arch) + init = os.path.join(os.sep, 'bootstrap-seeds', 'POSIX', arch, 'kaem-optional-seed') + target_rel = os.path.relpath(generator.target_dir, os.getcwd()) + run('env', '-i', 'DOCKER_BUILDKIT=1', + 'docker', 'build', + '--build-arg=ARCH='+ arch, + '--build-arg=TARGET=' + target_rel, + '--build-arg=SOURCE_DATE_EPOCH=1', + '--progress=auto', + '--platform=linux/amd64,linux/arm64,linux/i386,linux/riscv64', + '--target=package', + '--tag=live-bootstrap-' + arch, + '.') + elif args.bwrap: init = '/init' if not args.internal_ci or args.internal_ci == "pass1":