mirror of
https://github.com/kevinveenbirkenbach/create-linux-swapfile.git
synced 2026-08-16 20:02:47 +00:00
Turns the loose main.py into the 'swap-forge' distribution with a src layout, shipping both the swap-forge and the older swafo command. A failing swap command no longer produces a traceback: CalledProcessError is caught and reported as "'mkswap' failed with exit code 1" at exit 1, a missing binary as exit 127. Required binaries are declared per filesystem path so an ext4 host is never asked for btrfs tooling. The fstab entry is still written last, after swapon, so a failed activation leaves /etc/fstab untouched. The e2e suite proves that byte for byte across a create, a same-size rerun and a resize cycle, with real fallocate, chmod and file sizes. Adds unit, integration and container-based e2e tests, ruff and markdown/mermaid linting, CodeQL and Dependabot, and pins the core metadata to 2.4 so twine accepts the artifacts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
104 lines
3.8 KiB
Bash
104 lines
3.8 KiB
Bash
#!/bin/sh
|
|
# An unprivileged container cannot activate swap, so the run is expected to fail
|
|
# at mkswap or swapon - which of the two depends on the host's /proc/swaps.
|
|
# Everything before it - findmnt, fallocate, chmod - runs for real.
|
|
set -eu
|
|
|
|
fail() {
|
|
echo "FAIL: $1" >&2
|
|
exit 1
|
|
}
|
|
|
|
SWAPFORGE="$(command -v swap-forge)" || fail "swap-forge is not on PATH after install"
|
|
echo "ok: swap-forge installed at ${SWAPFORGE}"
|
|
|
|
command -v swafo >/dev/null || fail "the swafo alias is not on PATH after install"
|
|
echo "ok: swafo alias installed"
|
|
|
|
case "${SWAPFORGE}" in
|
|
/usr/local/bin/*) ;;
|
|
*) fail "swap-forge resolved to ${SWAPFORGE}, outside the install prefix" ;;
|
|
esac
|
|
echo "ok: resolved from the installed package, not the source tree"
|
|
|
|
for binary in findmnt swapoff chmod mkswap swapon fallocate chattr btrfs dd; do
|
|
command -v "${binary}" >/dev/null || fail "${binary} is missing from the image"
|
|
done
|
|
echo "ok: every declared binary is present"
|
|
|
|
swap-forge --help | grep -q "Swapfile size" || fail "--help does not describe the tool"
|
|
echo "ok: --help"
|
|
|
|
set +e
|
|
swap-forge >/dev/null 2>&1
|
|
status=$?
|
|
set -e
|
|
[ "${status}" -eq 2 ] || fail "a missing size exited ${status}, expected 2"
|
|
echo "ok: a missing size exits 2"
|
|
|
|
set +e
|
|
output="$(swap-forge 64T 2>&1)"
|
|
status=$?
|
|
set -e
|
|
[ "${status}" -eq 1 ] || fail "an invalid size exited ${status}, expected 1"
|
|
echo "${output}" | grep -q "Invalid size format" || fail "invalid size message wrong"
|
|
if echo "${output}" | grep -q "Traceback"; then
|
|
fail "an invalid size produced a traceback"
|
|
fi
|
|
echo "ok: an invalid size exits 1 without a traceback"
|
|
|
|
printf 'UUID=deadbeef / ext4 defaults 0 1\n' >/etc/fstab
|
|
cp /etc/fstab /tmp/fstab.before
|
|
|
|
set +e
|
|
output="$(swap-forge 8M 2>&1)"
|
|
status=$?
|
|
set -e
|
|
[ "${status}" -eq 1 ] || fail "the blocked activation exited ${status}, expected 1"
|
|
echo "${output}" | grep -q "Creating 8 MiB swapfile" || fail "the creation banner is missing"
|
|
echo "${output}" | grep -qE "'(mkswap|swapon)' failed with exit code" || fail "the blocked activation was not reported cleanly"
|
|
if echo "${output}" | grep -q "Traceback"; then
|
|
fail "the blocked activation produced a traceback"
|
|
fi
|
|
echo "ok: real fallocate and chmod ran; the blocked activation exits 1 cleanly"
|
|
|
|
[ -f /swapfile ] || fail "/swapfile was not created"
|
|
size="$(stat -c %s /swapfile)"
|
|
[ "${size}" -eq 8388608 ] || fail "/swapfile is ${size} bytes, expected 8388608"
|
|
echo "ok: /swapfile exists with exactly the requested size"
|
|
|
|
cmp -s /etc/fstab /tmp/fstab.before || fail "/etc/fstab changed although activation failed"
|
|
echo "ok: /etc/fstab is untouched when activation fails"
|
|
|
|
set +e
|
|
output="$(swap-forge 8M 2>&1)"
|
|
status=$?
|
|
set -e
|
|
[ "${status}" -eq 0 ] || fail "the idempotent rerun exited ${status}, expected 0"
|
|
echo "${output}" | grep -q "already exists with correct size (8 MiB). Skipping." \
|
|
|| fail "an existing swapfile of the right size was not detected"
|
|
echo "ok: a rerun with the same size is a no-op - the idempotency the role relies on"
|
|
|
|
set +e
|
|
output="$(swap-forge 12M 2>&1)"
|
|
status=$?
|
|
set -e
|
|
echo "${output}" | grep -q "has size 8 MiB, expected 12 MiB. Recreating..." \
|
|
|| fail "a size change did not trigger a recreation"
|
|
size="$(stat -c %s /swapfile)"
|
|
[ "${size}" -eq 12582912 ] || fail "/swapfile is ${size} bytes after resize, expected 12582912"
|
|
echo "ok: a different size really removes and recreates the swapfile"
|
|
|
|
cmp -s /etc/fstab /tmp/fstab.before || fail "/etc/fstab changed during the resize cycle"
|
|
echo "ok: /etc/fstab survived the resize cycle byte for byte"
|
|
|
|
set +e
|
|
output="$(env PATH=/nonexistent-bin "${SWAPFORGE}" 8M 2>&1)"
|
|
status=$?
|
|
set -e
|
|
[ "${status}" -eq 127 ] || fail "missing findmnt exited ${status}, expected 127"
|
|
echo "${output}" | grep -q "required command 'findmnt' is not installed" || fail "missing findmnt message wrong"
|
|
echo "ok: missing findmnt exits 127 with a clean message"
|
|
|
|
echo "ALL E2E CHECKS PASSED"
|