Skip to content

Commit a65cf2f

Browse files
authored
Update cli (tinkerbell#9)
## Description All cli flags can be defined as env vars. Flags translate to envs by becoming dropping the initial `--`, words become all caps, and dashes become underscores. For example, `--mkosi-mode` -> `MKOSI_MODE` Also, updated the help menu presentation. Organized by global and subcommand specific flags. 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 cf9a3ac + fc03348 commit a65cf2f

File tree

15 files changed

+705
-458
lines changed

15 files changed

+705
-458
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ jobs:
6262
mkosi.output/vmlinuz/${{ matrix.arch }}
6363
key: kernel-${{ matrix.arch }}-${{ env.KERNEL_VERSION }}-${{ hashFiles('config/defconfig.*', 'Dockerfile') }}
6464

65+
- name: Install Python dependencies
66+
run: pip install -r requirements.txt
67+
6568
- name: Build kernel
6669
run: ./build.py kernel
6770

@@ -112,6 +115,9 @@ jobs:
112115
mkosi.output/extra-tree/${{ matrix.arch }}/opt/cni
113116
key: tools-${{ matrix.arch }}-${{ hashFiles('captain/tools.py') }}
114117

118+
- name: Install Python dependencies
119+
run: pip install -r requirements.txt
120+
115121
- name: Download tools
116122
run: ./build.py tools
117123

@@ -174,6 +180,9 @@ jobs:
174180
sudo apt-get update
175181
sudo apt-get install -y bubblewrap
176182
183+
- name: Install Python dependencies
184+
run: pip install -r requirements.txt
185+
177186
- name: Build initramfs
178187
run: ./build.py initramfs
179188

@@ -238,6 +247,9 @@ jobs:
238247
mkdir -p mkosi.output/initramfs/${{ matrix.arch }}
239248
cp out/initramfs-${{ matrix.arch }}.cpio.zst mkosi.output/initramfs/${{ matrix.arch }}/image.cpio.zst
240249
250+
- name: Install Python dependencies
251+
run: pip install -r requirements.txt
252+
241253
- name: Build ISO
242254
run: ./build.py iso
243255

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
6363

6464
# Install mkosi from GitHub (not on PyPI)
6565
RUN pip3 install --break-system-packages \
66+
configargparse \
6667
"git+https://github.com/systemd/mkosi.git@${MKOSI_VERSION}"
6768

6869
# Verify mkosi is functional

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,6 @@ CaptainOS reads provisioning configuration from the kernel command line:
122122
├── config/
123123
│ ├── defconfig.amd64 # Kernel config for x86_64
124124
│ └── defconfig.arm64 # Kernel config for aarch64
125-
├── scripts/
126-
│ ├── build-kernel.py # In-container kernel build entry point
127-
│ └── download-tools.py # In-container tool download entry point
128125
└── mkosi.extra/ # Files overlaid into the image
129126
├── init # Custom PID 1 (rootfs → tmpfs → systemd)
130127
└── etc/

build.py

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,6 @@
11
#!/usr/bin/env python3
22
"""CaptainOS build system entry point.
33
4-
Usage:
5-
./build.py # Build the image (stages: kernel → tools → initramfs)
6-
./build.py build # Same as above
7-
./build.py kernel # Build only the kernel + modules
8-
./build.py tools # Download tools (containerd, runc, nerdctl, CNI)
9-
./build.py initramfs # Build only the initramfs via mkosi
10-
./build.py shell # Drop into an interactive shell inside the builder
11-
./build.py clean # Remove build artifacts
12-
./build.py summary # Print mkosi configuration summary
13-
./build.py qemu-test # Boot the built image in QEMU for testing
14-
15-
Environment variables:
16-
ARCH Target architecture: amd64 (default) or arm64
17-
KERNEL_MODE Kernel build mode: docker (default), native, or skip
18-
MKOSI_MODE mkosi build mode: docker (default), native, or skip
19-
KERNEL_SRC Path to a local kernel source tree (optional, avoids download)
20-
KERNEL_VERSION Kernel version to build (default: 6.12.69)
21-
NO_CACHE Set to 1 to force Docker image rebuild without cache
22-
BUILDER_IMAGE Override the builder Docker image name (default: captainos-builder)
23-
FORCE_KERNEL Set to 1 to force kernel rebuild
24-
FORCE_TOOLS Set to 1 to re-download tools
25-
QEMU_APPEND Extra kernel cmdline args for qemu-test
26-
QEMU_MEM QEMU RAM size (default: 2G)
27-
QEMU_SMP QEMU CPU count (default: 2)
28-
29-
Tinkerbell kernel cmdline (qemu-test only, passed via /proc/cmdline):
30-
TINK_GRPC_AUTHORITY tink-server gRPC endpoint (host:port)
31-
TINK_DOCKER_REGISTRY Registry host (triggers tink-agent services)
32-
TINK_WORKER_IMAGE Full image ref (overrides TINK_DOCKER_REGISTRY)
33-
TINK_WORKER_ID Machine / worker ID (auto-detected when empty)
34-
TINK_TLS Enable TLS to tink-server (default: false)
35-
TINK_INSECURE_TLS Allow insecure TLS (default: true)
36-
TINK_INSECURE_REGISTRIES Comma-separated insecure registries
37-
TINK_REGISTRY_USERNAME Registry auth username
38-
TINK_REGISTRY_PASSWORD Registry auth password
39-
TINK_SYSLOG_HOST Remote syslog host
40-
TINK_FACILITY Facility code
41-
424
Requires: Python >= 3.10, Docker (unless all stages use native or skip)
435
"""
446

@@ -48,7 +10,12 @@
4810
print("ERROR: Python >= 3.10 is required.", file=sys.stderr)
4911
sys.exit(1)
5012

51-
from captain.cli import main
13+
try:
14+
from captain.cli import main
15+
except ImportError as exc:
16+
print(f"ERROR: {exc}", file=sys.stderr)
17+
print("Install dependencies: pip install -r requirements.txt", file=sys.stderr)
18+
sys.exit(1)
5219

5320
if __name__ == "__main__":
5421
main()

captain/artifacts.py

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def _human_size(size: int) -> str:
3232

3333

3434
def collect_kernel(cfg: Config, logger: StageLogger | None = None) -> None:
35-
"""Copy just the kernel image from mkosi.output/vmlinuz/{arch}/ to out/."""
35+
"""Copy the kernel image from mkosi.output/vmlinuz/{arch}/ to out/."""
3636
_log = logger or _default_log
3737
out = ensure_dir(cfg.output_dir)
3838
vmlinuz_dir = cfg.vmlinuz_output
@@ -46,13 +46,10 @@ def collect_kernel(cfg: Config, logger: StageLogger | None = None) -> None:
4646
_log.warn("No kernel image found in mkosi.output/vmlinuz/{arch}/")
4747

4848

49-
def collect(cfg: Config, logger: StageLogger | None = None) -> None:
50-
"""Copy initramfs and kernel images from mkosi.output/ to out/."""
49+
def collect_initramfs(cfg: Config, logger: StageLogger | None = None) -> None:
50+
"""Copy the initramfs CPIO from mkosi.output/initramfs/{arch}/ to out/."""
5151
_log = logger or _default_log
52-
_log.log("Collecting build artifacts...")
5352
out = ensure_dir(cfg.output_dir)
54-
55-
# Find the initrd CPIO output
5653
cpio_files = sorted(cfg.initramfs_output.glob("*.cpio*"))
5754
if cpio_files:
5855
initrd_src = cpio_files[0]
@@ -62,19 +59,11 @@ def collect(cfg: Config, logger: StageLogger | None = None) -> None:
6259
else:
6360
_log.warn("No initramfs CPIO found in mkosi.output/initramfs/{arch}/")
6461

65-
# Find the kernel image (stored outside ExtraTrees so it doesn't bloat
66-
# the initramfs — iPXE loads the kernel separately).
67-
vmlinuz_dir = cfg.vmlinuz_output
68-
vmlinuz_files = sorted(vmlinuz_dir.glob("vmlinuz-*")) if vmlinuz_dir.is_dir() else []
69-
if vmlinuz_files:
70-
vmlinuz_src = vmlinuz_files[0]
71-
vmlinuz_dst = out / f"vmlinuz-{cfg.arch}"
72-
shutil.copy2(vmlinuz_src, vmlinuz_dst)
73-
_log.log(f"kernel: {vmlinuz_dst} ({_human_size(vmlinuz_dst.stat().st_size)})")
74-
else:
75-
_log.warn("No kernel image found in mkosi.output/vmlinuz/{arch}/")
7662

77-
# Collect ISO if present (may not exist when ISO_MODE=skip)
63+
def collect_iso(cfg: Config, logger: StageLogger | None = None) -> None:
64+
"""Copy the ISO image from mkosi.output/iso/{arch}/ to out/."""
65+
_log = logger or _default_log
66+
out = ensure_dir(cfg.output_dir)
7867
iso_dir = cfg.iso_output
7968
iso_files = sorted(iso_dir.glob("*.iso")) if iso_dir.is_dir() else []
8069
if iso_files:
@@ -83,11 +72,26 @@ def collect(cfg: Config, logger: StageLogger | None = None) -> None:
8372
shutil.copy2(iso_src, iso_dst)
8473
_log.log(f"iso: {iso_dst} ({_human_size(iso_dst.stat().st_size)})")
8574

86-
# Print checksums
87-
artifacts = sorted(out.iterdir())
88-
if artifacts:
75+
76+
def collect_checksums(cfg: Config, logger: StageLogger | None = None) -> None:
77+
"""Print SHA-256 checksums for all artifacts in out/."""
78+
_log = logger or _default_log
79+
out = cfg.output_dir
80+
if not out.is_dir():
81+
return
82+
artifact_files = sorted(f for f in out.iterdir() if f.is_file())
83+
if artifact_files:
8984
_log.log("Checksums:")
90-
for artifact in artifacts:
91-
if artifact.is_file():
92-
digest = _sha256(artifact)
93-
print(f" {digest} {artifact}")
85+
for artifact in artifact_files:
86+
digest = _sha256(artifact)
87+
print(f" {digest} {artifact}")
88+
89+
90+
def collect(cfg: Config, logger: StageLogger | None = None) -> None:
91+
"""Copy initramfs, kernel, and ISO images from mkosi.output/ to out/."""
92+
_log = logger or _default_log
93+
_log.log("Collecting build artifacts...")
94+
collect_initramfs(cfg, logger=_log)
95+
collect_kernel(cfg, logger=_log)
96+
collect_iso(cfg, logger=_log)
97+
collect_checksums(cfg, logger=_log)

0 commit comments

Comments
 (0)