461 lines
17 KiB
YAML
461 lines
17 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags: ['v*']
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
# ==========================================================================
|
|
# Run CI checks first
|
|
# ==========================================================================
|
|
ci:
|
|
uses: ./.github/workflows/ci.yml
|
|
|
|
# ==========================================================================
|
|
# Build agent binaries (both architectures)
|
|
# ==========================================================================
|
|
build-agent:
|
|
name: Build agent (${{ matrix.arch }})
|
|
needs: ci
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- arch: x86_64
|
|
target: x86_64-unknown-linux-musl
|
|
linker_pkg: ""
|
|
linker_env: ""
|
|
- arch: aarch64
|
|
target: aarch64-unknown-linux-musl
|
|
linker_pkg: gcc-aarch64-linux-gnu
|
|
linker_env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
|
|
- name: Cache cargo
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.cargo/bin/
|
|
~/.cargo/registry/index/
|
|
~/.cargo/registry/cache/
|
|
~/.cargo/git/db/
|
|
target/
|
|
key: agent-${{ matrix.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: agent-${{ matrix.arch }}-cargo-
|
|
|
|
- name: Install cross-compilation tools
|
|
if: matrix.linker_pkg != ''
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y musl-tools ${{ matrix.linker_pkg }}
|
|
|
|
- name: Install musl-tools
|
|
if: matrix.linker_pkg == ''
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y musl-tools
|
|
|
|
- name: Build agent
|
|
run: |
|
|
${{ matrix.linker_env }} \
|
|
cargo build --profile release-small -p smolvm-agent --target ${{ matrix.target }}
|
|
|
|
- name: Verify static binary
|
|
run: file target/${{ matrix.target }}/release-small/smolvm-agent
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
with:
|
|
name: agent-${{ matrix.arch }}
|
|
path: target/${{ matrix.target }}/release-small/smolvm-agent
|
|
|
|
# ==========================================================================
|
|
# Build agent rootfs (both architectures)
|
|
# ==========================================================================
|
|
build-rootfs:
|
|
name: Build rootfs (${{ matrix.arch }})
|
|
needs: build-agent
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- arch: aarch64
|
|
- arch: x86_64
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- name: Download agent binary
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: agent-${{ matrix.arch }}
|
|
path: /tmp/agent/
|
|
|
|
- name: Build rootfs
|
|
run: |
|
|
chmod +x /tmp/agent/smolvm-agent
|
|
AGENT_BINARY=/tmp/agent/smolvm-agent \
|
|
./scripts/build-agent-rootfs.sh --arch ${{ matrix.arch }} --no-build-agent
|
|
|
|
- name: Package rootfs as tarball
|
|
run: |
|
|
# Tar preserves ownership/permissions without needing read access
|
|
# to restricted dirs like /var/run/chrony (owned by chrony:chrony 700)
|
|
sudo tar -cf target/agent-rootfs.tar -C target/agent-rootfs .
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
with:
|
|
name: rootfs-${{ matrix.arch }}
|
|
path: target/agent-rootfs.tar
|
|
|
|
# ==========================================================================
|
|
# Build distribution tarballs (per platform)
|
|
# ==========================================================================
|
|
build-dist:
|
|
name: Build dist (${{ matrix.platform }})
|
|
needs: [build-agent, build-rootfs]
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- platform: darwin-arm64
|
|
runner: macos-14
|
|
rootfs_arch: aarch64
|
|
agent_arch: aarch64
|
|
lib_dir: lib
|
|
# Linux dist binaries build on 22.04 (glibc 2.35), NOT ubuntu-latest
|
|
# (24.04 = glibc 2.39). Symbol versioning is backward-compatible only,
|
|
# so the build host's glibc is the MINIMUM a user needs at runtime —
|
|
# a 2.39-built binary dies on 22.04 with "GLIBC_2.39 not found". 2.35
|
|
# covers Ubuntu 22.04+ / Debian 12+ / RHEL 9+.
|
|
- platform: linux-x86_64
|
|
runner: ubuntu-22.04
|
|
rootfs_arch: x86_64
|
|
agent_arch: x86_64
|
|
lib_dir: lib/linux-x86_64
|
|
- platform: linux-arm64
|
|
runner: ubuntu-22.04-arm
|
|
rootfs_arch: aarch64
|
|
agent_arch: aarch64
|
|
lib_dir: lib/linux-aarch64
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
lfs: true
|
|
submodules: ${{ startsWith(matrix.platform, 'linux-') && 'recursive' || 'false' }}
|
|
|
|
- name: Pull LFS files
|
|
run: git lfs pull
|
|
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Cache cargo
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.cargo/bin/
|
|
~/.cargo/registry/index/
|
|
~/.cargo/registry/cache/
|
|
~/.cargo/git/db/
|
|
target/
|
|
# Include lib dir hash so libkrun updates bust the cache
|
|
key: dist-${{ matrix.platform }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles(format('{0}/*', matrix.lib_dir)) }}
|
|
restore-keys: dist-${{ matrix.platform }}-cargo-
|
|
|
|
# Force relink if cached binary was linked against a different libkrun
|
|
- name: Clean stale build artifacts
|
|
run: rm -f target/release/smolvm target/release/deps/smolvm-*
|
|
|
|
- name: Install build dependencies (Linux)
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
sudo apt-get update
|
|
# patchelf: build-dist.sh strips libkrun's hard libvirglrenderer NEEDED
|
|
# so non-GPU Linux hosts can dlopen it (GPU stays optional at runtime).
|
|
# Without patchelf the strip is silently skipped and `smolvm run` fails
|
|
# with "libvirglrenderer.so.1: cannot open shared object file".
|
|
sudo apt-get install -y build-essential libssl-dev pkg-config e2fsprogs patchelf
|
|
|
|
- name: Install build dependencies (macOS)
|
|
if: runner.os == 'macOS'
|
|
run: brew install e2fsprogs
|
|
|
|
- name: Verify LFS libraries
|
|
run: |
|
|
echo "Library directory: ${{ matrix.lib_dir }}"
|
|
ls -la ${{ matrix.lib_dir }}/
|
|
file ${{ matrix.lib_dir }}/*
|
|
|
|
- name: Download rootfs
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: rootfs-${{ matrix.rootfs_arch }}
|
|
path: /tmp/rootfs-dl/
|
|
|
|
- name: Extract rootfs tarball
|
|
run: |
|
|
mkdir -p target/agent-rootfs
|
|
sudo tar -xf /tmp/rootfs-dl/agent-rootfs.tar -C target/agent-rootfs
|
|
|
|
- name: Download agent binary
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: agent-${{ matrix.agent_arch }}
|
|
path: /tmp/agent/
|
|
|
|
- name: Place agent binary for build-dist.sh
|
|
run: |
|
|
mkdir -p target/release-small
|
|
cp /tmp/agent/smolvm-agent target/release-small/smolvm-agent
|
|
chmod +x target/release-small/smolvm-agent
|
|
|
|
# Import Developer ID certificate if available (macOS only).
|
|
# Set secrets APPLE_CERTIFICATE_P12 (base64-encoded .p12) and
|
|
# APPLE_CERTIFICATE_PASSWORD in your GitHub repo settings.
|
|
- name: Import signing certificate (macOS)
|
|
if: runner.os == 'macOS' && env.APPLE_CERTIFICATE_P12 != ''
|
|
env:
|
|
APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }}
|
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
run: |
|
|
echo "$APPLE_CERTIFICATE_P12" | base64 --decode > /tmp/certificate.p12
|
|
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
|
|
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
|
security default-keychain -s build.keychain
|
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
|
security import /tmp/certificate.p12 -k build.keychain \
|
|
-P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
|
|
security set-key-partition-list -S apple-tool:,apple: \
|
|
-s -k "$KEYCHAIN_PASSWORD" build.keychain
|
|
rm /tmp/certificate.p12
|
|
|
|
# Check out the unified `smol` CLI (a separate repo) into ./smol so
|
|
# build-dist.sh builds and bundles it alongside the engine — one tarball
|
|
# serves both `smol` and `smolvm`. Requires repo secret SMOL_REPO_TOKEN
|
|
# with read access to smol-machines/smol. If unset, the step is skipped and
|
|
# an engine-only tarball is produced. Ref is left at the smol default
|
|
# branch (its versioning is independent of the engine tag); pin a ref here
|
|
# if you want reproducible smol-CLI releases.
|
|
- name: Check out smol CLI source
|
|
if: env.SMOL_REPO_TOKEN != ''
|
|
uses: actions/checkout@v6
|
|
with:
|
|
repository: smol-machines/smol
|
|
path: smol
|
|
token: ${{ secrets.SMOL_REPO_TOKEN }}
|
|
env:
|
|
SMOL_REPO_TOKEN: ${{ secrets.SMOL_REPO_TOKEN }}
|
|
|
|
# No separate guest-init build: libkrun 2.0.0 embeds the guest init
|
|
# (krun-init) directly in libkrun.so (INIT_BLOB is on by default), so the
|
|
# old per-arch `cc init/init.c` step is gone — init.c no longer exists in
|
|
# the 2.0.0 submodule, and a VM boots from the embedded init with no
|
|
# external init.krun (verified). build-dist.sh no longer bundles init.krun.
|
|
|
|
- name: Build distribution tarball
|
|
run: ./scripts/build-dist.sh --skip-agent-build
|
|
env:
|
|
LIB_DIR: ${{ matrix.lib_dir }}
|
|
CODESIGN_IDENTITY: ${{ secrets.APPLE_CERTIFICATE_P12 != '' && secrets.CODESIGN_IDENTITY || '-' }}
|
|
|
|
# Notarize the macOS binary if Developer ID signing was used.
|
|
# Requires secrets: APPLE_ID, APPLE_TEAM_ID, APPLE_APP_PASSWORD.
|
|
- name: Notarize (macOS)
|
|
if: runner.os == 'macOS' && env.APPLE_CERTIFICATE_P12 != ''
|
|
env:
|
|
APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }}
|
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
|
|
run: |
|
|
TARBALL=$(ls dist/smolvm-*.tar.gz)
|
|
xcrun notarytool submit "$TARBALL" \
|
|
--apple-id "$APPLE_ID" \
|
|
--team-id "$APPLE_TEAM_ID" \
|
|
--password "$APPLE_APP_PASSWORD" \
|
|
--wait
|
|
|
|
- name: Verify bundled libraries match source
|
|
run: |
|
|
VERSION="$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)"
|
|
DIST_LIB="dist/smolvm-${VERSION}-${{ matrix.platform }}/lib"
|
|
echo "Source libraries:"
|
|
ls -la ${{ matrix.lib_dir }}/
|
|
echo "Bundled libraries:"
|
|
ls -la "$DIST_LIB"/
|
|
# Verify sizes match for all bundled libraries (catches stale LFS or copy issues).
|
|
# Covers both required libs (libkrun, libkrunfw) and optional GPU libs.
|
|
for lib in "$DIST_LIB"/lib*; do
|
|
[[ -L "$lib" ]] && continue # skip symlinks
|
|
name=$(basename "$lib")
|
|
src="${{ matrix.lib_dir }}/$name"
|
|
if [[ -f "$src" ]]; then
|
|
src_size=$(wc -c < "$src")
|
|
dst_size=$(wc -c < "$lib")
|
|
if [[ "$src_size" != "$dst_size" ]]; then
|
|
echo "ERROR: $name size mismatch: source=$src_size bundled=$dst_size"
|
|
exit 1
|
|
fi
|
|
echo "OK: $name ($dst_size bytes)"
|
|
fi
|
|
done
|
|
|
|
- name: List distribution contents
|
|
run: |
|
|
VERSION="$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)"
|
|
ls -la "dist/smolvm-${VERSION}-"*/
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
with:
|
|
name: dist-${{ matrix.platform }}
|
|
path: dist/smolvm-*.tar.gz
|
|
|
|
# ==========================================================================
|
|
# Build Windows distribution (cross-compiled; zipped)
|
|
# ==========================================================================
|
|
build-dist-windows:
|
|
name: Build dist (windows-x86_64)
|
|
needs: [build-agent, build-rootfs]
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: x86_64-w64-mingw32-gcc
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
lfs: true
|
|
|
|
- name: Pull LFS files
|
|
run: git lfs pull
|
|
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: x86_64-pc-windows-gnu
|
|
|
|
- name: Install build dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
# mingw: cross-link smolvm.exe; clang/libclang: bindgen in build deps
|
|
# (matches the CI windows job); e2fsprogs: pre-format disk templates
|
|
# (Windows has no host mkfs.ext4); zip: package the dist.
|
|
sudo apt-get install -y gcc-mingw-w64-x86-64 clang libclang-dev e2fsprogs zip
|
|
|
|
- name: Verify Windows libraries
|
|
run: |
|
|
ls -la lib/windows-x86_64/
|
|
file lib/windows-x86_64/*.dll
|
|
|
|
- name: Download rootfs (x86_64)
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: rootfs-x86_64
|
|
path: /tmp/rootfs-dl/
|
|
|
|
- name: Extract rootfs
|
|
run: |
|
|
mkdir -p target/agent-rootfs
|
|
sudo tar -xf /tmp/rootfs-dl/agent-rootfs.tar -C target/agent-rootfs
|
|
|
|
- name: Download agent binary (x86_64)
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: agent-x86_64
|
|
path: /tmp/agent/
|
|
|
|
# The rootfs artifact ships agent-less; inject the freshly built agent and
|
|
# repoint /sbin/init at it (same as the Unix build-dist.sh path).
|
|
- name: Inject agent into rootfs
|
|
run: |
|
|
sudo cp /tmp/agent/smolvm-agent target/agent-rootfs/usr/local/bin/smolvm-agent
|
|
sudo chmod +x target/agent-rootfs/usr/local/bin/smolvm-agent
|
|
sudo ln -sf /usr/local/bin/smolvm-agent target/agent-rootfs/sbin/init
|
|
|
|
- name: Build Windows distribution
|
|
run: ./scripts/build-dist-windows.sh
|
|
env:
|
|
LIB_DIR: lib/windows-x86_64
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
with:
|
|
name: dist-windows-x86_64
|
|
path: dist/smolvm-*-windows-x86_64.zip
|
|
|
|
# ==========================================================================
|
|
# Create GitHub Release
|
|
# ==========================================================================
|
|
release:
|
|
name: Create Release
|
|
needs: [build-dist, build-dist-windows]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- name: Download all dist artifacts
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: dist-*
|
|
path: dist/
|
|
merge-multiple: true
|
|
|
|
- name: List release artifacts
|
|
run: ls -la dist/
|
|
|
|
- name: Generate checksums
|
|
run: |
|
|
cd dist
|
|
sha256sum *.tar.gz *.zip > checksums.sha256
|
|
cat checksums.sha256
|
|
|
|
- name: Create GitHub Release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
gh release create "${{ github.ref_name }}" \
|
|
--title "smolvm ${{ github.ref_name }}" \
|
|
--generate-notes \
|
|
dist/*.tar.gz \
|
|
dist/*.zip \
|
|
dist/checksums.sha256
|
|
|
|
# Ping the Homebrew tap to bump its formula to this release immediately,
|
|
# instead of waiting for its scheduled poll. Best-effort: if the token
|
|
# secret isn't configured, this no-ops (the tap's cron picks the release up
|
|
# within a few hours anyway) and never fails the release. The token is a
|
|
# fine-grained PAT with Contents:write on smol-machines/homebrew-tap.
|
|
- name: Notify Homebrew tap of the new release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_DISPATCH_TOKEN }}
|
|
TAG: ${{ github.ref_name }}
|
|
run: |
|
|
if [ -z "${GH_TOKEN:-}" ]; then
|
|
echo "HOMEBREW_TAP_DISPATCH_TOKEN not set — skipping instant bump (the tap polls on a schedule)."
|
|
exit 0
|
|
fi
|
|
version="${TAG#v}"
|
|
gh api --method POST repos/smol-machines/homebrew-tap/dispatches \
|
|
-f event_type=smolvm-release \
|
|
-f "client_payload[version]=$version"
|
|
echo "Dispatched smolvm-release ($version) to the Homebrew tap."
|
|
|
|
# Build + publish the Arch/pacman packages for this release. Invoked directly
|
|
# (not via the `release: published` event) because that event never fires for a
|
|
# release created with the default GITHUB_TOKEN — the same GitHub restriction
|
|
# the Homebrew step above works around. Runs after the release exists so the
|
|
# PKGBUILD can download its release binaries.
|
|
publish-pacman:
|
|
name: Publish pacman repo
|
|
needs: release
|
|
permissions:
|
|
contents: write
|
|
uses: ./.github/workflows/pacman-repo.yml
|
|
with:
|
|
tag: ${{ github.ref_name }}
|