Release #6
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: Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release version (e.g. 0.3.14-pspdfkit.1)" | |
| required: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| LIBKRUNFW_VERSION: "5.2.1" | |
| LIBKRUNFW_ABI: "5" | |
| permissions: | |
| contents: write | |
| packages: write | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # Build kernel.c on Linux for macOS libkrunfw linking | |
| # --------------------------------------------------------------------------- | |
| build-kernel: | |
| name: Build kernel.c (aarch64) | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - name: Cache kernel.c | |
| id: cache-kernel | |
| uses: actions/cache@v4 | |
| with: | |
| path: vendor/libkrunfw/kernel.c | |
| key: kernel-c-aarch64-${{ hashFiles('vendor/libkrunfw/**') }} | |
| - name: Install kernel build deps | |
| if: steps.cache-kernel.outputs.cache-hit != 'true' | |
| run: sudo apt-get update && sudo apt-get install -y libcap-ng-dev gcc make flex bison libelf-dev bc python3-pyelftools | |
| - name: Build kernel.c | |
| if: steps.cache-kernel.outputs.cache-hit != 'true' | |
| run: | | |
| cd vendor/libkrunfw | |
| make -j$(nproc) | |
| - name: Upload kernel.c | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: kernel-c-aarch64 | |
| path: vendor/libkrunfw/kernel.c | |
| # --------------------------------------------------------------------------- | |
| # Build agentd on Linux for macOS packaging | |
| # --------------------------------------------------------------------------- | |
| build-agentd-aarch64: | |
| name: Build agentd (aarch64-linux-musl) | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install agentd build deps | |
| run: sudo apt-get update && sudo apt-get install -y musl-tools | |
| - name: Build agentd | |
| run: | | |
| rustup target add aarch64-unknown-linux-musl | |
| cargo build --release --manifest-path crates/agentd/Cargo.toml --target aarch64-unknown-linux-musl | |
| mkdir -p build | |
| cp crates/agentd/target/aarch64-unknown-linux-musl/release/agentd build/agentd | |
| - name: Upload agentd | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: agentd-aarch64-linux-musl | |
| path: build/agentd | |
| # --------------------------------------------------------------------------- | |
| # Build | |
| # --------------------------------------------------------------------------- | |
| build: | |
| name: Build (${{ matrix.target }}) | |
| needs: [build-kernel, build-agentd-aarch64] | |
| if: always() | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: linux-x86_64 | |
| runner: ubuntu-latest | |
| arch: x86_64 | |
| os: linux | |
| agentd_target: x86_64-unknown-linux-musl | |
| libkrunfw_file: libkrunfw.so.5.2.1 | |
| libkrunfw_asset: libkrunfw-linux-x86_64.so | |
| napi_target: x86_64-unknown-linux-gnu | |
| node_file: microsandbox.linux-x64-gnu.node | |
| npm_dir: linux-x64-gnu | |
| - target: linux-aarch64 | |
| runner: ubuntu-24.04-arm | |
| arch: aarch64 | |
| os: linux | |
| agentd_target: aarch64-unknown-linux-musl | |
| libkrunfw_file: libkrunfw.so.5.2.1 | |
| libkrunfw_asset: libkrunfw-linux-aarch64.so | |
| napi_target: aarch64-unknown-linux-gnu | |
| node_file: microsandbox.linux-arm64-gnu.node | |
| npm_dir: linux-arm64-gnu | |
| - target: darwin-aarch64 | |
| runner: macos-14 | |
| arch: aarch64 | |
| os: darwin | |
| agentd_target: "" | |
| libkrunfw_file: libkrunfw.5.dylib | |
| libkrunfw_asset: libkrunfw-darwin-aarch64.dylib | |
| napi_target: aarch64-apple-darwin | |
| node_file: microsandbox.darwin-arm64.node | |
| npm_dir: darwin-arm64 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| # -- Linux build deps -- | |
| - name: Install build deps (Linux) | |
| if: matrix.os == 'linux' | |
| run: sudo apt-get update && sudo apt-get install -y musl-tools libcap-ng-dev gcc make flex bison libelf-dev bc python3-pyelftools | |
| # -- agentd (Linux: native musl) -- | |
| - name: Build agentd (musl) | |
| if: matrix.os == 'linux' | |
| run: | | |
| rustup target add ${{ matrix.agentd_target }} | |
| cargo build --release --manifest-path crates/agentd/Cargo.toml --target ${{ matrix.agentd_target }} | |
| mkdir -p build | |
| cp crates/agentd/target/${{ matrix.agentd_target }}/release/agentd build/agentd | |
| # -- agentd (macOS: download prebuilt Linux artifact) -- | |
| - name: Download agentd (macOS) | |
| if: matrix.os == 'darwin' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: agentd-aarch64-linux-musl | |
| path: build/ | |
| # -- libkrunfw (cached) -- | |
| - name: Cache libkrunfw | |
| id: cache-libkrunfw | |
| uses: actions/cache@v4 | |
| with: | |
| path: build/libkrunfw* | |
| key: libkrunfw-${{ matrix.target }}-${{ hashFiles('vendor/libkrunfw/**') }} | |
| - name: Build libkrunfw (Linux) | |
| if: steps.cache-libkrunfw.outputs.cache-hit != 'true' && matrix.os == 'linux' | |
| run: | | |
| cd vendor/libkrunfw | |
| make -j$(nproc) | |
| cd ../.. | |
| mkdir -p build | |
| cp vendor/libkrunfw/libkrunfw.so.${{ env.LIBKRUNFW_VERSION }} build/ | |
| cd build | |
| ln -sf libkrunfw.so.${{ env.LIBKRUNFW_VERSION }} libkrunfw.so.${{ env.LIBKRUNFW_ABI }} | |
| ln -sf libkrunfw.so.${{ env.LIBKRUNFW_ABI }} libkrunfw.so | |
| - name: Download kernel.c (macOS) | |
| if: steps.cache-libkrunfw.outputs.cache-hit != 'true' && matrix.os == 'darwin' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: kernel-c-aarch64 | |
| path: vendor/libkrunfw/ | |
| - name: Build libkrunfw (macOS) | |
| if: steps.cache-libkrunfw.outputs.cache-hit != 'true' && matrix.os == 'darwin' | |
| run: | | |
| cd vendor/libkrunfw | |
| cc -fPIC -DABI_VERSION=${{ env.LIBKRUNFW_ABI }} -shared -o libkrunfw.${{ env.LIBKRUNFW_ABI }}.dylib kernel.c | |
| cd ../.. | |
| mkdir -p build | |
| cp vendor/libkrunfw/libkrunfw.${{ env.LIBKRUNFW_ABI }}.dylib build/ | |
| cd build | |
| ln -sf libkrunfw.${{ env.LIBKRUNFW_ABI }}.dylib libkrunfw.dylib | |
| # -- msb -- | |
| - name: Build msb | |
| run: | | |
| cargo build --release --no-default-features --features net -p microsandbox-cli | |
| mkdir -p build | |
| cp target/release/msb build/msb | |
| # -- macOS codesign -- | |
| - name: Codesign msb | |
| if: matrix.os == 'darwin' | |
| run: codesign --entitlements msb-entitlements.plist --force -s - build/msb | |
| # -- Node SDK -- | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Build Node SDK | |
| working-directory: sdk/node-ts | |
| run: | | |
| npm ci | |
| npx napi build --release --platform --js index.cjs --dts index.d.cts --target ${{ matrix.napi_target }} | |
| npx tsc | |
| - name: Upload Node SDK artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: node-sdk-${{ matrix.npm_dir }} | |
| path: | | |
| sdk/node-ts/${{ matrix.node_file }} | |
| sdk/node-ts/index.cjs | |
| sdk/node-ts/index.d.cts | |
| sdk/node-ts/dist/ | |
| # -- Python SDK -- | |
| - uses: astral-sh/setup-uv@v3 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: "sdk/python/uv.lock" | |
| - name: Stage runtime bundle (Python SDK) | |
| run: | | |
| mkdir -p sdk/python/microsandbox/_bundled/bin | |
| mkdir -p sdk/python/microsandbox/_bundled/lib | |
| cp build/msb sdk/python/microsandbox/_bundled/bin/ | |
| cp build/${{ matrix.libkrunfw_file }} sdk/python/microsandbox/_bundled/lib/ | |
| cd sdk/python/microsandbox/_bundled/lib | |
| if [ "${{ matrix.os }}" = "darwin" ]; then | |
| ln -sf ${{ matrix.libkrunfw_file }} libkrunfw.dylib | |
| else | |
| ln -sf ${{ matrix.libkrunfw_file }} libkrunfw.so.${{ env.LIBKRUNFW_ABI }} | |
| ln -sf libkrunfw.so.${{ env.LIBKRUNFW_ABI }} libkrunfw.so | |
| fi | |
| - name: Build Python wheel | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| working-directory: sdk/python | |
| command: build | |
| args: --release --out dist | |
| manylinux: ${{ matrix.os == 'linux' && '2_28' || 'off' }} | |
| before-script-linux: dnf install -y libcap-ng-devel | |
| - name: Upload Python SDK wheel | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: python-sdk-${{ matrix.target }} | |
| path: sdk/python/dist/*.whl | |
| # -- Stage release artifacts -- | |
| - name: Stage artifacts | |
| run: | | |
| mkdir -p artifacts | |
| # Standalone msb | |
| cp build/msb artifacts/msb-${{ matrix.target }} | |
| # Standalone libkrunfw | |
| cp build/${{ matrix.libkrunfw_file }} artifacts/${{ matrix.libkrunfw_asset }} | |
| # Standalone agentd (Linux only — guest binary) | |
| if [ "${{ matrix.os }}" = "linux" ]; then | |
| cp build/agentd artifacts/agentd-${{ matrix.arch }} | |
| fi | |
| # Bundle: msb + libkrunfw | |
| tar -czf artifacts/microsandbox-${{ matrix.target }}.tar.gz \ | |
| -C build msb ${{ matrix.libkrunfw_file }} | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-${{ matrix.target }} | |
| path: artifacts/ | |
| # --------------------------------------------------------------------------- | |
| # Assemble: collect all artifacts, generate checksums, create GitHub release | |
| # --------------------------------------------------------------------------- | |
| assemble: | |
| name: Assemble Release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: release-artifacts | |
| pattern: release-* | |
| merge-multiple: true | |
| - name: Copy install script | |
| if: hashFiles('scripts/install.sh') != '' | |
| run: cp scripts/install.sh release-artifacts/install.sh | |
| - name: Generate checksums | |
| working-directory: release-artifacts | |
| run: sha256sum * > checksums.sha256 | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release create "v${{ inputs.version }}" \ | |
| --title "v${{ inputs.version }}" \ | |
| --generate-notes \ | |
| release-artifacts/* | |
| # --------------------------------------------------------------------------- | |
| # Upload Node SDK artifacts to GitHub Release | |
| # --------------------------------------------------------------------------- | |
| node-sdk-release: | |
| name: Upload Node SDK to Release | |
| needs: assemble | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Download Node SDK artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: node-artifacts | |
| pattern: node-sdk-* | |
| - name: Package platform tarballs | |
| run: | | |
| mkdir -p node-release | |
| VERSION="${{ inputs.version }}" | |
| RELEASE_BASE="https://github.com/${{ github.repository }}/releases/download/v${VERSION}" | |
| # --- Platform packages (one per target) --- | |
| # These contain only the native .node binary + a package.json with os/cpu | |
| # fields. npm's optionalDependencies resolution will skip non-matching | |
| # platforms automatically. | |
| for dir in darwin-arm64 linux-x64-gnu linux-arm64-gnu; do | |
| pkg_dir="sdk/node-ts/npm/${dir}" | |
| # Patch version into platform package.json | |
| node -e " | |
| const fs = require('fs'); | |
| const pkg = JSON.parse(fs.readFileSync('${pkg_dir}/package.json', 'utf8')); | |
| pkg.version = '${VERSION}'; | |
| fs.writeFileSync('${pkg_dir}/package.json', JSON.stringify(pkg, null, 2)); | |
| " | |
| # Copy the .node binary into the platform package | |
| cp node-artifacts/node-sdk-${dir}/microsandbox.*.node "${pkg_dir}/" | |
| # Pack the platform package | |
| npm pack --pack-destination node-release "./${pkg_dir}" | |
| # npm pack produces <scope>-<name>-<version>.tgz; rename to a | |
| # predictable name so the main SDK can reference it. | |
| mv "node-release/superradcompany-microsandbox-${dir}-${VERSION}.tgz" \ | |
| "node-release/microsandbox-native-${dir}.tgz" | |
| # Clean up for next iteration | |
| rm -f ${pkg_dir}/microsandbox.*.node | |
| git checkout "${pkg_dir}/package.json" | |
| done | |
| # --- Main SDK package (platform-independent, no .node files) --- | |
| # Copy tsc output and generated JS (same across all builds) | |
| cp -r node-artifacts/node-sdk-darwin-arm64/dist sdk/node-ts/ | |
| cp node-artifacts/node-sdk-darwin-arm64/index.cjs sdk/node-ts/ | |
| cp node-artifacts/node-sdk-darwin-arm64/index.d.cts sdk/node-ts/ | |
| # Patch version and optionalDependencies to point to platform tarballs | |
| node -e " | |
| const fs = require('fs'); | |
| const pkg = JSON.parse(fs.readFileSync('sdk/node-ts/package.json', 'utf8')); | |
| pkg.version = '${VERSION}'; | |
| pkg.optionalDependencies = { | |
| '@superradcompany/microsandbox-darwin-arm64': '${RELEASE_BASE}/microsandbox-native-darwin-arm64.tgz', | |
| '@superradcompany/microsandbox-linux-x64-gnu': '${RELEASE_BASE}/microsandbox-native-linux-x64-gnu.tgz', | |
| '@superradcompany/microsandbox-linux-arm64-gnu': '${RELEASE_BASE}/microsandbox-native-linux-arm64-gnu.tgz', | |
| }; | |
| fs.writeFileSync('sdk/node-ts/package.json', JSON.stringify(pkg, null, 2)); | |
| " | |
| npm pack --pack-destination node-release ./sdk/node-ts | |
| mv "node-release/microsandbox-${VERSION}.tgz" "node-release/microsandbox-node-sdk.tgz" | |
| git checkout sdk/node-ts/package.json | |
| - name: Upload to GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release upload "v${{ inputs.version }}" node-release/*.tgz | |
| # --------------------------------------------------------------------------- | |
| # Upload Python SDK wheels to GitHub Release | |
| # --------------------------------------------------------------------------- | |
| python-sdk-release: | |
| name: Upload Python wheels to Release | |
| needs: assemble | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download Python SDK wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| pattern: python-sdk-* | |
| merge-multiple: true | |
| - name: List wheels | |
| run: ls -lh dist/ | |
| - name: Upload to GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release upload "v${{ inputs.version }}" dist/*.whl | |
| # --------------------------------------------------------------------------- | |
| # Publish Docker image to GHCR | |
| # --------------------------------------------------------------------------- | |
| # Build per-arch Docker images and push digests | |
| # --------------------------------------------------------------------------- | |
| docker-build: | |
| name: Docker (${{ matrix.arch }}) | |
| needs: build | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| runner: ubuntu-latest | |
| artifact: release-linux-x86_64 | |
| msb_asset: msb-linux-x86_64 | |
| libkrunfw_asset: libkrunfw-linux-x86_64.so | |
| - arch: arm64 | |
| runner: ubuntu-24.04-arm | |
| artifact: release-linux-aarch64 | |
| msb_asset: msb-linux-aarch64 | |
| libkrunfw_asset: libkrunfw-linux-aarch64.so | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: release-artifacts | |
| - name: Stage binaries | |
| run: | | |
| mkdir -p packaging/docker/build/${{ matrix.arch }} | |
| cp release-artifacts/${{ matrix.msb_asset }} packaging/docker/build/${{ matrix.arch }}/msb | |
| cp release-artifacts/${{ matrix.libkrunfw_asset }} packaging/docker/build/${{ matrix.arch }}/libkrunfw.so.${{ env.LIBKRUNFW_VERSION }} | |
| chmod +x packaging/docker/build/${{ matrix.arch }}/msb | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/build-push-action@v6 | |
| id: push | |
| with: | |
| context: packaging/docker | |
| platforms: linux/${{ matrix.arch }} | |
| cache-from: type=gha,scope=${{ matrix.arch }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.arch }} | |
| outputs: type=image,name=ghcr.io/pspdfkit-labs/microsandbox,push-by-digest=true,name-canonical=true,push=true | |
| - name: Export digest | |
| run: | | |
| mkdir -p /tmp/digests | |
| echo "${{ steps.push.outputs.digest }}" > /tmp/digests/${{ matrix.arch }}.txt | |
| - name: Upload digest | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-digest-${{ matrix.arch }} | |
| path: /tmp/digests/${{ matrix.arch }}.txt | |
| # --------------------------------------------------------------------------- | |
| # Combine per-arch images into a multi-arch manifest | |
| # --------------------------------------------------------------------------- | |
| docker-manifest: | |
| name: Docker manifest | |
| needs: docker-build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download digests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: digests | |
| pattern: docker-digest-* | |
| merge-multiple: true | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/metadata-action@v5 | |
| id: meta | |
| with: | |
| images: ghcr.io/pspdfkit-labs/microsandbox | |
| tags: | | |
| type=raw,value=${{ inputs.version }} | |
| type=raw,value=latest | |
| - name: Create multi-arch manifest | |
| run: | | |
| AMD64_DIGEST=$(cat digests/amd64.txt) | |
| ARM64_DIGEST=$(cat digests/arm64.txt) | |
| IMAGE=ghcr.io/pspdfkit-labs/microsandbox | |
| for TAG in $(echo "${{ steps.meta.outputs.tags }}"); do | |
| docker buildx imagetools create -t "$TAG" \ | |
| "${IMAGE}@${AMD64_DIGEST}" \ | |
| "${IMAGE}@${ARM64_DIGEST}" | |
| done |