-
Notifications
You must be signed in to change notification settings - Fork 1
/
arch-ostree
executable file
·474 lines (421 loc) · 12.6 KB
/
arch-ostree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
#!/usr/bin/env bash
# shellcheck disable=SC2154
set -euo pipefail
scriptdir="$(dirname "$(readlink -f "$0")")"
lib_dir="$scriptdir/lib"
share_dir="$scriptdir/share"
# shellcheck source=lib/common.sh
source "$lib_dir/common.sh"
builder_tag="archlinux-ostree-builder"
rootfs_tag="archlinux-ostree-rootfs"
ostree_repo="/sysroot/ostree/repo"
podman_build_cache=false
usage() {
cat <<EOF
USAGE: ${0##*/} [options] [command] [arg...]
<bool> is a value of 0, false, no, 1, true or yes.
COMMON OPTIONS:
--aur-dir
Path to the directory where built AUR packages are stored.
--builder-tag <name>
Name of the tag to use for the pacstrap container.
Default: archlinux-ostree-builder
--deploy-env-dir <path>
Path to the directory that's used as the deployment environment.
--ostree-repo <path>
Path to the ostree repository.
Default: /sysroot/ostree/repo
--podman-build-cache <bool>
Enable/Disable podman build cache. Boolean.
Default: no.
--pacman-cache <path>
Path to a persistent pacman package cache. This controls both podmans
--no-cache option and buildahs BUILDAH_LAYERS environment variable.
Default: undefined, thus the pacman cache is disabled.
--sysroot-dir
Path to the directory where your rootfs and all your partitions
(like boot or efi) are mounted.
--help
Print this help message
ROOTFS OPTIONS:
--rootfs-dir <path>
Path to the directory where the rootfs will be created in.
CONTAINER OPTIONS:
--rootfs-containerfile <path>
Path to the Containerfile used for building the rootfs container.
--rootfs-tag <name>
Name of the tag to use for the rootfs container.
Default: archlinux-ostree-rootfs
COMMON COMMANDS:
prepare_live_env
Prepare currently booted live ISO environment for setting up a new
ostree installation.
build_builder_container
(Re-)Build pacstrap container that's used by the other commands and tag
it with --builder-tag.
deploy_env -- [arg...]
Spawn a shell with a deployment at --deploy-env-dir as the rootfs.
This can be used to install the bootloader and create your first
deployment from the arch live ISO.
The shell runs inside a chroot that looks similar to a booted ostree
environment.
CONTAINER COMMANDS:
build_rootfs_container
Build rootfs container from --rootfs-containerfile and tag it with
--rootfs-tag.
commit_rootfs_container -- [arg...]
Commit --rootfs-tag container to --ostree-repo. This tool provides
--repo and --tree-dir to "ostree commit". Everything else
(like branch name) can and has to be provided by you.
enter_rootfs_container -- [arg...]
Enter --rootfs-tag container. All changes will be lost.
ROOTFS COMMANDS:
build_rootfs_directory <path>
Builds a rootfs directory at --rootfs-dir using the script-file at
<path>. See BUILD_SCRIPT_FORMAT for details on the format of that file.
<path> is deleted recursively before building the new rootfs.
commit_rootfs_directory -- [arg...]
Commit --rootfs-dir container to --ostree-repo. This tool provides
--repo and --tree-dir to ostree commit. Everything else
(like branch name) can and has to be provided by you.
enter_rootfs_directory -- [arg...]
Enter --rootfs-dir. All changes will be lost.
pacstrap_rootfs_directory [pkg...]
Run pacstrap on --rootfs-dir. If the directory exists, it will NOT be
deleted. [pkg...] will be passed to pacstrap and must contain the
packages that shall be installed.
BUILD_SCRIPT FORMAT:
A build script is a shell-script that's sourced by another shell-script.
It can be used to define everything that's needed to build your rootfs in a
single file.
This works similar to PKGBUILD in that it has to define certain variables
and functions. The script can be sourced multiple times and into different
environments(like containers). So don't expect any variables or files to
stay around in between function calls.
VARIABLES:
packages
A bash array that contains the packages you want to install using
pacstrap.
aur_packages
A bash array that contains the packages you want to install from the
AUR. They are built inside a clean --builder-tag container, so you
don't need to worry about leaving behind trash in your actual rootfs.
Currently, they are build manually using makepkg, so you have to make
sure to include dependent packages that are only available in the AUR
as well. The packages are built in the order they are defined.
FUNCTIONS:
prepare
This is called after creating the rootfs directory, but before calling
pacstrap. It can be used to copy files like mkinitcpio.conf that will
be already be used by pacstrap.
It's called from the environment that arch-ostree runs in. The path to
the rootfs directory is stored in the variable "rootfs".
post_install_early
This is called after pacstrap inside a container that uses the
pacstrapped directory as it's rootfs. This can be used to write the
mirrorlist or init and populate the pacman keys which may be needed if
you want to install AUR packages(which are installed after this step).
post_install
Called after both post_install_early and installing AUR packages. This
can be used for common post installation steps like generating locales
or enabling systemd services.
EOF
}
prepare_live_env() {
msg "Install required packages"
pacman \
--needed \
--noconfirm \
-S \
ostree \
podman
msg "Patch storage.conf"
sed -i \
-e 's|^\(graphroot\s*=\s*\).*|\1"/mnt/setup/container-storage"|g' \
/etc/containers/storage.conf
msg "Patch containers.conf"
sed -i \
-e 's|^# \(image_copy_tmp_dir\s*=\s*\).*|\1"/mnt/setup/container-tmp"|g' \
/etc/containers/containers.conf
mkdir -p "/mnt/setup/container-tmp"
}
pacstrap_rootfs_directory() {
# Without the increased pids-limit, pacstrap may fail at verifying
# package integrity.
podman run \
--pids-limit 4096 \
--cap-add sys_admin \
--cap-add mknod \
--security-opt apparmor=unconfined \
--net=host \
"${pacman_cache_args[@]}" \
-v "$rootfs_dir:/mnt" \
--rm -it \
"localhost/$builder_tag" \
pacstrap -c -G -M /mnt "$@"
}
deploy_env() {
# We want to mount everything that the user has mounted at
# `sysroot_dir`(usually /mnt). This gives us all of the bootloader
# partitions.
findmnt_output="$(findmnt \
--all \
--real \
--submounts \
--mountpoint "$sysroot_dir" \
--list \
--noheadings \
--output TARGET \
| tail -n +2)"
mkdir -p "$deploy_env_dir/etc"
mount -o ro,bind "$deploy_env_dir" "$deploy_env_dir"
mount -o ro,bind "$deploy_env_dir/usr/etc" "$deploy_env_dir/etc"
for path in /run /tmp /var; do
mount -t tmpfs tmpfs "${deploy_env_dir}${path}"
done
for path in home mnt roothome usrlocal srv opt; do
mkdir "$deploy_env_dir/var/$path"
done
for path in /dev /proc /sys; do
mount --rbind "$path" "${deploy_env_dir}${path}"
done
mount -o rw,bind "$sysroot_dir" "${deploy_env_dir}/sysroot"
mounted_boot=false
if [ -n "$findmnt_output" ]; then
while read -r source; do
target="$(realpath --relative-to="$sysroot_dir" "$source")"
mount -o bind "$source" "${deploy_env_dir}/$target"
if [ "$target" = "boot" ]; then
mounted_boot=true
fi
done <<< "$findmnt_output"
fi
# In case there is no separate boot partition we need to
# bind-mount the boot directory so a `grub-install` within the
# deploy env installs to the actual boot directory outside of
# the temporary deployment rootfs.
if [ -e "$sysroot_dir/boot" ] && [ $mounted_boot = false ]; then
mount --mkdir -o bind "$sysroot_dir/boot" "$deploy_env_dir/boot"
fi
chroot "$deploy_env_dir" "${args[@]}"
}
build_rootfs_directory() {
rootfs="$rootfs_dir"
build_script="$(realpath "$1")"
build_scriptdir=$(dirname "$build_script")
# shellcheck disable=SC1090
source "$build_script"
post_install_args=()
# shellcheck disable=SC2236 # This doesn't work with -n
if [ ! -z "${aur_packages+x}" ]; then
msg "Build AUR packages"
if [ -d "$aur_dir" ]; then
rm -r "$aur_dir"
fi
mkdir "$aur_dir"
podman run \
"${pacman_cache_args[@]}" \
--net=host \
--rm -it \
-v "$aur_dir:/tmp/aur" \
-v "$lib_dir/build_aur_packages:/tmp/script" \
"$builder_tag" \
/tmp/script \
"${aur_packages[@]}"
post_install_args+=(-v "$aur_dir:/tmp/aur")
fi
rm -rf "$rootfs"
if [ -e "$rootfs" ]; then
die "Failed to delete rootfs directory"
fi
if have_function prepare; then
msg "Prepare"
pushd "$build_scriptdir"
prepare
popd
fi
msg "Pacstrap"
pacstrap_rootfs_directory "${packages[@]}"
msg "Post-install"
podman run \
"${post_install_args[@]}" \
--net=host \
--rm -it \
--tmpfs /tmp \
--tmpfs /run \
-v "$build_scriptdir:/tmp/work" \
-v "$build_script:/tmp/script" \
-v "$lib_dir/common.sh:/tmp/common.sh" \
-v "$lib_dir/build_rootfs_post_install:/tmp/entrypoint" \
-w /tmp/work \
--entrypoint /tmp/entrypoint \
--rootfs "$rootfs"
}
long_opts=(
aur-dir:
builder-tag:
deploy-env-dir:
help
ostree-repo:
pacman-cache:
podman-build-cache:
rootfs-containerfile:
rootfs-dir:
rootfs-tag:
sysroot-dir:
)
if ! temp=$(getopt -o '' --long "$(join_by , "${long_opts[@]}")" -- "$@"); then
die "Invalid arguments"
fi
eval set -- "$temp"
while true; do
case "$1" in
'--builder-tag'|\
'--rootfs-tag')
name="$(arg_to_varname "$1")"
printf -v "$name" "%s" "$2"
shift 2
continue
;;
'--aur-dir'|\
'--deploy-env-dir'|\
'--ostree-repo'|\
'--pacman-cache'|\
'--rootfs-containerfile'|\
'--rootfs-dir'|\
'--sysroot-dir')
name="$(arg_to_varname "$1")"
value="$(realpath "$2")"
printf -v "$name" "%s" "$value"
shift 2
continue
;;
'--podman-build-cache')
name="$(arg_to_varname "$1")"
case "$2" in
'true'|'1'|'yes')
eval "$name=true"
;;
'false'|'0'|'no')
eval "$name=false"
;;
*)
die "Unsupported bool value: $2"
;;
esac
shift 2
continue
;;
'--help')
usage
exit $(( $# ? 0 : 1 ))
;;
'--')
shift
break
;;
*)
die "BUG: Unexpected argument: $1"
;;
esac
done
if [ -z ${1+x} ]; then
die "Missing command argument"
fi
command="${1}"
shift 1
pacman_cache_args=()
# shellcheck disable=SC2236 # This doesn't work with -n
if [ ! -z ${pacman_cache+x} ]; then
mkdir -p "$pacman_cache"
pacman_cache_args=(
-v "$pacman_cache:/var/cache/pacman/pkg"
)
fi
podman_build_cache_args=()
if [ "$podman_build_cache" = false ]; then
podman_build_cache_args+=(--no-cache)
export BUILDAH_LAYERS=false
fi
case "$command" in
'prepare_live_env')
prepare_live_env
;;
'build_builder_container')
podman build \
--net=host \
"${podman_build_cache_args[@]}" \
-f "$share_dir/Containerfile.builder" \
-t "$builder_tag"
;;
'build_rootfs_container')
podman build \
--net=host \
"${podman_build_cache_args[@]}" \
"${pacman_cache_args[@]}" \
--cap-add sys_admin \
--cap-add mknod \
-f "$rootfs_containerfile" \
-t "$rootfs_tag"
;;
'enter_rootfs_container')
podman run \
--net=host \
--rm -it \
"localhost/$rootfs_tag" \
"$@"
;;
'commit_rootfs_container')
podman run \
--net=host \
--cap-add sys_admin \
--security-opt apparmor=unconfined \
--mount "type=image,src=localhost/$rootfs_tag,dst=/mnt-lower" \
--mount "type=bind,src=$ostree_repo,dst=/sysroot/ostree/repo" \
-v "$lib_dir:/opt/lib:ro" \
-v "$share_dir:/opt/share:ro" \
--rm -it \
"localhost/$builder_tag" \
/opt/lib/commit_rootfs "$@"
;;
'pacstrap_rootfs_directory')
if [ -d "$rootfs_dir" ]; then
warning "rootfs directory exists already."
else
mkdir -p "$rootfs_dir"
fi
pacstrap_rootfs_directory "$@"
;;
'build_rootfs_directory')
build_rootfs_directory "$@"
;;
'enter_rootfs_directory')
podman run \
--net=host \
--rm -it \
--rootfs "$rootfs_dir:O" \
/bin/bash "$@"
;;
'commit_rootfs_directory')
podman run \
--net=host \
--cap-add sys_admin \
--security-opt apparmor=unconfined \
--mount "type=bind,src=$rootfs_dir,dst=/mnt-lower,ro" \
--mount "type=bind,src=$ostree_repo,dst=/sysroot/ostree/repo" \
-v "$lib_dir:/opt/lib:ro" \
-v "$share_dir:/opt/share:ro" \
--tmpfs /overlay \
--rm -it \
"localhost/$builder_tag" \
/opt/lib/commit_rootfs "$@"
;;
'deploy_env')
args=("$@")
unshare -m bash -c "set -euo pipefail; $(declare_all); deploy_env"
;;
*)
die "Unsupported command: ${command}"
;;
esac
msg "Successful"