Skip to content

Commit 3065d01

Browse files
committed
Merge branch 'feat/command-option'
2 parents 22013dc + 9cf8b4d commit 3065d01

8 files changed

Lines changed: 148 additions & 6 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ Edit `/etc/atomic.conf`:
166166
| `MAPPER_NAME` | `root_crypt` | dm-crypt mapper name (fallback if auto-detection fails) |
167167
| `KERNEL_PKG` | `linux` | Kernel package (linux/linux-lts/linux-zen) |
168168
| `KERNEL_PARAMS` | *(security defaults)* | Kernel command line parameters |
169+
| `CHROOT_COMMAND` | *(none)* | Default command in snapshot chroot (overrides built-in `pacman -Syu`) |
169170
| `SBCTL_SIGN` | `0` | Sign UKI files with sbctl for Secure Boot (`0`=off, `1`=on) |
170171
| `UPGRADE_GUARD` | `1` | Upgrade guard: block direct `pacman -Syu` (`0`=off, `1`=on) |
171172
| `HOME_COPY_FILES` | *(empty)* | Files to copy into isolated home subvolumes (see [Home isolation](#home-isolation)) |

‎bin/atomic-upgrade‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ show_help() {
1818
cat << EOF
1919
atomic-upgrade v${VERSION} - Atomic system upgrades for Arch Linux
2020
21-
Usage: atomic-upgrade [OPTIONS] [-- COMMAND...]
21+
Usage: atomic-upgrade [OPTIONS] [-- CHROOT_COMMAND...]
2222
2323
Options:
2424
-n, --dry-run Show what would be done
@@ -106,9 +106,18 @@ if [[ -n "$CUSTOM_TAG" ]]; then
106106
fi
107107
fi
108108

109-
# Default chroot command
109+
# Default chroot command: use CHROOT_COMMAND from config (or default in common.sh).
110+
# Note: CHROOT_COMMAND uses simple word splitting; args with spaces inside
111+
# are not supported (same limitation as HOME_COPY_FILES).
110112
if [[ ${#CHROOT_CMD[@]} -eq 0 ]]; then
111-
CHROOT_CMD=(/usr/bin/pacman -Syu)
113+
[[ -z "${CHROOT_COMMAND:-}" ]] && CHROOT_COMMAND="/usr/bin/pacman -Syu"
114+
read -ra CHROOT_CMD <<< "$CHROOT_COMMAND"
115+
fi
116+
117+
# Warn if CHROOT_COMMAND contains quotes (word splitting doesn't process them)
118+
if [[ "$CHROOT_COMMAND" =~ [\'\"] ]]; then
119+
echo "WARN: CHROOT_COMMAND contains quotes, but word splitting doesn't process them." >&2
120+
echo "WARN: Use CLI instead: atomic-upgrade -- pacman -S 'nvidia dkms'" >&2
112121
fi
113122

114123
# Verify required variables are set

‎etc/atomic.conf‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# /etc/atomic.conf
2+
3+
# Note: inline comments start at ' #' (space + hash).
4+
# Values containing a literal ' #' will be truncated at that point.
5+
26
#BTRFS_MOUNT="/run/atomic/temp_root"
37
#NEW_ROOT="/run/atomic/newroot"
48
#ESP="/efi"
@@ -7,8 +11,10 @@
711
#KERNEL_PKG="linux"
812
#KERNEL_PARAMS="rw slab_nomerge init_on_alloc=1 page_alloc.shuffle=1 pti=on vsyscall=none randomize_kstack_offset=on debugfs=off"
913

10-
# Note: inline comments start at ' #' (space + hash).
11-
# Values containing a literal ' #' will be truncated at that point.
14+
# Default command to run in the snapshot chroot (overrides built-in default)
15+
# Command-line -- CHROOT_COMMAND... overrides this setting
16+
# Note: args with spaces inside are not supported (simple word splitting)
17+
#CHROOT_COMMAND="/usr/bin/pacman -Syu"
1218

1319
# Sign UKI files with sbctl for Secure Boot (requires sbctl)
1420
#SBCTL_SIGN=1

‎lib/atomic/common.sh‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ UPGRADE_GUARD=1
2929
HOME_COPY_FILES=""
3030
# Kernel security parameters
3131
KERNEL_PARAMS="rw slab_nomerge init_on_alloc=1 page_alloc.shuffle=1 pti=on vsyscall=none randomize_kstack_offset=on debugfs=off"
32+
# Default chroot command (overridden by config or CLI -- CHROOT_COMMAND...)
33+
CHROOT_COMMAND="/usr/bin/pacman -Syu"
3234

3335
# ── Config loading (safe parser, no arbitrary code execution) ───────
3436

@@ -46,7 +48,7 @@ load_config() {
4648
fi
4749

4850
# Whitelist of allowed config keys
49-
local -a allowed=(BTRFS_MOUNT NEW_ROOT ESP KEEP_GENERATIONS MAPPER_NAME KERNEL_PARAMS KERNEL_PKG SBCTL_SIGN UPGRADE_GUARD HOME_COPY_FILES)
51+
local -a allowed=(BTRFS_MOUNT NEW_ROOT ESP KEEP_GENERATIONS MAPPER_NAME KERNEL_PARAMS KERNEL_PKG CHROOT_COMMAND SBCTL_SIGN UPGRADE_GUARD HOME_COPY_FILES)
5052

5153
while IFS='=' read -r key value; do
5254
# Strip leading/trailing whitespace from key

‎man/atomic.conf.5‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ device arguments.
7171
Default: \f[B]rw slab_nomerge init_on_alloc=1 page_alloc.shuffle=1
7272
pti=on vsyscall=none randomize_kstack_offset=on debugfs=off\f[R].
7373
.TP
74+
\f[B]COMMAND\f[R]
75+
Default command to run in the snapshot chroot, overriding the built\-in
76+
default of \f[B]pacman \-Syu\f[R].
77+
Command\-line \f[B]\-\-\f[R] \f[I]COMMAND...\f[R] takes priority over
78+
this setting.
79+
Note: arguments with spaces inside are not supported due to simple word
80+
splitting.
81+
Default: \f[I](none)\f[R].
82+
.TP
7483
\f[B]SBCTL_SIGN\f[R]
7584
Enable UKI signing with \f[B]sbctl\f[R](8) for Secure Boot.
7685
Set to \f[B]1\f[R] to enable, \f[B]0\f[R] to disable.

‎man/atomic.conf.5.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ rejected entirely.
6666
page_alloc.shuffle=1 pti=on vsyscall=none randomize_kstack_offset=on
6767
debugfs=off**.
6868

69+
**CHROOT_COMMAND**
70+
: Default command to run in the snapshot chroot, overriding the built-in
71+
default of **pacman -Syu**. Command-line **-- CHROOT_COMMAND...** takes priority
72+
over this setting. Note: arguments with spaces inside are not supported
73+
due to simple word splitting. Default: *(none)*.
74+
6975
**SBCTL_SIGN**
7076
: Enable UKI signing with **sbctl**(8) for Secure Boot. Set to **1** to
7177
enable, **0** to disable. When disabled, UKI files are built unsigned.

‎tests/test_config.sh‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,40 @@ assert_eq "HOME_COPY_FILES absent → stays empty" "" "$HOME_COPY_FILES"
276276
# Restore
277277
HOME_COPY_FILES=""
278278

279+
# ── CHROOT_COMMAND config ───────────────────────────────────
279280

281+
section "CHROOT_COMMAND config"
282+
283+
# Test: CHROOT_COMMAND loaded from config
284+
CONFIG_FILE="${TESTDIR}/chroot_command.conf"
285+
cat > "$CONFIG_FILE" <<'EOF'
286+
CHROOT_COMMAND=/usr/bin/pacman -Syu
287+
EOF
288+
CHROOT_COMMAND=""
289+
load_config
290+
assert_eq "CHROOT_COMMAND loaded from config" "/usr/bin/pacman -Syu" "$CHROOT_COMMAND"
291+
CHROOT_COMMAND=""
292+
293+
# Test: CHROOT_COMMAND with quotes
294+
CONFIG_FILE="${TESTDIR}/chroot_command_quoted.conf"
295+
cat > "$CONFIG_FILE" <<'EOF'
296+
CHROOT_COMMAND="/usr/bin/pacman -S nvidia"
297+
EOF
298+
CHROOT_COMMAND=""
299+
load_config
300+
assert_eq "CHROOT_COMMAND with double quotes" "/usr/bin/pacman -S nvidia" "$CHROOT_COMMAND"
301+
CHROOT_COMMAND=""
302+
303+
# Test: CHROOT_COMMAND absent → stays empty
304+
CONFIG_FILE="${TESTDIR}/chroot_command_absent.conf"
305+
cat > "$CONFIG_FILE" <<'EOF'
306+
KEEP_GENERATIONS=3
307+
EOF
308+
CHROOT_COMMAND=""
309+
load_config
310+
assert_eq "CHROOT_COMMAND absent → stays empty" "" "$CHROOT_COMMAND"
311+
312+
#
280313
# ── config key whitespace trimming ─────────────
281314

282315
section "Config key whitespace trimming"

‎tests/test_upgrade.sh‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,82 @@ assert_eq "default cmd count" "CHROOT_CMD_COUNT=2" "$(echo "$_out" | grep '^CHRO
196196
assert_eq "default cmd[0]" "CHROOT_CMD_0=/usr/bin/pacman" "$(echo "$_out" | grep '^CHROOT_CMD_0=')"
197197
assert_eq "default cmd[1]" "CHROOT_CMD_1=-Syu" "$(echo "$_out" | grep '^CHROOT_CMD_1=')"
198198

199+
# ── Word splitting limitation: args with spaces ────────────
200+
201+
section "Word splitting limitation (space in args)"
202+
203+
CONFIG_FILE="${TESTDIR}/word_split.conf"
204+
cat > "$CONFIG_FILE" <<'EOF'
205+
CHROOT_COMMAND=/usr/bin/pacman -S nvidia driver
206+
EOF
207+
208+
_TEST_SCRIPT_WS="${TESTDIR}/atomic-upgrade-word-split"
209+
sed \
210+
-e 's|^\(export ATOMIC_UPGRADE=1\)$|\1\nCONFIG_FILE="${CONFIG_FILE:-/etc/atomic.conf}"|' \
211+
-e 's/^\(\[\[ \$EUID -eq 0 \]\]\)/# \1/' \
212+
-e '/^# Verify required variables are set$/i\
213+
if [[ "${ATOMIC_EXIT_AFTER_PARSE:-}" == "1" ]]; then\
214+
echo "PARSE_OK"\
215+
echo "CHROOT_CMD_COUNT=${#CHROOT_CMD[@]}"\
216+
for i in "${!CHROOT_CMD[@]}"; do\
217+
echo "CHROOT_CMD_${i}=${CHROOT_CMD[$i]}"\
218+
done\
219+
exit 0\
220+
fi\
221+
' \
222+
"$SCRIPT" > "$_TEST_SCRIPT_WS"
223+
chmod +x "$_TEST_SCRIPT_WS"
224+
225+
run_cmd env CONFIG_FILE="$CONFIG_FILE" ATOMIC_EXIT_AFTER_PARSE=1 bash "$_TEST_SCRIPT_WS"
226+
assert_eq "word split: PARSE_OK" "0" "$_rc"
227+
assert_eq "word split: count is 4" "CHROOT_CMD_COUNT=4" "$(echo "$_out" | grep '^CHROOT_CMD_COUNT=')"
228+
assert_eq "word split: arg[2]" "CHROOT_CMD_2=nvidia" "$(echo "$_out" | grep '^CHROOT_CMD_2=')"
229+
assert_eq "word split: arg[3]" "CHROOT_CMD_3=driver" "$(echo "$_out" | grep '^CHROOT_CMD_3=')"
230+
231+
rm -f "$_TEST_SCRIPT_WS"
232+
233+
# ── CLI -- CHROOT_COMMAND priority over config CHROOT_COMMAND= ───────────
234+
235+
section "CLI -- CHROOT_COMMAND priority over config CHROOT_COMMAND="
236+
237+
CONFIG_FILE="${TESTDIR}/cli_priority.conf"
238+
cat > "$CONFIG_FILE" <<'EOF'
239+
CHROOT_COMMAND=/usr/bin/pacman -Syu
240+
EOF
241+
242+
_TEST_SCRIPT_CLI="${TESTDIR}/atomic-upgrade-cli-priority"
243+
sed \
244+
-e 's|^\(export ATOMIC_UPGRADE=1\)$|\1\nCONFIG_FILE="${CONFIG_FILE:-/etc/atomic.conf}"|' \
245+
-e 's/^\(\[\[ \$EUID -eq 0 \]\]\)/# \1/' \
246+
-e '/^# Verify required variables are set$/i\
247+
if [[ "${ATOMIC_EXIT_AFTER_PARSE:-}" == "1" ]]; then\
248+
echo "PARSE_OK"\
249+
echo "DRY_RUN=${DRY_RUN}"\
250+
echo "CUSTOM_TAG=${CUSTOM_TAG}"\
251+
echo "NO_GC=${NO_GC}"\
252+
echo "SEPARATE_HOME=${SEPARATE_HOME}"\
253+
echo "COPY_FILES=${COPY_FILES}"\
254+
echo "CHROOT_CMD_COUNT=${#CHROOT_CMD[@]}"\
255+
for i in "${!CHROOT_CMD[@]}"; do\
256+
echo "CHROOT_CMD_${i}=${CHROOT_CMD[$i]}"\
257+
done\
258+
echo "CHROOT_COMMAND_FROM_CONFIG=${CHROOT_COMMAND:-}"\
259+
exit 0\
260+
fi\
261+
' \
262+
"$SCRIPT" > "$_TEST_SCRIPT_CLI"
263+
chmod +x "$_TEST_SCRIPT_CLI"
264+
265+
run_cmd env CONFIG_FILE="$CONFIG_FILE" ATOMIC_EXIT_AFTER_PARSE=1 bash "$_TEST_SCRIPT_CLI" -- /usr/bin/pacman -S nvidia
266+
assert_eq "CLI priority: PARSE_OK" "0" "$_rc"
267+
assert_contains "CLI priority: uses CLI args" "CHROOT_CMD_0=/usr/bin/pacman" "$_out"
268+
assert_contains "CLI priority: CLI arg[1]" "CHROOT_CMD_1=-S" "$_out"
269+
assert_contains "CLI priority: CLI arg[2]" "CHROOT_CMD_2=nvidia" "$_out"
270+
assert_eq "CLI priority: count is 3" "CHROOT_CMD_COUNT=3" "$(echo "$_out" | grep '^CHROOT_CMD_COUNT=')"
271+
assert_contains "CLI priority: CHROOT_COMMAND loaded from config" "CHROOT_COMMAND_FROM_CONFIG=/usr/bin/pacman -Syu" "$_out"
272+
273+
rm -f "$_TEST_SCRIPT_CLI"
274+
199275
# ── GEN_ID format validation ────────────────────────────────
200276

201277
section "GEN_ID format validation"

0 commit comments

Comments
 (0)