diff --git a/hydra/image.py b/hydra/image.py index 9d7eebe..5df4562 100644 --- a/hydra/image.py +++ b/hydra/image.py @@ -142,6 +142,8 @@ def kernel_image(root): candidates = [root_path(root, 'boot/' + prefix + version) for prefix in ('kernel-', 'vmlinuz-')] images = [path for path in candidates if path.is_file()] + if len(images) == 2 and images[0].samefile(images[1]): + return images[1] # Stable installer/GRUB vmlinuz alias for Gentoo kernel-bin. if len(images) != 1: raise ImageError('Installed modules require exactly one matching boot kernel image') return images[0] @@ -256,6 +258,10 @@ def configure_rootfs(root, source): if not (source / name).is_file(): raise ImageError(f'Required committed runtime/installer asset is missing: {name}') version = kernel_version(root) + kernel = kernel_image(root) + alias = root_path(root, 'boot/vmlinuz-' + version) + if not alias.exists(): + alias.symlink_to(kernel.name) sanitize_rootfs(root) copy_overlay(root, source / 'image/rootfs') write(root, 'etc/hydra/live-mode/dracut.conf', (source / 'image/dracut.conf').read_text()) diff --git a/image/README.md b/image/README.md index ea6fc9e..cf2a926 100644 --- a/image/README.md +++ b/image/README.md @@ -8,6 +8,8 @@ scripts/hydra-build fetch scripts/hydra-build prepare scripts/hydra-build packages --pretend scripts/hydra-build packages +scripts/hydra-build rootfs +scripts/hydra-build iso scripts/hydra-build status ``` @@ -34,5 +36,12 @@ for installed-system updates. Rolling binhost contents can change; resulting versions/USE are recorded without claiming bit-identical output. The pinned service-keyring hash may need deliberate review when Gentoo rotates keys. -`rootfs` and `iso` are reserved assembly commands and currently return an -explicit error until the live-image assembly implementation is integrated. +`rootfs` exports the dedicated builder into a private intermediate and assembles +an isolated sanitized payload, generic live initramfs and squashfs. `iso` checks +those artifacts and emits the UEFI `HYDRA_OS` ISO, SHA256 and build manifest in +`build/artifacts/`. The working builder is retained for package retries. The +installed Gentoo repository uses the standard rsync backend with signed +metamanifest verification; the builder continues to use its pinned snapshot. +QEMU and remote-viewer retain local patches and are excluded from upstream +binary packages on installed updates. Disposable boot/install validation is +required before treating an emitted ISO as release ready. diff --git a/image/configure-rootfs.sh b/image/configure-rootfs.sh index c27094b..58f66aa 100755 --- a/image/configure-rootfs.sh +++ b/image/configure-rootfs.sh @@ -6,6 +6,11 @@ if [[ $# != 1 || "$1" != /hydra-output/rootfs ]]; then fi root=$1 python3 /hydra-source/hydra/image.py configure "$root" --source /hydra-source +# This is a separate rootfs inside the assembly container, never the host root. +mount --bind /dev "$root/dev" +mount -t proc proc "$root/proc" +mount --bind /sys "$root/sys" +trap 'umount "$root/sys" "$root/proc" "$root/dev"' EXIT for group in wheel kvm video render audio; do chroot "$root" getent group "$group" >/dev/null || chroot "$root" groupadd -r "$group" done @@ -24,10 +29,5 @@ chroot "$root" chmod 0750 /usr/libexec/qemu-bridge-helper chroot "$root" visudo --check systemctl --root="$root" enable NetworkManager.service greetd.service hydra-network.service hydra-dnsmasq.service hydra-bridge-permissions.service getty@tty2.service serial-getty@ttyS0.service systemctl --root="$root" set-default graphical.target -# This is a separate rootfs inside the assembly container, never the host root. -mount --bind /dev "$root/dev" -mount -t proc proc "$root/proc" -mount --bind /sys "$root/sys" -trap 'umount "$root/sys" "$root/proc" "$root/dev"' EXIT kernel=$(python3 /hydra-source/hydra/image.py kernel "$root") chroot "$root" dracut --force --no-hostonly --no-hostonly-cmdline --conf /etc/hydra/live-mode/dracut.conf --confdir /etc/hydra/live-mode/dracut.conf.d --add dmsquash-live /boot/initramfs-live.img "$kernel" diff --git a/image/rootfs/etc/portage/repos.conf/gentoo.conf b/image/rootfs/etc/portage/repos.conf/gentoo.conf new file mode 100644 index 0000000..52fe413 --- /dev/null +++ b/image/rootfs/etc/portage/repos.conf/gentoo.conf @@ -0,0 +1,11 @@ +[gentoo] +location = /var/db/repos/gentoo +sync-type = rsync +sync-uri = rsync://rsync.gentoo.org/gentoo-portage +auto-sync = yes +sync-rsync-verify-metamanifest = yes +sync-rsync-verify-max-age = 3 +sync-openpgp-key-package = sec-keys/openpgp-keys-gentoo-release +sync-openpgp-key-path = /usr/share/openpgp-keys/gentoo-release.asc +sync-openpgp-keyserver = hkps://keys.gentoo.org +sync-webrsync-verify-signature = yes diff --git a/tests/test_image.py b/tests/test_image.py index 7aade23..7c6bed8 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -46,6 +46,37 @@ class ImageTests(unittest.TestCase): self.assertEqual(image.kernel_version(self.root), version) self.assertEqual(image.kernel_image(self.root), kernel) + def test_configured_real_kernel_layout_is_accepted_by_installer(self): + from hydra.installer import select_kernel + version = '6.18.54-gentoo-dist-bin' + (self.root / 'lib/modules' / version).mkdir(parents=True) + kernel = self.file('boot/kernel-' + version, 'real Gentoo kernel') + image.configure_rootfs(self.root, Path(__file__).resolve().parents[1]) + self.assertEqual(select_kernel(self.root, None), version) + alias = self.root / ('boot/vmlinuz-' + version) + self.assertTrue(alias.samefile(kernel)) + self.assertEqual(image.kernel_image(self.root), alias) + + def test_exported_repository_sync_works_with_extracted_tree(self): + import configparser + self.kernel() + self.file('etc/portage/repos.conf/gentoo.conf', + '[gentoo]\nlocation=/var/db/repos/gentoo\nsync-type=git\nauto-sync=no\n') + self.file('var/db/repos/gentoo/profiles/repo_name', 'gentoo') + image.configure_rootfs(self.root, Path(__file__).resolve().parents[1]) + config = configparser.ConfigParser() + config.read(self.root / 'etc/portage/repos.conf/gentoo.conf') + repo = config['gentoo'] + self.assertEqual(repo['sync-type'], 'rsync') + self.assertEqual(repo['sync-uri'], 'rsync://rsync.gentoo.org/gentoo-portage') + self.assertEqual(repo['auto-sync'], 'yes') + self.assertEqual(repo['sync-rsync-verify-metamanifest'], 'yes') + self.assertEqual(repo['sync-openpgp-key-path'], '/usr/share/openpgp-keys/gentoo-release.asc') + self.assertFalse((self.root / 'var/db/repos/gentoo/.git').exists()) + source = Path(__file__).resolve().parents[1] + builder_config = (source / 'image/portage/repos.conf/gentoo.conf').read_text() + self.assertIn('auto-sync = no', builder_config) + def test_conflicting_kernel_images_refused(self): version = self.kernel() self.file('boot/kernel-' + version, 'different kernel')