Repository navigation
chore(deps): update dependency smol-machines/smolvm to v1.21.0 #71
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |