Skip to content

Commit e728a8b

Browse files
authored
Use Linux architecture names in output artifact filenames: (#49)
## Description Tinkerbell iPXE scripts are built around architecture naming in a specific way. x86_64 and aarch64. This makes CaptainOS backward compatible. Fixes: # ## How Has This Been Tested? ## How are existing users impacted? What migration steps/scripts do we need? ## Checklist: I have: - [ ] updated the documentation and/or roadmap (if required) - [ ] added unit or e2e tests - [ ] provided instructions on how to upgrade
2 parents 2d0e189 + c9d73bc commit e728a8b

File tree

12 files changed

+49
-37
lines changed

12 files changed

+49
-37
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,11 @@ jobs:
279279
fail-fast: false
280280
matrix:
281281
arch: [amd64, arm64]
282+
include:
283+
- arch: amd64
284+
output_arch: x86_64
285+
- arch: arm64
286+
output_arch: aarch64
282287
env:
283288
ARCH: ${{ matrix.arch }}
284289
KERNEL_MODE: skip
@@ -339,7 +344,7 @@ jobs:
339344
- name: Stage initramfs for ISO build
340345
run: |
341346
mkdir -p "mkosi.output/initramfs/${KERNEL_VERSION}/${{ matrix.arch }}"
342-
cp "out/initramfs-${KERNEL_VERSION}-${{ matrix.arch }}.cpio.zst" \
347+
cp "out/initramfs-${KERNEL_VERSION}-${{ matrix.output_arch }}" \
343348
"mkosi.output/initramfs/${KERNEL_VERSION}/${{ matrix.arch }}/image.cpio.zst"
344349
345350
- name: Install Python dependencies
@@ -352,7 +357,7 @@ jobs:
352357
uses: actions/upload-artifact@v4
353358
with:
354359
name: iso-${{ matrix.arch }}
355-
path: out/captainos-${{ env.KERNEL_VERSION }}-${{ matrix.arch }}.iso
360+
path: out/captainos-${{ env.KERNEL_VERSION }}-${{ matrix.output_arch }}.iso
356361
retention-days: 1
357362

358363
# -------------------------------------------------------------------

README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ It is built with [mkosi](https://github.com/systemd/mkosi), producing a minimal
1616

1717
## How it works
1818

19-
1. The machine PXE boots the kernel (`vmlinuz`) and initramfs (`initramfs.cpio.zst`) or runs the UEFI-bootable ISO image
19+
1. The machine PXE boots the kernel (`vmlinuz`) and initramfs (`initramfs`) or runs the UEFI-bootable ISO image
2020
2. A custom `/init` script transitions the rootfs to tmpfs, then exec's systemd
2121
3. systemd-networkd configures DHCP on all ethernet interfaces
2222
4. containerd starts, then `tink-agent-setup` pulls the tink-agent container image (configured via kernel cmdline), extracts the binary, and runs it as a host process
@@ -87,10 +87,12 @@ commands:
8787
8888
Output artifacts are placed in `out/`:
8989
90-
- `out/initramfs-<arch>.cpio.zst` — the initramfs
91-
- `out/vmlinuz-<arch>` — the kernel
92-
- `out/captainos-<arch>.iso` — UEFI-bootable ISO image
93-
- `out/sha256sums-<arch>.txt` — SHA-256 checksums
90+
- `out/vmlinuz-<kver>-<arch>` — the kernel
91+
- `out/initramfs-<kver>-<arch>` — the initramfs
92+
- `out/captainos-<kver>-<arch>.iso` — UEFI-bootable ISO image
93+
- `out/sha256sums-<kver>-<arch>.txt` — SHA-256 checksums
94+
95+
Here `<kver>` is the kernel version (e.g. `6.18.16`) and `<arch>` is the Linux architecture name (`x86_64` or `aarch64`).
9496
9597
## Release
9698
@@ -102,8 +104,8 @@ Three multi-arch OCI indexes are published per build:
102104
103105
| Image | Tag | Contents |
104106
| --- | --- | --- |
105-
| amd64-only | `vX.Y.Z-<sha7>-amd64` | vmlinuz, initramfs, ISO, checksums (amd64) |
106-
| arm64-only | `vX.Y.Z-<sha7>-arm64` | vmlinuz, initramfs, ISO, checksums (arm64) |
107+
| amd64-only | `vX.Y.Z-<sha7>-amd64` | vmlinuz, initramfs, ISO, checksums (x86_64) |
108+
| arm64-only | `vX.Y.Z-<sha7>-arm64` | vmlinuz, initramfs, ISO, checksums (aarch64) |
107109
| combined | `vX.Y.Z-<sha7>` | all artifacts from both architectures |
108110
109111
Each artifact file is pushed as its own OCI layer. Deterministic tar creation (zeroed metadata) ensures identical layer digests across per-arch and combined images, so registries deduplicate shared blobs — the combined image adds zero additional storage.
@@ -119,8 +121,8 @@ When a `v*` tag is pushed, the release workflow:
119121
120122
1. Pulls the combined OCI image (both architectures)
121123
2. Attaches all artifacts as downloadable files on the GitHub Release page:
122-
- `vmlinuz-amd64`, `initramfs-amd64.cpio.zst`, `captainos-amd64.iso`, `sha256sums-amd64.txt`
123-
- `vmlinuz-arm64`, `initramfs-arm64.cpio.zst`, `captainos-arm64.iso`, `sha256sums-arm64.txt`
124+
- `vmlinuz-<kver>-x86_64`, `initramfs-<kver>-x86_64`, `captainos-<kver>-x86_64.iso`, `sha256sums-<kver>-x86_64.txt`
125+
- `vmlinuz-<kver>-aarch64`, `initramfs-<kver>-aarch64`, `captainos-<kver>-aarch64.iso`, `sha256sums-<kver>-aarch64.txt`
124126
3. Tags all three OCI images with the clean release version (`vX.Y.Z`, `vX.Y.Z-amd64`, `vX.Y.Z-arm64`)
125127
126128
### Release subcommands

captain/artifacts.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def collect_kernel(cfg: Config, logger: StageLogger | None = None) -> None:
3939
vmlinuz_files = sorted(vmlinuz_dir.glob("vmlinuz-*")) if vmlinuz_dir.is_dir() else []
4040
if vmlinuz_files:
4141
vmlinuz_src = vmlinuz_files[0]
42-
vmlinuz_dst = out / f"vmlinuz-{cfg.kernel_version}-{cfg.arch}"
42+
vmlinuz_dst = out / f"vmlinuz-{cfg.kernel_version}-{cfg.arch_info.output_arch}"
4343
shutil.copy2(vmlinuz_src, vmlinuz_dst)
4444
_log.log(f"kernel: {vmlinuz_dst} ({_human_size(vmlinuz_dst.stat().st_size)})")
4545
else:
@@ -53,7 +53,7 @@ def collect_initramfs(cfg: Config, logger: StageLogger | None = None) -> None:
5353
cpio_files = sorted(cfg.initramfs_output.glob("*.cpio*"))
5454
if cpio_files:
5555
initrd_src = cpio_files[0]
56-
initrd_dst = out / f"initramfs-{cfg.kernel_version}-{cfg.arch}.cpio.zst"
56+
initrd_dst = out / f"initramfs-{cfg.kernel_version}-{cfg.arch_info.output_arch}"
5757
shutil.copy2(initrd_src, initrd_dst)
5858
_log.log(f"initramfs: {initrd_dst} ({_human_size(initrd_dst.stat().st_size)})")
5959
else:
@@ -68,7 +68,7 @@ def collect_iso(cfg: Config, logger: StageLogger | None = None) -> None:
6868
iso_files = sorted(iso_dir.glob("*.iso")) if iso_dir.is_dir() else []
6969
if iso_files:
7070
iso_src = iso_files[0]
71-
iso_dst = out / f"captainos-{cfg.kernel_version}-{cfg.arch}.iso"
71+
iso_dst = out / f"captainos-{cfg.kernel_version}-{cfg.arch_info.output_arch}.iso"
7272
shutil.copy2(iso_src, iso_dst)
7373
_log.log(f"iso: {iso_dst} ({_human_size(iso_dst.stat().st_size)})")
7474

captain/cli/_commands.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,18 @@ def _cmd_checksums(cfg: Config, _extra_args: list[str], args: object = None) ->
271271
else:
272272
# Default mode: produce checksums for the selected architecture.
273273
out = cfg.output_dir
274-
arch = cfg.arch
274+
oarch = cfg.arch_info.output_arch
275275
kver = cfg.kernel_version
276276
arch_files = [
277-
out / f"vmlinuz-{kver}-{arch}",
278-
out / f"initramfs-{kver}-{arch}.cpio.zst",
279-
out / f"captainos-{kver}-{arch}.iso",
277+
out / f"vmlinuz-{kver}-{oarch}",
278+
out / f"initramfs-{kver}-{oarch}",
279+
out / f"captainos-{kver}-{oarch}.iso",
280280
]
281281
existing = [f for f in arch_files if f.is_file()]
282282
if not existing:
283-
clog.err(f"No artifacts found for {kver}-{arch} in {out}")
283+
clog.err(f"No artifacts found for {kver}-{oarch} in {out}")
284284
raise SystemExit(1)
285-
dest = Path(output) if output else out / f"sha256sums-{kver}-{arch}.txt"
285+
dest = Path(output) if output else out / f"sha256sums-{kver}-{oarch}.txt"
286286
artifacts.collect_checksums(existing, dest, logger=clog)
287287
clog.log("Checksums complete!")
288288

captain/cli/_stages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def _build_iso_stage(cfg: Config) -> None:
161161
return
162162

163163
# --- idempotency --------------------------------------------------
164-
iso_path = cfg.iso_output / f"captainos-{cfg.kernel_version}-{cfg.arch}.iso"
164+
iso_path = cfg.iso_output / f"captainos-{cfg.kernel_version}-{cfg.arch_info.output_arch}.iso"
165165
if iso_path.is_file() and not cfg.force_iso:
166166
isolog.log(f"ISO already built: {iso_path} (use --force-iso to rebuild)")
167167
return

captain/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def mkosi_output(self) -> Path:
187187

188188
@property
189189
def initramfs_output(self) -> Path:
190-
"""Per-version, per-arch directory for mkosi initramfs output (image.cpio.zst)."""
190+
"""Per-version, per-arch directory for mkosi initramfs output."""
191191
return self.project_dir / "mkosi.output" / "initramfs" / self.kernel_version / self.arch
192192

193193
@property

captain/iso.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def _grub_cfg(arch: str) -> str:
3434
3535
menuentry "CaptainOS" {{
3636
linux /boot/vmlinuz {console} 464vn90e7rbj08xbwdjejmdf4it17c5zfzjyfhthbh19eij201hjgit021bmpdb9ctrc87x2ymc8e7icu4ffi15x1hah9iyaiz38ckyap8hwx2vt5rm44ixv4hau8iw718q5yd019um5dt2xpqqa2rjtdypzr5v1gun8un110hhwp8cex7pqrh2ivh0ynpm4zkkwc8wcn367zyethzy7q8hzudyeyzx3cgmxqbkh825gcak7kxzjbgjajwizryv7ec1xm2h0hh7pz29qmvtgfjj1vphpgq1zcbiiehv52wrjy9yq473d9t1rvryy6929nk435hfx55du3ih05kn5tju3vijreru1p6knc988d4gfdz28eragvryq5x8aibe5trxd0t6t7jwxkde34v6pj1khmp50k6qqj3nzgcfzabtgqkmeqhdedbvwf3byfdma4nkv3rcxugaj2d0ru30pa2fqadjqrtjnv8bu52xzxv7irbhyvygygxu1nt5z4fh9w1vwbdcmagep26d298zknykf2e88kumt59ab7nq79d8amnhhvbexgh48e8qc61vq2e9qkihzt1twk1ijfgw70nwizai15iqyted2dt9gfmf2gg7amzufre79hwqkddc1cd935ywacnkrnak6r7xzcz7zbmq3kt04u2hg1iuupid8rt4nyrju51e6uejb2ruu36g9aibmz3hnmvazptu8x5tyxk820g2cdpxjdij766bt2n3djur7v623a2v44juyfgz80ekgfb9hkibpxh3zgknw8a34t4jifhf116x15cei9hwch0fye3xyq0acuym8uhitu5evc4rag3ui0fny3qg4kju7zkfyy8hwh537urd5uixkzwu5bdvafz4jmv7imypj543xg5em8jk8cgk7c4504xdd5e4e71ihaumt6u5u2t1w7um92fepzae8p0vq93wdrd1756npu1pziiur1payc7kmdwyxg3hj5n4phxbc29x0tcddamjrwt260b0w
37-
initrd /boot/initramfs.cpio.zst
37+
initrd /boot/initramfs
3838
}}
3939
""")
4040

@@ -70,7 +70,7 @@ def build(cfg: Config) -> None:
7070
│ ├── grub/
7171
│ │ └── grub.cfg
7272
│ ├── vmlinuz
73-
│ └── initramfs.cpio.zst
73+
│ └── initramfs
7474
7575
``grub-mkrescue`` turns this into a bootable ISO with an embedded
7676
EFI System Partition.
@@ -95,14 +95,14 @@ def build(cfg: Config) -> None:
9595

9696
# Copy kernel and initramfs
9797
shutil.copy2(vmlinuz, boot_dir / "vmlinuz")
98-
shutil.copy2(initramfs, boot_dir / "initramfs.cpio.zst")
98+
shutil.copy2(initramfs, boot_dir / "initramfs")
9999

100100
# Write GRUB configuration
101101
(grub_dir / "grub.cfg").write_text(_grub_cfg(cfg.arch))
102102

103103
# Build the ISO
104104
iso_dir = ensure_dir(cfg.iso_output)
105-
iso_path = iso_dir / f"captainos-{cfg.kernel_version}-{cfg.arch}.iso"
105+
iso_path = iso_dir / f"captainos-{cfg.kernel_version}-{cfg.arch_info.output_arch}.iso"
106106

107107
_log.log(f"Building ISO with grub-mkrescue ({grub_platform})...")
108108
grub_mkrescue = shutil.which("grub-mkrescue")

captain/oci/_build.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from captain import artifacts, buildah
1212
from captain.log import StageLogger
13+
from captain.util import get_arch_info
1314

1415

1516
def _deterministic_tar(file_path: Path, output_dir: Path) -> Path:
@@ -47,19 +48,20 @@ def _collect_arch_artifacts(
4748
# Collect kernel
4849
vmlinuz_dir = project_dir / "mkosi.output" / "kernel" / kernel_version / arch
4950
vmlinuz_files = sorted(vmlinuz_dir.glob("vmlinuz-*")) if vmlinuz_dir.is_dir() else []
50-
vmlinuz_dst = out / f"vmlinuz-{kernel_version}-{arch}"
51+
oarch = get_arch_info(arch).output_arch
52+
vmlinuz_dst = out / f"vmlinuz-{kernel_version}-{oarch}"
5153
if vmlinuz_files:
5254
shutil.copy2(vmlinuz_files[0], vmlinuz_dst)
5355
logger.log(f"kernel: {vmlinuz_dst}")
5456
else:
5557
logger.warn(f"No kernel image found for {arch}")
5658

5759
arch_files = [
58-
out / f"vmlinuz-{kernel_version}-{arch}",
59-
out / f"initramfs-{kernel_version}-{arch}.cpio.zst",
60-
out / f"captainos-{kernel_version}-{arch}.iso",
60+
out / f"vmlinuz-{kernel_version}-{oarch}",
61+
out / f"initramfs-{kernel_version}-{oarch}",
62+
out / f"captainos-{kernel_version}-{oarch}.iso",
6163
]
62-
checksums_path = out / f"sha256sums-{kernel_version}-{arch}.txt"
64+
checksums_path = out / f"sha256sums-{kernel_version}-{oarch}.txt"
6365
artifacts.collect_checksums(arch_files, checksums_path, logger=logger)
6466

6567
push_files = [*arch_files, checksums_path]

captain/qemu.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ def run_qemu(cfg: Config, args: argparse.Namespace | None = None) -> None:
6565
:mod:`configargparse`. When provided, Tinkerbell kernel cmdline
6666
parameters are drawn from it instead of the environment.
6767
"""
68-
kernel = cfg.output_dir / f"vmlinuz-{cfg.kernel_version}-{cfg.arch}"
69-
initrd = cfg.output_dir / f"initramfs-{cfg.kernel_version}-{cfg.arch}.cpio.zst"
68+
kernel = cfg.output_dir / f"vmlinuz-{cfg.kernel_version}-{cfg.arch_info.output_arch}"
69+
initrd = cfg.output_dir / f"initramfs-{cfg.kernel_version}-{cfg.arch_info.output_arch}"
7070

7171
missing: list[str] = []
7272
if not kernel.is_file():

captain/tools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def download_all(cfg: Config) -> None:
167167

168168
_log.log("Tool download complete.")
169169

170-
# NOTE: UPX compression is not used. The final image is cpio.zst, and zstd
171-
# compresses raw ELF binaries better than UPX-packed ones (UPX output looks
172-
# like random data to zstd, defeating its compression).
170+
# NOTE: UPX compression is not used. The initramfs is a zstd-compressed
171+
# CPIO archive, and zstd compresses raw ELF binaries better than UPX-packed
172+
# ones (UPX output looks like random data to zstd, defeating its compression).
173173
_log.log("All tools ready.")

0 commit comments

Comments
 (0)