Skip to content

chore(deps): update dependency smol-machines/smolvm to v1.20.2 #67

chore(deps): update dependency smol-machines/smolvm to v1.20.2

chore(deps): update dependency smol-machines/smolvm to v1.20.2 #67

name: smolvm-release-verify
on:
pull_request:
paths:
- versions.toml
workflow_dispatch:
inputs:
version:
description: "SmolVM release to verify (default: smolvm_golden_version in versions.toml)"
required: false
type: string
permissions:
contents: read
concurrency:
group: github-hosted-kvm
cancel-in-progress: false
jobs:
verify:
name: Verify smolvm release on GitHub-hosted KVM
if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'renovate/')
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
fetch-depth: 2
- name: Detect golden-pin movement
id: changed
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
set -euo pipefail
CHANGED=false
if ! git diff --quiet "$BASE_SHA" -- versions.toml \
&& git diff "$BASE_SHA" -- versions.toml \
| grep -q '^[+-][[:space:]]*smolvm_golden_version'; then
CHANGED=true
fi
echo "changed=$CHANGED" >> "$GITHUB_OUTPUT"
- name: Resolve candidate version
id: resolve
env:
INPUT_VERSION: ${{ github.event.inputs.version }}
run: |
set -euo pipefail
if [ -n "$INPUT_VERSION" ]; then
VERSION="$INPUT_VERSION"
else
VERSION="$(grep -E '^[[:space:]]*smolvm_golden_version[[:space:]]*=' versions.toml | cut -d'"' -f2 || true)"
fi
[ -n "$VERSION" ] || { echo "no smolvm version resolved" >&2; exit 1; }
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Install candidate smolvm (mirrors release-golden.yml)
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.changed == 'true'
env:
SMOLVM_VERSION: ${{ steps.resolve.outputs.version }}
run: |
set -euo pipefail
case "$(uname -s)/$(uname -m)" in
Darwin/arm64) PLATFORM="darwin-arm64"; DATA_DIR="$HOME/Library/Application Support/smolvm" ;;
Darwin/x86_64) PLATFORM="darwin-x86_64"; DATA_DIR="$HOME/Library/Application Support/smolvm" ;;
Linux/x86_64) PLATFORM="linux-x86_64"; DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/smolvm" ;;
Linux/aarch64) PLATFORM="linux-arm64"; DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/smolvm" ;;
*) echo "unsupported host $(uname -s)/$(uname -m)" >&2; exit 1 ;;
esac
ARCHIVE="smolvm-${SMOLVM_VERSION}-${PLATFORM}.tar.gz"
BASE="https://github.com/smol-machines/smolvm/releases/download/v${SMOLVM_VERSION}"
WORK="$(mktemp -d "${TMPDIR:-/tmp}/smolvm-verify.XXXXXX")"
curl -fsSL -o "$WORK/$ARCHIVE" "$BASE/$ARCHIVE"
curl -fsSL -o "$WORK/checksums.sha256" "$BASE/checksums.sha256"
( cd "$WORK" && grep -F "$ARCHIVE" checksums.sha256 | shasum -a 256 -c - )
tar -xzf "$WORK/$ARCHIVE" -C "$WORK"
SRC="$WORK/smolvm-${SMOLVM_VERSION}-${PLATFORM}"
mkdir -p "$HOME/.smolvm"
rm -rf "$HOME/.smolvm/lib"
cp -R "$SRC/lib" "$HOME/.smolvm/lib"
rm -f "$HOME/.smolvm/storage-template.ext4" \
"$HOME/.smolvm/overlay-template.ext4" \
"$HOME/.smolvm/storage-template.ext4.zst" \
"$HOME/.smolvm/overlay-template.ext4.zst"
for f in smolvm smolvm-bin storage-template.ext4 overlay-template.ext4 \
storage-template.ext4.zst overlay-template.ext4.zst; do
[ -f "$SRC/$f" ] && cp "$SRC/$f" "$HOME/.smolvm/"
done
echo "$SMOLVM_VERSION" > "$HOME/.smolvm/.version"
mkdir -p "$DATA_DIR"
rm -rf "$DATA_DIR/agent-rootfs"
cp -R "$SRC/agent-rootfs" "$DATA_DIR/agent-rootfs"
mkdir -p "$HOME/.local/bin"
ln -sfn "$HOME/.smolvm/smolvm" "$HOME/.local/bin/smolvm"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Probe capabilities
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.changed == 'true'
run: |
set -euo pipefail
smolvm --version
smolvm machine create --help | grep -q -- --mount-socket \
|| { echo "candidate lacks --mount-socket" >&2; exit 1; }
smolvm machine start --help | grep -q -- --forkable \
|| { echo "candidate lacks --forkable" >&2; exit 1; }
smolvm machine fork --help >/dev/null 2>&1 \
|| { echo "candidate lacks machine fork command" >&2; exit 1; }
- name: Grant runner KVM access
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.changed == 'true'
run: |
set -euo pipefail
# Recent hosted images no longer put the runner user in the kvm
# group, so every boot dies with `Cannot access /dev/kvm
# (permission denied)` before the candidate under test even runs.
# This only opens the device on the ephemeral host; the gate below
# still boots a real VM with the candidate smolvm.
ls -l /dev/kvm || { echo "no /dev/kvm on this host" >&2; exit 1; }
sudo chmod 666 /dev/kvm
ls -l /dev/kvm
- name: Boot a VM with the candidate (regression smoke)
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.changed == 'true'
run: |
set -euo pipefail
NAME="preloop-verify-$(date +%s)"
SOCKET="$(mktemp -u "${TMPDIR:-/tmp}/preloop-verify.XXXXXX.sock")"
cleanup() {
smolvm machine stop --name "$NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$NAME" -f >/dev/null 2>&1 || true
rm -f "$SOCKET"
}
trap cleanup EXIT
IMAGE="$(grep '^ubuntu_24_04_base' versions.toml | cut -d'"' -f2)"
[ -n "$IMAGE" ] || { echo "ubuntu_24_04_base missing from versions.toml" >&2; exit 1; }
python3 -c "import socket; s = socket.socket(socket.AF_UNIX); s.bind('$SOCKET'); s.close()"
echo "creating machine from $IMAGE"
smolvm machine create --name "$NAME" \
--image "$IMAGE" \
--net \
--storage 10 \
--mount-socket "$SOCKET:/run/preloop-verify.sock"
smolvm machine start --name "$NAME"
echo "waiting for the guest to boot..."
BOOTED=""
for _ in $(seq 1 150); do
if smolvm machine exec --name "$NAME" -- sh -c 'echo smolvm-verify-ok' \
| grep -q smolvm-verify-ok; then
BOOTED=1
break
fi
sleep 2
done
if [ -z "$BOOTED" ]; then
echo "machine did not boot (or exec failed) with candidate smolvm" >&2
smolvm machine status --name "$NAME" 2>&1 || true
exit 1
fi
smolvm machine exec --name "$NAME" -- sh -c "test -S /run/preloop-verify.sock"
smolvm machine exec --name "$NAME" -- id -u | grep -q '^0$' \
|| { echo "exec as root failed" >&2; exit 1; }
echo "candidate smolvm boots VMs and handles socket mounts + user exec: PASS"
- name: Verify CoW fork lifecycle
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.changed == 'true'
run: |
set -euo pipefail
BASE_NAME="preloop-fork-base-$(date +%s)"
CLONE_NAME="preloop-fork-clone-$(date +%s)"
SOCKET="$(mktemp -u "${TMPDIR:-/tmp}/preloop-fork.XXXXXX.sock")"
python3 -c "import socket; s = socket.socket(socket.AF_UNIX); s.bind('$SOCKET'); s.close()"
cleanup() {
smolvm machine stop --name "$CLONE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$CLONE_NAME" -f >/dev/null 2>&1 || true
smolvm machine stop --name "$BASE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$BASE_NAME" -f >/dev/null 2>&1 || true
rm -f "$SOCKET"
}
trap cleanup EXIT
smolvm machine create --name "$BASE_NAME" --image alpine:3.20 --net --mount-socket "$SOCKET:/run/preloop-fork.sock"
smolvm machine start --name "$BASE_NAME" --forkable
for _ in $(seq 1 60); do
if smolvm machine exec --name "$BASE_NAME" -- sh -c 'echo fork-base-ready' 2>/dev/null | grep -q fork-base-ready; then
break
fi
sleep 1
done
smolvm machine fork --golden "$BASE_NAME" --name "$CLONE_NAME"
smolvm machine start --name "$CLONE_NAME"
for _ in $(seq 1 60); do
if smolvm machine exec --name "$CLONE_NAME" -- sh -c 'echo clone-ready' 2>/dev/null | grep -q clone-ready; then
break
fi
sleep 1
done
smolvm machine exec --name "$CLONE_NAME" -- sh -c "test -S /run/preloop-fork.sock"
echo "candidate smolvm CoW fork lifecycle: PASS"
- name: Verify packed ownership preservation
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.changed == 'true'
run: |
set -euo pipefail
BASE_NAME="preloop-ownership-base-$(date +%s)"
CLONE_NAME="${BASE_NAME}-clone"
WORK="$(mktemp -d "${TMPDIR:-/tmp}/preloop-ownership.XXXXXX")"
PACK="$WORK/ownership-pack"
echo "SMOLVM_VERIFY_BASE_NAME=$BASE_NAME" >> "$GITHUB_ENV"
echo "SMOLVM_VERIFY_CLONE_NAME=$CLONE_NAME" >> "$GITHUB_ENV"
echo "SMOLVM_VERIFY_WORK=$WORK" >> "$GITHUB_ENV"
cleanup() {
smolvm machine stop --name "$CLONE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$CLONE_NAME" -f --cascade >/dev/null 2>&1 || true
smolvm machine stop --name "$BASE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$BASE_NAME" -f --cascade >/dev/null 2>&1 || true
rm -rf "$WORK"
}
trap cleanup EXIT
smolvm machine create --name "$BASE_NAME" \
--image alpine:3.20 \
--net \
--init 'addgroup -g 2345 repro && adduser -D -u 2345 -G repro repro && touch /opt/preloop-ownership && chown 2345:2345 /opt/preloop-ownership'
smolvm machine start --name "$BASE_NAME"
READY=""
for _ in $(seq 1 150); do
if smolvm machine exec --name "$BASE_NAME" -- \
sh -c 'test "$(stat -c %u:%g /opt/preloop-ownership)" = 2345:2345'; then
READY=1
break
fi
sleep 2
done
[ -n "$READY" ] || { echo "ownership fixture VM did not become ready" >&2; exit 1; }
smolvm machine stop --name "$BASE_NAME"
smolvm pack create --from-vm "$BASE_NAME" --output "$PACK" --no-sign
# This runner is root, but preloop.service runs as an unprivileged
# user, so smolvm's per-VM uid drop is inactive in production. Match
# that here: as root the drop is on by default and squashes packed
# ownership (see docs/internal/workflows.md), which is an upstream
# defect, not a property of the release under test.
export SMOLVM_VM_UID_DROP=off
smolvm machine create --name "$CLONE_NAME" --from "${PACK}.smolmachine"
smolvm machine start --name "$CLONE_NAME"
ACTUAL=""
for _ in $(seq 1 150); do
ACTUAL="$(smolvm machine exec --name "$CLONE_NAME" -- \
sh -c 'stat -c %u:%g /opt/preloop-ownership' 2>/dev/null | tail -n 1 || true)"
if [ "$ACTUAL" = "2345:2345" ]; then
break
fi
if [ -n "$ACTUAL" ]; then
echo "packed ownership mismatch: expected 2345:2345, got $ACTUAL" >&2
exit 1
fi
sleep 2
done
[ "$ACTUAL" = "2345:2345" ] \
|| { echo "packed ownership mismatch: expected 2345:2345, got $ACTUAL" >&2; exit 1; }
echo "packed ownership preservation: PASS"
- name: Probe packed ownership under the per-VM uid drop
if: github.event_name == 'workflow_dispatch' || steps.changed.outputs.changed == 'true'
continue-on-error: true
run: |
set -uo pipefail
# Non-blocking: tracks the upstream defect where a privileged smolvm
# preserves the archived uid on disk and then presents it to the guest
# through a count-1 idmapped mount, so any non-zero uid surfaces as the
# overflow uid. Blocking on it would gate our bumps on an upstream fix
# we do not ship; losing the signal entirely would let it regress
# silently if preloop ever runs privileged. See
# docs/internal/workflows.md.
BASE_NAME="preloop-uiddrop-base-$(date +%s)"
CLONE_NAME="${BASE_NAME}-clone"
WORK="$(mktemp -d "${TMPDIR:-/tmp}/preloop-uiddrop.XXXXXX")"
PACK="$WORK/uiddrop-pack"
cleanup() {
smolvm machine stop --name "$CLONE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$CLONE_NAME" -f --cascade >/dev/null 2>&1 || true
smolvm machine stop --name "$BASE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$BASE_NAME" -f --cascade >/dev/null 2>&1 || true
rm -rf "$WORK"
}
trap cleanup EXIT
if [ "$(id -u)" -ne 0 ]; then
echo "uid drop is inactive for an unprivileged smolvm; probe not applicable"
exit 0
fi
smolvm machine create --name "$BASE_NAME" \
--image alpine:3.20 \
--net \
--init 'touch /opt/preloop-ownership && chown 2345:2345 /opt/preloop-ownership'
smolvm machine start --name "$BASE_NAME"
sleep 5
smolvm machine stop --name "$BASE_NAME"
smolvm pack create --from-vm "$BASE_NAME" --output "$PACK" --no-sign
smolvm machine create --name "$CLONE_NAME" --from "${PACK}.smolmachine"
smolvm machine start --name "$CLONE_NAME"
sleep 5
OWNED="$(smolvm machine exec --name "$CLONE_NAME" -- \
sh -c 'stat -c %u:%g /opt/preloop-ownership' 2>/dev/null | tail -n 1 || true)"
ROOT_OWNED="$(smolvm machine exec --name "$CLONE_NAME" -- \
sh -c 'stat -c %u:%g /etc/passwd' 2>/dev/null | tail -n 1 || true)"
echo "uid 2345 file presented to guest as: ${OWNED:-<unavailable>}"
echo "root-owned file presented to guest as: ${ROOT_OWNED:-<unavailable>}"
if [ "$OWNED" = "2345:2345" ] && [ "$ROOT_OWNED" = "0:0" ]; then
echo "privileged packed ownership: FIXED UPSTREAM — drop the workaround in the gate above"
else
echo "privileged packed ownership: still squashed (known upstream defect)"
fi
- name: Cleanup ownership regression machines
if: always()
run: |
set +e
if [ -n "${SMOLVM_VERIFY_CLONE_NAME:-}" ]; then
smolvm machine stop --name "$SMOLVM_VERIFY_CLONE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$SMOLVM_VERIFY_CLONE_NAME" -f --cascade >/dev/null 2>&1 || true
fi
if [ -n "${SMOLVM_VERIFY_BASE_NAME:-}" ]; then
smolvm machine stop --name "$SMOLVM_VERIFY_BASE_NAME" >/dev/null 2>&1 || true
smolvm machine delete --name "$SMOLVM_VERIFY_BASE_NAME" -f --cascade >/dev/null 2>&1 || true
fi
if [ -n "${SMOLVM_VERIFY_WORK:-}" ]; then
rm -rf "$SMOLVM_VERIFY_WORK"
fi