|
1 |
+ |
#!/usr/bin/env bash
|
|
2 |
+ |
#
|
|
3 |
+ |
# build-image.sh — build the Alloy bootc image and a bootable artifact
|
|
4 |
+ |
# (installer ISO by default) with bootc-image-builder.
|
|
5 |
+ |
#
|
|
6 |
+ |
# Alloy re-brands os-release (ID=alloy, VERSION_ID=0.0). Two consequences
|
|
7 |
+ |
# are handled in the Containerfile (pinned $releasever, disabled build-only
|
|
8 |
+ |
# repos) and one here: bootc-image-builder has no distro definition named
|
|
9 |
+ |
# "alloy-0.0", so build/alloy-0.0.yaml is bind-mounted into its defs dir.
|
|
10 |
+ |
#
|
|
11 |
+ |
# Everything runs rootful on purpose: the image build and bib share one
|
|
12 |
+ |
# container store, so bib finds the image at /var/lib/containers/storage
|
|
13 |
+ |
# without a rootless->rootful copy.
|
|
14 |
+ |
#
|
|
15 |
+ |
# Usage:
|
|
16 |
+ |
# build/build-image.sh # build image + installer ISO
|
|
17 |
+ |
# build/build-image.sh --type raw # build image + raw disk image
|
|
18 |
+ |
# build/build-image.sh --skip-build # reuse the current image, just run bib
|
|
19 |
+ |
# build/build-image.sh --write /dev/sdX # also dd the artifact to a device
|
|
20 |
+ |
#
|
|
21 |
+ |
# Writing requires an explicit device path and an interactive confirmation.
|
|
22 |
+ |
|
|
23 |
+ |
set -euo pipefail
|
|
24 |
+ |
|
|
25 |
+ |
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
26 |
+ |
IMAGE="localhost/alloy:local"
|
|
27 |
+ |
BIB_IMAGE="quay.io/centos-bootc/bootc-image-builder:latest"
|
|
28 |
+ |
DEF="$REPO_ROOT/build/alloy-0.0.yaml"
|
|
29 |
+ |
OUTPUT="$REPO_ROOT/output"
|
|
30 |
+ |
|
|
31 |
+ |
TYPE="iso"
|
|
32 |
+ |
WRITE_DEV=""
|
|
33 |
+ |
SKIP_BUILD=0
|
|
34 |
+ |
|
|
35 |
+ |
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
|
|
36 |
+ |
|
|
37 |
+ |
usage() {
|
|
38 |
+ |
sed -n '2,20p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
39 |
+ |
exit "${1:-0}"
|
|
40 |
+ |
}
|
|
41 |
+ |
|
|
42 |
+ |
while [ $# -gt 0 ]; do
|
|
43 |
+ |
case "$1" in
|
|
44 |
+ |
--type) TYPE="${2:?--type needs a value}"; shift 2 ;;
|
|
45 |
+ |
--write) WRITE_DEV="${2:?--write needs a device path}"; shift 2 ;;
|
|
46 |
+ |
--skip-build) SKIP_BUILD=1; shift ;;
|
|
47 |
+ |
-h|--help) usage 0 ;;
|
|
48 |
+ |
*) die "unknown argument: $1 (see --help)" ;;
|
|
49 |
+ |
esac
|
|
50 |
+ |
done
|
|
51 |
+ |
|
|
52 |
+ |
command -v podman >/dev/null || die "podman not found"
|
|
53 |
+ |
[ -f "$DEF" ] || die "missing distro def: $DEF"
|
|
54 |
+ |
[ -f "$REPO_ROOT/Containerfile" ] || die "no Containerfile at $REPO_ROOT"
|
|
55 |
+ |
|
|
56 |
+ |
# 1. Build the bootc image (rootful, so bib sees it in the same store).
|
|
57 |
+ |
if [ "$SKIP_BUILD" -eq 0 ]; then
|
|
58 |
+ |
echo "==> Building $IMAGE (rootful)"
|
|
59 |
+ |
sudo podman build -t "$IMAGE" "$REPO_ROOT"
|
|
60 |
+ |
else
|
|
61 |
+ |
echo "==> Skipping image build; reusing $IMAGE"
|
|
62 |
+ |
sudo podman image exists "$IMAGE" || die "$IMAGE not in the root store; drop --skip-build"
|
|
63 |
+ |
fi
|
|
64 |
+ |
|
|
65 |
+ |
# 2. Make sure the image builder is present.
|
|
66 |
+ |
sudo podman image exists "$BIB_IMAGE" || {
|
|
67 |
+ |
echo "==> Pulling $BIB_IMAGE"
|
|
68 |
+ |
sudo podman pull "$BIB_IMAGE"
|
|
69 |
+ |
}
|
|
70 |
+ |
|
|
71 |
+ |
# 3. Build the artifact. The alloy-0.0 def is mounted read-only into bib's
|
|
72 |
+ |
# defs directory; librepo (the default) resolves repos from the image.
|
|
73 |
+ |
echo "==> Building --type $TYPE into $OUTPUT"
|
|
74 |
+ |
mkdir -p "$OUTPUT"
|
|
75 |
+ |
sudo rm -rf "${OUTPUT:?}/"* 2>/dev/null || true
|
|
76 |
+ |
sudo podman run --rm --privileged \
|
|
77 |
+ |
--security-opt label=type:unconfined_t \
|
|
78 |
+ |
-v /var/lib/containers/storage:/var/lib/containers/storage \
|
|
79 |
+ |
-v "$OUTPUT":/output \
|
|
80 |
+ |
-v "$DEF":/usr/share/bootc-image-builder/defs/alloy-0.0.yaml:ro \
|
|
81 |
+ |
"$BIB_IMAGE" \
|
|
82 |
+ |
--type "$TYPE" \
|
|
83 |
+ |
--log-level info \
|
|
84 |
+ |
"$IMAGE"
|
|
85 |
+ |
|
|
86 |
+ |
# 4. Locate the produced artifact.
|
|
87 |
+ |
case "$TYPE" in
|
|
88 |
+ |
iso) ARTIFACT="$OUTPUT/bootiso/install.iso" ;;
|
|
89 |
+ |
raw) ARTIFACT="$OUTPUT/image/disk.raw" ;;
|
|
90 |
+ |
qcow2) ARTIFACT="$OUTPUT/qcow2/disk.qcow2" ;;
|
|
91 |
+ |
*) ARTIFACT="$(sudo find "$OUTPUT" -type f ! -name '*.json' | head -1)" ;;
|
|
92 |
+ |
esac
|
|
93 |
+ |
[ -n "$ARTIFACT" ] && sudo test -f "$ARTIFACT" || die "expected artifact not found for type $TYPE"
|
|
94 |
+ |
echo "==> Built: $ARTIFACT ($(sudo du -h "$ARTIFACT" | cut -f1))"
|
|
95 |
+ |
|
|
96 |
+ |
# 5. Optionally write to a device.
|
|
97 |
+ |
if [ -n "$WRITE_DEV" ]; then
|
|
98 |
+ |
[ -b "$WRITE_DEV" ] || die "$WRITE_DEV is not a block device"
|
|
99 |
+ |
# Refuse to write to a disk that carries a mounted filesystem (root disk guard).
|
|
100 |
+ |
if lsblk -nro MOUNTPOINT "$WRITE_DEV" | grep -qE '^/$|^/boot'; then
|
|
101 |
+ |
die "$WRITE_DEV has a system mountpoint; refusing to write"
|
|
102 |
+ |
fi
|
|
103 |
+ |
echo
|
|
104 |
+ |
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,MODEL,SERIAL,TRAN "$WRITE_DEV"
|
|
105 |
+ |
echo
|
|
106 |
+ |
printf 'This ERASES all data on %s. Type the device path to confirm: ' "$WRITE_DEV"
|
|
107 |
+ |
read -r reply
|
|
108 |
+ |
[ "$reply" = "$WRITE_DEV" ] || die "confirmation did not match; not writing"
|
|
109 |
+ |
echo "==> Writing $ARTIFACT to $WRITE_DEV"
|
|
110 |
+ |
sudo dd if="$ARTIFACT" of="$WRITE_DEV" bs=4M oflag=direct conv=fsync status=progress
|
|
111 |
+ |
sync
|
|
112 |
+ |
echo "==> Done. $WRITE_DEV is now a bootable Alloy $TYPE."
|
|
113 |
+ |
else
|
|
114 |
+ |
echo "==> To write it to a USB stick:"
|
|
115 |
+ |
echo " sudo dd if=$ARTIFACT of=/dev/sdX bs=4M oflag=direct conv=fsync status=progress"
|
|
116 |
+ |
echo " (or re-run with --write /dev/sdX to rebuild and write in one step)"
|
|
117 |
+ |
fi
|