rootfs: dependency-resolved --local-debs installation, APT-based kernel delivery, and deferred bootloader update#172
Open
bjordiscollaku wants to merge 4 commits into
Conversation
a1e3eb6 to
dc80c48
Compare
dc80c48 to
444f0e6
Compare
Problem - ROOTFS_DIR was bound to $WORKDIR/rootfs while the script itself resides under rootfs/scripts/ in the same repository. - The preprocessing stage performs `rm -rf "$ROOTFS_DIR"`; when invoked from qcom-build-utils/, that path overlaps the source tree and can remove the script directory used by subsequent invocations. Root cause - The working rootfs output directory and the repository source directory shared the same basename (rootfs) and filesystem root. Implementation - Rename the runtime extraction/build directory from $WORKDIR/rootfs to $WORKDIR/rootfs_work. Operational impact - Prevents destructive collision between generated rootfs state and checked-in script sources. - Enables repeated invocations in a single CI job (e.g. multi-kernel loops) without losing rootfs/scripts/build-rootfs.sh mid-run. Scope - Path-safety fix only; no package-selection or install-order behavior changes. Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
Context
- The script previously required --kernel-package, which blocked valid
flows where kernels are provided by apt sources or installed later
via overlay manifests.
- Local .deb injection relied on direct package install assumptions
and did not provide a first-class dependency-resolving ingestion
path.
Implementation
- Relax the required CLI contract:
- keep --product-conf and --seed mandatory,
- make --kernel-package optional with conditional validation, copy,
and install behavior.
- Introduce a repeatable --local-debs <path> input:
- accepts file or directory paths,
- validates each path preflight,
- stages all discovered debs into /opt/local-debs in the rootfs.
- Inside the chroot, create a temporary local apt repository from the
staged debs:
- dpkg-scanpackages index generation,
- file:///opt/local-debs source registration,
- package-name extraction from deb metadata,
- apt-driven installation to resolve inter-package dependencies.
Why an apt-backed local repo
- `dpkg -i` alone is non-closure-preserving for dependency graphs.
- A local apt repository allows the resolver to satisfy dependency
chains across the provided package set in deterministic order.
Operational behavior
- If no --local-debs are provided, local-repo stages are true no-ops.
- If directories are provided but empty, execution remains stable (no
hard failure).
Scope
- Packaging ingress expansion and input-contract modernization; the
bootloader sequencing change lands in a subsequent commit.
Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
Problem - update-grub was executed immediately after the direct kernel `dpkg -i`, before overlay apt packages and local-debs repo packages were installed. - When the effective kernel came from overlay manifest apt sources, GRUB generation could observe stale kernel state. Implementation - Move post-bootloader actions to after all install sources complete: 1. firmware `dpkg -i` (optional) 2. kernel `dpkg -i` (optional) 3. overlay manifest apt packages 4. local-debs apt repository packages 5. update-grub 6. grub cleanup/normalization 7. package manifest capture/delta generation - Keep the explicit update-grub invocation in the chroot (do not rely on hook execution gated by systemd runtime presence). Result - Bootloader state reflects final package-set convergence rather than an intermediate state. - The kernel delivery path (direct deb vs apt overlay vs local deb repo) no longer changes GRUB correctness. Scope - Install-order and bootloader-timing correction only; the DTB policy cleanup remains separate. Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
Context - The script carried a platform-specific Debian path that: - searched for glymur-crd.dtb, - created a /boot/dtb symlink, - patched grub.cfg to inject devicetree directives. - This behavior is board-specific policy, not generic rootfs assembly responsibility. Implementation - Remove the Debian-only DTB injection block entirely, including the conditional guard, filesystem probing, symlink synthesis, and GRUB mutation. Architectural rationale - Keep build-rootfs.sh focused on distribution-agnostic image construction. - Avoid embedding platform policy and hardcoded DTB assumptions in a shared reusable tool. - Defer DTB/boot policy to dedicated metadata/build layers that own board-specific behavior. Operational impact - Reduces implicit side effects in the generated GRUB config. - Improves portability and maintainability of rootfs construction across product lines. Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
444f0e6 to
262231c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
build-rootfs.shgains a dependency-resolving install path for locally built.debpackages (--local-debs), drops the hard requirement on--kernel-package, and defersupdate-grubuntil every package-delivery path has completed. With these changes the kernel can arrive via any of three routes: direct deb (--kernel-package), overlay-manifest APT source (--overlay), or a staged local APT repository (--local-debs). In all cases the generated bootloader configuration reflects the final installed package set, regardless of which route delivered the kernel.Two supporting changes complete the PR: a workspace-isolation fix that makes the script safe to invoke repeatedly from the same checkout (required for multi-kernel CI loops), and removal of a board-specific DTB/GRUB mutation that did not belong in this distribution-agnostic tool.
Primary consumer: the multi-kernel distro pipeline in qcom-distro-images #84, which invokes this script once per kernel variant.
Why
Four independent problems, fixed in four scoped commits:
--kernel-packagewas mandatory, blocking flows where the kernel comes from APT or overlay manifests. Plaindpkg -icannot resolve dependency graphs, which is a problem for kernel package sets that span multiple debs (for example canonical-layout kernels split acrosslinux-image-*andlinux-modules-*with inter-package dependencies).update-grubran too early. It executed immediately after the direct kerneldpkg -i, before overlay-manifest and local-debs installs. When the effective kernel arrives through one of those later paths, GRUB was generated against stale kernel state.ROOTFS_DIRwas$WORKDIR/rootfs, which collides with the repository's ownrootfs/source directory when invoked from the repo root. The preprocessing stage'srm -rf "$ROOTFS_DIR"could removerootfs/scripts/build-rootfs.shmid-run, which breaks any loop that calls the script more than once per workspace.glymur-crd.dtb, symlinked it to/boot/dtb, and injected adevicetreedirective intogrub.cfg. That is platform-specific boot policy hardcoded into shared rootfs assembly.What changed (one commit per concern)
1.
rootfs: isolate build workspace from source tree(dd57d03)Runtime extraction/build directory renamed from
$WORKDIR/rootfsto$WORKDIR/rootfs_work. The generated-state directory and the checked-in source directory no longer share a path, so repeated invocations in a single CI job can no longer destroy the script sources. Path-safety fix only; no other behavior change.2.
rootfs: add local-debs flow; kernel deb optional(5dd6591)New repeatable CLI option
--local-debs <path>:.debfile or a directory of.debfiles, and may be given multiple times,/opt/local-debsinside the rootfs.Inside the chroot a throwaway local APT repository is built from the staged debs:
dpkg-dev(--no-install-recommends),dpkg-scanpackages . /dev/null > Packages,deb [trusted=yes] file:///opt/local-debs ./insources.list.d/local-debs.list,dpkg-deb --field <deb> Package),apt-get install -y <names>, so APT computes the install order and satisfies inter-package dependencies across the provided set and the configured remote sources.Why an APT repository instead of
dpkg -i:dpkg -iinstalls exactly what it is handed and fails on unmet dependencies. The local-repo approach lets APT resolve dependency chains (for examplelinux-imageandlinux-modules) deterministically.In the same commit,
--kernel-packagebecomes optional, with conditional validation, copy, and install (a skip message when absent).--product-confand--seedremain the only required arguments. With no--local-debs, every local-repo stage is a no-op.3.
rootfs: run update-grub after all package installs(35cdcac)update-grub(and the subsequent grub.cfg normalization and manifest-delta capture) moves from immediately after the kerneldpkg -ito after all install sources complete. The explicit invocation is kept because thezz-update-grubkernel hook skips itself in a chroot (no/run/systemd/system). The bootloader state now reflects whichever kernel was installed last, and the kernel delivery path no longer changes GRUB correctness.4.
rootfs: drop Debian-specific DTB injection(dc80c48)Removes the
glymur-crd.dtbprobe, the/boot/dtbsymlink, and thegrub.cfgdevicetreeinjection. DTB and boot policy belong to the target-specific layers that own board behavior (in the distro pipeline, FIT DTB generation is handled per kernel variant by qcom-distro-images CI), not to the generic image builder.Install sequence inside the chroot (after this PR)
dpkg -i(if--firmware).dpkg -i(if--kernel-package).--overlay).--local-debs).update-grub, explicit, after all installs.search --label system, striproot=/dev/*).CLI contract
--product-conf--seed--kernel-package--firmware--overlay--variant--local-debsCompatibility
--kernel-packageworks exactly as before, and the new behavior is opt-in.update-grubtiming: for callers whose kernel arrives only via--kernel-package(and whose overlays do not install kernels), the resultinggrub.cfgis identical; it is simply generated later. For overlay or local-debs delivered kernels, the previous behavior was incorrect and is now fixed.devicetreedirective. The only consumer (trixie/generic) is retired from the nightly matrix in qcom-distro-images #84; board DTB policy is now owned by target-specific layers.Known trade-off (candidate follow-up)
The staged debs under
/opt/local-debsand thelocal-debs.listAPT source entry remain in the shipped image (the index stays self-consistent, so on-deviceapt updatekeeps working). This mirrors the existing behavior of the kernel deb being copied to/and left there, but it does add image payload. A cleanup pass (remove staged debs and the source entry after install) is a reasonable follow-up.Files changed
rootfs/scripts/build-rootfs.sh(+111/-50)Validation
bash -n rootfs/scripts/build-rootfs.sh.qcom-build-utils-ref=feat/rootfs-local-debs-overlay-kernel-bootflow. Every rootfs in those runs was assembled by this script, covering both the single-deb qcom-next closure and the multi-deb canonical closure (linux-imagepluslinux-modulesresolved by APT), across latest and pinned modes and server and desktop matrix targets.