Skip to content

Commit 7982971

Browse files
jrtoddyahesford
authored andcommitted
zbm-builder.sh: support custom build/source paths and hooks
* Build from a local source tree with `-l` * Override build directory with `-b` * Include hooks in $BUILD_DIRECTORY/hooks.{early_setup,setup,teardown}.d * Update command-line documentation and docs/BUILD.md Co-authored-by: James R. Todd <[email protected]> Co-authored-by: Andrew J. Hesford <[email protected]> Closes: #282.
1 parent fc83894 commit 7982971

File tree

3 files changed

+152
-53
lines changed

3 files changed

+152
-53
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
*.swp
33
*/*.swp
44
releases/*
5+
build

docs/BUILD.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,25 @@ The default behavior of `zbm-builder.sh` will:
6666
2. If `./config.yaml` exists, inform the builder to use that custom configuration instead of the default
6767
3. Run the internal build script to produce output in the `./build` subdirectory
6868

69-
### Customizing Images
69+
### Custom ZFSBootMenu Hooks
7070

71-
A custom `config.yaml` may be provided in the working directory to override the
72-
default ZFSBootMenu configuration. The build container runs its build script
73-
from the working directory on the host. Therefore, relative paths in a custom
74-
`config.yaml` will be interpreted relative to the working directory when
75-
`zbm-builder.sh` is invoked.
71+
ZFSBootMenu supports [custom hooks](pod/zfsbootmenu.7.pod#options-for-dracut) in three stages:
72+
73+
1. `early_setup` hooks run after the `zfs` kernel driver has been loaded, but before ZFSBootMenu attempts to import any pools.
74+
2. `setup` hooks run after pools are imported, right before ZFSBootMenu will either boot a default environment or present a menu.
75+
3. `teardown` hooks run immediately before ZFSBootMenu will `kexec` the kernel for the selected environment.
76+
77+
When `zbm-builder.sh` runs, it will identify custom hooks as executable files in the respective subdirectories of its build directory:
78+
79+
1. `hooks.early_setup.d`
80+
2. `hooks.setup.d`
81+
3. `hooks.teardown.d`
82+
83+
For each hook directory that contains at least one executable file, `zbm-builder.sh` will write custom configuration snippets for `dracut` and `mkinitcpio` that will include these files in the output images.
84+
85+
### Fully Customizing Images
86+
87+
A custom `config.yaml` may be provided in the working directory to override the default ZFSBootMenu configuration. The build container runs its build script from the working directory on the host. Therefore, relative paths in a custom `config.yaml` will be interpreted relative to the working directory when `zbm-builder.sh` is invoked.
7688

7789
> The internal build script **always** overrides the output paths for ZFSBootMenu components and UEFI executables to ensure that the images will reside in the `./build` subdirectory upon completion. Relative paths are primarily useful for specifying local `dracut` or `mkinitcpio` configuration paths.
7890

zbm-builder.sh

Lines changed: 133 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,103 @@
11
#!/bin/bash
22
# vim: softtabstop=2 shiftwidth=2 expandtab
33

4+
sanitise_path() {
5+
local rpath
6+
if rpath="$(readlink -f "${1}")" && [ -d "${rpath}" ]; then
7+
echo "${rpath}"
8+
return 0
9+
fi
10+
11+
return 1
12+
}
13+
414
usage() {
5-
cat <<EOF
15+
cat << EOF
16+
Build ZFSBootMenu images in an OCI container using podman or docker.
17+
618
Usage: $0 [options]
7-
OPTIONS:
8-
-h Display help text
919
10-
-L Use local './' source tree instead of remote.
11-
(Default: upstream master.)
20+
OPTIONS:
1221
13-
-H Do not copy /etc/hostid into image
14-
(Has no effect if ./hostid exists)
22+
-h Display this help text
1523
16-
-C Do not copy /etc/zfs/zpool.cache into image
17-
(Has no effect if ./zpool.cache exists)
24+
-b <path>
25+
Use an alternate build directory
26+
(Default: current directory)
1827
1928
-d Force use of docker instead of podman
2029
30+
-i <image>
31+
Build within the named container image
32+
(Default: ghcr.io/zbm-dev/zbm-builder:latest)
33+
34+
-l <path>
35+
Build from ZFSBootMenu source tree at <path>
36+
(Default: fetch upstream source tree inside container)
37+
2138
-t <tag>
22-
Build the given ZFSBootMenu tag
23-
(Default: upstream master)
39+
Build specific ZFSBootMenu commit or tag (e.g. v1.12.0, d5594589)
40+
(Default: current upstream master)
2441
25-
-B <tag>
26-
Use the given zbm-builder tag to build images
27-
(Default: ghcr.io/zbm-dev/zbm-builder:latest)
42+
-C Do not include host /etc/zfs/zpool.cache in image
43+
(If ./zpool.cache exists, this switch will be ignored)
44+
45+
-H Do not include host /etc/hostid in image
46+
(If ./hostid exists, this switch will be ignored)
47+
48+
For more information, see documentation at
49+
50+
https://github.com/zbm-dev/zfsbootmenu/blob/master/README.md
51+
https://github.com/zbm-dev/zfsbootmenu/blob/master/docs/BUILD.md
2852
EOF
2953
}
3054

31-
if command -v podman >/dev/null 2>&1; then
32-
PODMAN=podman
33-
elif command -v docker >/dev/null 2>&1; then
34-
PODMAN=docker
35-
fi
36-
3755
SKIP_HOSTID=
3856
SKIP_CACHE=
3957

40-
BUILD_TAG="ghcr.io/zbm-dev/zbm-builder:latest"
58+
# By default, use the latest upstream build container image
59+
BUILD_IMG="ghcr.io/zbm-dev/zbm-builder:latest"
60+
61+
# By default, build from the current directory
62+
BUILD_DIRECTORY="${PWD}"
4163

42-
BUILD_ARGS=()
43-
VOLUME_ARGS=("-v" "${PWD}:/build")
64+
# By default, there is no local repo or repo tag
65+
BUILD_REPO=
66+
BUILD_TAG=
4467

45-
while getopts "hHLCdt:B:" opt; do
68+
if command -v podman >/dev/null 2>&1; then
69+
PODMAN="podman"
70+
else
71+
PODMAN="docker"
72+
fi
73+
74+
while getopts "b:dhi:l:t:CH" opt; do
4675
case "${opt}" in
47-
L)
48-
VOLUME_ARGS+=("-v" "${PWD}:/zbm")
49-
;;
50-
H)
51-
SKIP_HOSTID="yes"
52-
;;
53-
C)
54-
SKIP_CACHE="yes"
76+
b)
77+
BUILD_DIRECTORY="${OPTARG}"
5578
;;
5679
d)
5780
PODMAN=docker
5881
;;
59-
t)
60-
BUILD_ARGS+=( "-t" "${OPTARG}" )
61-
;;
62-
B)
63-
BUILD_TAG="${OPTARG}"
64-
;;
6582
h)
6683
usage
6784
exit 0
6885
;;
86+
i)
87+
BUILD_IMG="${OPTARG}"
88+
;;
89+
l)
90+
BUILD_REPO="${OPTARG}"
91+
;;
92+
t)
93+
BUILD_TAG="${OPTARG}"
94+
;;
95+
C)
96+
SKIP_CACHE="yes"
97+
;;
98+
H)
99+
SKIP_HOSTID="yes"
100+
;;
69101
*)
70102
usage
71103
exit 1
@@ -74,14 +106,39 @@ while getopts "hHLCdt:B:" opt; do
74106
done
75107

76108
if ! command -v "${PODMAN}" >/dev/null 2>&1; then
77-
echo "ERROR: container front-end ${PODMAN} not found"
109+
echo "ERROR: this script requires podman or docker"
110+
exit 1
111+
fi
112+
113+
# Always mount a build directory at /build
114+
if ! BUILD_DIRECTORY="$( sanitise_path "${BUILD_DIRECTORY}" )"; then
115+
echo "ERROR: build directory does not exist"
78116
exit 1
79117
fi
80118

119+
VOLUME_ARGS=( "-v" "${BUILD_DIRECTORY}:/build" )
120+
121+
# Only mount a local repo at /zbm if specified
122+
if [ -n "${BUILD_REPO}" ]; then
123+
if ! BUILD_REPO="$( sanitise_path "${BUILD_REPO}" )"; then
124+
echo "ERROR: local repository does not exist"
125+
exit 1
126+
fi
127+
128+
VOLUME_ARGS+=( "-v" "${BUILD_REPO}:/zbm:ro" )
129+
fi
130+
131+
# If a tag was specified for building, pass to the container
132+
if [ -n "${BUILD_TAG}" ]; then
133+
BUILD_ARGS=( "-t" "${BUILD_TAG}" )
134+
else
135+
BUILD_ARGS=()
136+
fi
137+
81138
# If no local hostid is available, copy the system hostid if desired
82-
if ! [ -r ./hostid ]; then
139+
if ! [ -r "${BUILD_DIRECTORY}"/hostid ]; then
83140
if [ "${SKIP_HOSTID}" != "yes" ] && [ -r /etc/hostid ]; then
84-
if ! cp /etc/hostid ./hostid; then
141+
if ! cp /etc/hostid "${BUILD_DIRECTORY}"/hostid; then
85142
echo "ERROR: unable to copy /etc/hostid"
86143
echo "Copy a hostid file to ./hostid or use -H to disable"
87144
exit 1
@@ -90,21 +147,50 @@ if ! [ -r ./hostid ]; then
90147
fi
91148

92149
# If no local zpool.cache is available, copy the system cache if desired
93-
if ! [ -r ./zpool.cache ]; then
150+
if ! [ -r "${BUILD_DIRECTORY}"/zpool.cache ]; then
94151
if [ "${SKIP_CACHE}" != "yes" ] && [ -r /etc/zfs/zpool.cache ]; then
95-
if ! cp /etc/zfs/zpool.cache ./zpool.cache; then
152+
if ! cp /etc/zfs/zpool.cache "${BUILD_DIRECTORY}"/zpool.cache; then
96153
echo "ERROR: unable to copy /etc/zfs/zpool.cache"
97154
echo "Copy a zpool cache to ./zpool.cache or use -C to disable"
98155
exit 1
99156
fi
100157
fi
101158
fi
102159

103-
# If no config is specified, use in-tree default
104-
if ! [ -r ./config.yaml ]; then
160+
# If no config is specified, use in-tree default but force EFI and components
161+
if ! [ -r "${BUILD_DIRECTORY}"/config.yaml ]; then
105162
BUILD_ARGS+=( "-c" "/zbm/etc/zfsbootmenu/config.yaml" )
163+
BUILD_ARGS+=( "-e" ".EFI.Enabled=true" )
164+
BUILD_ARGS+=( "-e" ".Components.Enabled=true" )
106165
fi
107166

108-
# Make `/build` the working directory so relative paths in a config file make sense
109-
"${PODMAN}" run --rm "${VOLUME_ARGS[@]}" -w "/build" "${BUILD_TAG}" "${BUILD_ARGS[@]}"
167+
# Try to include ZBM hooks in the images by default
168+
for stage in early_setup setup teardown; do
169+
[ -d "${BUILD_DIRECTORY}/hooks.${stage}.d" ] || continue
170+
171+
# Only executable hooks are added to the image
172+
hooks=()
173+
for f in "${BUILD_DIRECTORY}/hooks.${stage}.d"/*; do
174+
[ -x "${f}" ] || continue
175+
hooks+=( "/build/hooks.${stage}.d/${f##*/}" )
176+
done
177+
178+
[ "${#hooks[@]}" -gt 0 ] || continue
179+
180+
hconf="zbm-builder.${stage}.conf"
181+
182+
# Write a dracut configuration snippet
183+
mkdir -p "${BUILD_DIRECTORY}/dracut.conf.d"
184+
echo "zfsbootmenu_${stage}+=\" ${hooks[*]} \"" > "${BUILD_DIRECTORY}/dracut.conf.d/${hconf}"
185+
186+
# Write a mkinitcpio configuration snippet
187+
mkdir -p "${BUILD_DIRECTORY}/mkinitcpio.conf.d"
188+
echo "zfsbootmenu_${stage}=(" > "${BUILD_DIRECTORY}/mkinitcpio.conf.d/${hconf}"
189+
for hook in "${hooks[@]}"; do
190+
echo " \"${hook}\"" >> "${BUILD_DIRECTORY}/mkinitcpio.conf.d/${hconf}"
191+
done
192+
echo ")" >> "${BUILD_DIRECTORY}/mkinitcpio.conf.d/${hconf}"
193+
done
110194

195+
# Make `/build` the working directory so relative paths in configs make sense
196+
exec "${PODMAN}" run --rm "${VOLUME_ARGS[@]}" -w "/build" "${BUILD_IMG}" "${BUILD_ARGS[@]}"

0 commit comments

Comments
 (0)