name: Release VM Dev # Build openshell-vm binaries for all supported platforms and upload them to # the rolling "vm-dev" GitHub Release. Each binary is self-extracting: it # embeds pre-built kernel runtime artifacts (from release-vm-kernel.yml) and a # base rootfs tarball. # # Prerequisites: the vm-dev release must already contain kernel runtime # tarballs. Run the "Release VM Kernel" workflow first if they are missing. on: push: branches: [main] workflow_dispatch: permissions: contents: write packages: read # Serialize with release-vm-kernel.yml — both update the vm-dev release. concurrency: group: vm-dev-release cancel-in-progress: false defaults: run: shell: bash jobs: # --------------------------------------------------------------------------- # Compute versions (reuse the same logic as release-dev.yml) # --------------------------------------------------------------------------- compute-versions: name: Compute Versions runs-on: build-amd64 timeout-minutes: 5 container: image: ghcr.io/nvidia/openshell/ci:latest credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} outputs: cargo_version: ${{ steps.v.outputs.cargo }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Mark workspace safe for git run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Fetch tags run: git fetch --tags --force - name: Compute versions id: v run: | set -euo pipefail echo "cargo=$(uv run python tasks/scripts/release.py get-version --cargo)" >> "$GITHUB_OUTPUT" # --------------------------------------------------------------------------- # Download kernel runtime tarballs from the vm-dev release # --------------------------------------------------------------------------- download-kernel-runtime: name: Download Kernel Runtime runs-on: build-amd64 timeout-minutes: 10 container: image: ghcr.io/nvidia/openshell/ci:latest credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 - name: Download all runtime tarballs env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail mkdir -p runtime-artifacts for platform in linux-aarch64 linux-x86_64 darwin-aarch64; do echo "Downloading vm-runtime-${platform}.tar.zst..." gh release download vm-dev \ --repo "${GITHUB_REPOSITORY}" \ --pattern "vm-runtime-${platform}.tar.zst" \ --dir runtime-artifacts \ --clobber done echo "Downloaded runtime artifacts:" ls -lah runtime-artifacts/ - name: Verify downloads run: | set -euo pipefail for platform in linux-aarch64 linux-x86_64 darwin-aarch64; do file="runtime-artifacts/vm-runtime-${platform}.tar.zst" if [ ! -f "$file" ]; then echo "ERROR: Missing ${file}" >&2 echo "" >&2 echo "The vm-dev release does not have kernel runtime artifacts." >&2 echo "Run the 'Release VM Kernel' workflow first:" >&2 echo " gh workflow run release-vm-kernel.yml" >&2 exit 1 fi echo "OK: ${file} ($(du -sh "$file" | cut -f1))" done - name: Upload as workflow artifact uses: actions/upload-artifact@v4 with: name: kernel-runtime-tarballs path: runtime-artifacts/vm-runtime-*.tar.zst retention-days: 1 # --------------------------------------------------------------------------- # Build base rootfs tarballs (architecture-specific) # --------------------------------------------------------------------------- build-rootfs: name: Build Rootfs (${{ matrix.arch }}) needs: [compute-versions] strategy: matrix: include: - arch: arm64 runner: build-arm64 guest_arch: aarch64 - arch: amd64 runner: build-amd64 guest_arch: x86_64 runs-on: ${{ matrix.runner }} timeout-minutes: 30 container: image: ghcr.io/nvidia/openshell/ci:latest credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} options: --privileged volumes: - /var/run/docker.sock:/var/run/docker.sock env: MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENSHELL_IMAGE_TAG: dev steps: - uses: actions/checkout@v4 - name: Mark workspace safe for git run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Log in to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin - name: Install tools run: mise install - name: Install zstd run: apt-get update && apt-get install -y --no-install-recommends zstd && rm -rf /var/lib/apt/lists/* - name: Build base rootfs tarball run: | set -euo pipefail crates/openshell-vm/scripts/build-rootfs.sh \ --base \ --arch ${{ matrix.guest_arch }} \ target/rootfs-build mkdir -p target/vm-runtime-compressed tar -C target/rootfs-build -cf - . \ | zstd -19 -T0 -o target/vm-runtime-compressed/rootfs.tar.zst echo "Rootfs tarball: $(du -sh target/vm-runtime-compressed/rootfs.tar.zst | cut -f1)" - name: Upload rootfs artifact uses: actions/upload-artifact@v4 with: name: rootfs-${{ matrix.arch }} path: target/vm-runtime-compressed/rootfs.tar.zst retention-days: 1 # --------------------------------------------------------------------------- # Build openshell-vm binary (Linux — native on each arch) # --------------------------------------------------------------------------- build-vm-linux: name: Build VM (Linux ${{ matrix.arch }}) needs: [compute-versions, download-kernel-runtime, build-rootfs] strategy: matrix: include: - arch: arm64 runner: build-arm64 target: aarch64-unknown-linux-gnu platform: linux-aarch64 guest_arch: aarch64 - arch: amd64 runner: build-amd64 target: x86_64-unknown-linux-gnu platform: linux-x86_64 guest_arch: x86_64 runs-on: ${{ matrix.runner }} timeout-minutes: 30 container: image: ghcr.io/nvidia/openshell/ci:latest credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} options: --privileged env: MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} OPENSHELL_IMAGE_TAG: dev steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Mark workspace safe for git run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Fetch tags run: git fetch --tags --force - name: Install tools run: mise install - name: Cache Rust target and registry uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: shared-key: vm-linux-${{ matrix.arch }} cache-directories: .cache/sccache cache-targets: "true" - name: Install zstd run: apt-get update && apt-get install -y --no-install-recommends zstd && rm -rf /var/lib/apt/lists/* - name: Download kernel runtime tarball uses: actions/download-artifact@v4 with: name: kernel-runtime-tarballs path: runtime-download/ - name: Download rootfs tarball uses: actions/download-artifact@v4 with: name: rootfs-${{ matrix.arch }} path: rootfs-download/ - name: Stage compressed runtime for embedding run: | set -euo pipefail COMPRESSED_DIR="${PWD}/target/vm-runtime-compressed" mkdir -p "$COMPRESSED_DIR" # Extract kernel runtime tarball and re-compress individual files EXTRACT_DIR=$(mktemp -d) zstd -d "runtime-download/vm-runtime-${{ matrix.platform }}.tar.zst" --stdout \ | tar -xf - -C "$EXTRACT_DIR" echo "Extracted runtime files:" ls -lah "$EXTRACT_DIR" for file in "$EXTRACT_DIR"/*; do [ -f "$file" ] || continue name=$(basename "$file") [ "$name" = "provenance.json" ] && continue zstd -19 -f -q -T0 -o "${COMPRESSED_DIR}/${name}.zst" "$file" done # Copy rootfs tarball (already zstd-compressed) cp rootfs-download/rootfs.tar.zst "${COMPRESSED_DIR}/rootfs.tar.zst" echo "Staged compressed artifacts:" ls -lah "$COMPRESSED_DIR" - name: Scope workspace to VM crates run: | set -euo pipefail sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-vm", "crates/openshell-core", "crates/openshell-bootstrap", "crates/openshell-policy"]|' Cargo.toml - name: Patch workspace version if: needs.compute-versions.outputs.cargo_version != '' run: | set -euo pipefail sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ needs.compute-versions.outputs.cargo_version }}"'"/}' Cargo.toml - name: Build openshell-vm run: | set -euo pipefail OPENSHELL_VM_RUNTIME_COMPRESSED_DIR="${PWD}/target/vm-runtime-compressed" \ mise x -- cargo build --release -p openshell-vm - name: sccache stats if: always() run: mise x -- sccache --show-stats - name: Package binary run: | set -euo pipefail mkdir -p artifacts tar -czf "artifacts/openshell-vm-${{ matrix.target }}.tar.gz" \ -C target/release openshell-vm ls -lh artifacts/ - name: Upload artifact uses: actions/upload-artifact@v4 with: name: vm-linux-${{ matrix.arch }} path: artifacts/*.tar.gz retention-days: 5 # --------------------------------------------------------------------------- # Build openshell-vm binary (macOS ARM64 via osxcross) # --------------------------------------------------------------------------- build-vm-macos: name: Build VM (macOS) needs: [compute-versions, download-kernel-runtime, build-rootfs] runs-on: build-amd64 timeout-minutes: 60 container: image: ghcr.io/nvidia/openshell/ci:latest credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} options: --privileged volumes: - /var/run/docker.sock:/var/run/docker.sock env: MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SCCACHE_MEMCACHED_ENDPOINT: ${{ vars.SCCACHE_MEMCACHED_ENDPOINT }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Mark workspace safe for git run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Fetch tags run: git fetch --tags --force - name: Log in to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin - name: Set up Docker Buildx uses: ./.github/actions/setup-buildx - name: Install zstd run: apt-get update && apt-get install -y --no-install-recommends zstd && rm -rf /var/lib/apt/lists/* - name: Download kernel runtime tarball uses: actions/download-artifact@v4 with: name: kernel-runtime-tarballs path: runtime-download/ - name: Download rootfs tarball (arm64) uses: actions/download-artifact@v4 with: name: rootfs-arm64 path: rootfs-download/ - name: Prepare compressed runtime directory run: | set -euo pipefail COMPRESSED_DIR="${PWD}/target/vm-runtime-compressed-macos" mkdir -p "$COMPRESSED_DIR" # Extract the darwin runtime tarball and re-compress for embedding. # The macOS embedded.rs expects: libkrun.dylib.zst, libkrunfw.5.dylib.zst, gvproxy.zst EXTRACT_DIR=$(mktemp -d) zstd -d "runtime-download/vm-runtime-darwin-aarch64.tar.zst" --stdout \ | tar -xf - -C "$EXTRACT_DIR" echo "Extracted darwin runtime files:" ls -lah "$EXTRACT_DIR" for file in "$EXTRACT_DIR"/*; do [ -f "$file" ] || continue name=$(basename "$file") [ "$name" = "provenance.json" ] && continue zstd -19 -f -q -T0 -o "${COMPRESSED_DIR}/${name}.zst" "$file" done # The macOS VM guest is always Linux ARM64, so use the arm64 rootfs cp rootfs-download/rootfs.tar.zst "${COMPRESSED_DIR}/rootfs.tar.zst" echo "Staged macOS compressed artifacts:" ls -lah "$COMPRESSED_DIR" - name: Build macOS binary via Docker (osxcross) run: | set -euo pipefail docker buildx build \ --file deploy/docker/Dockerfile.vm-macos \ --build-arg OPENSHELL_CARGO_VERSION="${{ needs.compute-versions.outputs.cargo_version }}" \ --build-arg OPENSHELL_IMAGE_TAG=dev \ --build-arg CARGO_TARGET_CACHE_SCOPE="${{ github.sha }}" \ --build-context vm-runtime-compressed="${PWD}/target/vm-runtime-compressed-macos" \ --target binary \ --output type=local,dest=out/ \ . - name: Package binary run: | set -euo pipefail mkdir -p artifacts tar -czf artifacts/openshell-vm-aarch64-apple-darwin.tar.gz \ -C out openshell-vm ls -lh artifacts/ - name: Upload artifact uses: actions/upload-artifact@v4 with: name: vm-macos path: artifacts/*.tar.gz retention-days: 5 # --------------------------------------------------------------------------- # Upload all VM binaries to the vm-dev rolling release # --------------------------------------------------------------------------- release-vm-dev: name: Release VM Dev needs: [build-vm-linux, build-vm-macos] runs-on: build-amd64 timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Download all VM binary artifacts uses: actions/download-artifact@v4 with: pattern: vm-* path: release/ merge-multiple: true - name: Filter to only binary tarballs run: | set -euo pipefail mkdir -p release-final # Only include the openshell-vm binary tarballs, not kernel runtime cp release/openshell-vm-*.tar.gz release-final/ count=$(ls release-final/openshell-vm-*.tar.gz 2>/dev/null | wc -l) if [ "$count" -eq 0 ]; then echo "ERROR: No VM binary tarballs found in release/" >&2 exit 1 fi echo "Release artifacts (${count} binaries):" ls -lh release-final/ - name: Generate checksums run: | set -euo pipefail cd release-final sha256sum openshell-vm-*.tar.gz > vm-binary-checksums-sha256.txt cat vm-binary-checksums-sha256.txt - name: Ensure vm-dev tag exists run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git tag -fa vm-dev -m "VM Development Build" "${GITHUB_SHA}" git push --force origin vm-dev - name: Prune stale VM binary assets from vm-dev release uses: actions/github-script@v7 with: script: | const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); let release; try { release = await github.rest.repos.getReleaseByTag({ owner, repo, tag: 'vm-dev' }); } catch (err) { if (err.status === 404) { core.info('No existing vm-dev release; will create fresh.'); return; } throw err; } // Delete old VM binary assets (keep kernel runtime assets) for (const asset of release.data.assets) { if (asset.name.startsWith('openshell-vm-') || asset.name === 'vm-binary-checksums-sha256.txt') { core.info(`Deleting stale asset: ${asset.name}`); await github.rest.repos.deleteReleaseAsset({ owner, repo, asset_id: asset.id }); } } - name: Upload to vm-dev GitHub Release uses: softprops/action-gh-release@v2 with: name: OpenShell VM Development Build prerelease: true tag_name: vm-dev target_commitish: ${{ github.sha }} body: | Rolling development build of **openshell-vm** — the MicroVM runtime for OpenShell. > **NOTE**: This is a development build, not a tagged release, and may be unstable. ### Kernel Runtime Artifacts Pre-built kernel runtime (libkrunfw + libkrun + gvproxy) for embedding into the openshell-vm binary. These are rebuilt when the kernel config or pinned dependency versions change. | Platform | Artifact | |----------|----------| | Linux ARM64 | `vm-runtime-linux-aarch64.tar.zst` | | Linux x86_64 | `vm-runtime-linux-x86_64.tar.zst` | | macOS ARM64 | `vm-runtime-darwin-aarch64.tar.zst` | ### VM Binaries Self-extracting openshell-vm binaries with embedded kernel runtime and base rootfs. These are rebuilt on every push to main. | Platform | Artifact | |----------|----------| | Linux ARM64 | `openshell-vm-aarch64-unknown-linux-gnu.tar.gz` | | Linux x86_64 | `openshell-vm-x86_64-unknown-linux-gnu.tar.gz` | | macOS ARM64 | `openshell-vm-aarch64-apple-darwin.tar.gz` | ### Quick install ``` curl -fsSL https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install-vm.sh | sh ``` Auto-detects your platform, verifies checksums, and codesigns on macOS. files: | release-final/openshell-vm-aarch64-unknown-linux-gnu.tar.gz release-final/openshell-vm-x86_64-unknown-linux-gnu.tar.gz release-final/openshell-vm-aarch64-apple-darwin.tar.gz release-final/vm-binary-checksums-sha256.txt