Skip to content

Release VM Assets

Release VM Assets #2

name: Release VM Assets
on:
workflow_dispatch:
inputs:
channel:
description: Asset channel to publish.
required: true
default: stable
profile:
description: Profile id that owns the VM image build.
required: true
default: code
dry_run:
description: Build and validate without deploying release.capsem.org.
required: true
type: boolean
default: true
permissions:
attestations: write
contents: write
id-token: write
jobs:
build-assets:
strategy:
matrix:
include:
- arch: arm64
runner: ubuntu-24.04-arm
rust-target: aarch64-unknown-linux-musl
- arch: x86_64
runner: ubuntu-24.04
rust-target: x86_64-unknown-linux-musl
runs-on: ${{ matrix.runner }}
name: Build VM assets (${{ matrix.arch }})
steps:
- uses: actions/checkout@v5
- uses: docker/setup-buildx-action@v4
- uses: astral-sh/setup-uv@v5
- run: uv sync
- uses: extractions/setup-just@v3
- uses: pnpm/action-setup@v5
with:
version: 10
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
cache-dependency-path: frontend/pnpm-lock.yaml
- name: Install musl C toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends musl-tools
- name: Install OBOM generator
run: |
npm install -g @cyclonedx/cdxgen@latest
cdxgen --version
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust-target }}
- name: Build VM assets (kernel + rootfs)
env:
CAPSEM_CDXGEN_CMD: cdxgen
CC_aarch64_unknown_linux_musl: musl-gcc
CC_x86_64_unknown_linux_musl: musl-gcc
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
run: |
just build-kernel ${{ matrix.arch }} "${{ inputs.profile }}"
just build-rootfs ${{ matrix.arch }} "${{ inputs.profile }}"
- uses: actions/upload-artifact@v7
with:
name: vm-assets-${{ matrix.arch }}
path: assets/${{ matrix.arch }}/
assemble-channel:
needs: build-assets
runs-on: ubuntu-latest
name: Assemble asset channel
outputs:
asset_changed: ${{ steps.asset-delta.outputs.changed }}
asset_blobs_changed: ${{ steps.asset-delta.outputs.asset_blobs_changed }}
env:
CHANNEL: ${{ inputs.channel }}
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v5
- run: uv sync
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/download-artifact@v8
with:
name: vm-assets-arm64
path: assets/arm64/
- uses: actions/download-artifact@v8
with:
name: vm-assets-x86_64
path: assets/x86_64/
- name: Generate asset manifest
run: |
cargo run -p capsem-admin -- manifest generate assets
- name: Preserve binary channel metadata
run: |
scripts/preserve-binary-channel-metadata.py \
--manifest-path assets/manifest.json \
--previous-manifest-url "https://release.capsem.org/assets/$CHANNEL/manifest.json" \
--allow-missing-previous
- name: Check asset release delta
id: asset-delta
run: |
scripts/check-asset-release-delta.py \
--new-manifest assets/manifest.json \
--previous-manifest-url "https://release.capsem.org/assets/$CHANNEL/manifest.json" \
--allow-missing-previous \
--json-output target/asset-release-delta/delta.json
- name: Build asset channel preview
if: ${{ steps.asset-delta.outputs.changed == 'true' }}
run: |
cargo run -p capsem-admin -- assets channel build \
--manifest "file://$PWD/assets/manifest.json" \
--channel "$CHANNEL" \
--out-dir target/release-channel
- name: Check asset channel preview
if: ${{ steps.asset-delta.outputs.changed == 'true' }}
run: |
cargo run -p capsem-admin -- assets channel check \
--channel "$CHANNEL" \
--dist target/release-channel
- name: Publish immutable GitHub asset release
if: ${{ steps.asset-delta.outputs.asset_blobs_changed == 'true' }}
env:
DRY_RUN: ${{ inputs.dry_run }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
ASSET_VERSION=$(python - <<'PY'
import json
with open("assets/manifest.json", encoding="utf-8") as handle:
print(json.load(handle)["assets"]["current"])
PY
)
TAG="assets-v$ASSET_VERSION"
RELEASE_DIR="target/asset-release/$TAG"
UPLOAD_SCRIPT="target/asset-release/upload-assets.sh"
mkdir -p "$RELEASE_DIR"
for arch_dir in assets/*; do
[ -d "$arch_dir" ] || continue
arch="$(basename "$arch_dir")"
for logical_name in vmlinuz initrd.img rootfs.erofs obom.cdx.json; do
src="$arch_dir/$logical_name"
test -f "$src"
cp "$src" "$RELEASE_DIR/$arch-$logical_name"
done
done
files=("$RELEASE_DIR"/*)
test "${#files[@]}" -gt 0
mkdir -p "$(dirname "$UPLOAD_SCRIPT")"
{
echo '#!/usr/bin/env bash'
echo 'set -euo pipefail'
printf 'if gh release view %q >/dev/null 2>&1; then\n' "$TAG"
printf ' gh release upload %q' "$TAG"
printf ' %q' "${files[@]}"
printf ' --clobber\n'
printf 'else\n'
printf ' gh release create %q' "$TAG"
printf ' %q' "${files[@]}"
printf ' --title %q --notes %q --target %q\n' \
"Capsem VM assets $ASSET_VERSION" \
"Immutable Capsem VM asset release for $CHANNEL/$ASSET_VERSION." \
"$GITHUB_SHA"
printf 'fi\n'
} > "$UPLOAD_SCRIPT"
chmod +x "$UPLOAD_SCRIPT"
{
echo "asset_version=$ASSET_VERSION"
echo "asset_release_tag=$TAG"
} >> "$GITHUB_OUTPUT"
{
echo "## Immutable VM asset release"
echo
echo "- Tag: \`$TAG\`"
echo "- Dry run: \`$DRY_RUN\`"
echo "- Files:"
printf ' - `%s`\n' "${files[@]}"
} >> "$GITHUB_STEP_SUMMARY"
if [[ "$DRY_RUN" == "true" ]]; then
echo "Dry run: would publish immutable VM asset release $TAG"
sed 's/^/DRY-RUN: /' "$UPLOAD_SCRIPT"
else
"$UPLOAD_SCRIPT"
fi
- name: Attest VM asset provenance
if: ${{ inputs.dry_run == false && steps.asset-delta.outputs.asset_blobs_changed == 'true' }}
uses: actions/attest-build-provenance@v4
with:
subject-path: |
target/asset-release/assets-v*/*-vmlinuz
target/asset-release/assets-v*/*-initrd.img
target/asset-release/assets-v*/*-rootfs.erofs
target/asset-release/assets-v*/*-obom.cdx.json
- uses: actions/upload-artifact@v7
with:
name: asset-channel-source
path: assets/manifest.json
- uses: actions/upload-artifact@v7
with:
name: asset-release-delta
path: target/asset-release-delta/
- uses: actions/upload-artifact@v7
if: ${{ inputs.dry_run == true && steps.asset-delta.outputs.asset_blobs_changed == 'true' }}
with:
name: asset-release-plan
path: target/asset-release/
- uses: actions/upload-artifact@v7
if: ${{ steps.asset-delta.outputs.changed == 'true' }}
with:
name: asset-channel-preview
path: target/release-channel/
deploy-channel:

Check failure on line 250 in .github/workflows/release-assets.yaml

View workflow run for this annotation

GitHub Actions / Release VM Assets

Invalid workflow file

The workflow is not valid. .github/workflows/release-assets.yaml (Line: 250, Col: 3): Error calling workflow 'google/capsem/.github/workflows/release-channel.yaml@71b166ff5b7b45678a4435ab6fe41dfc82afbcc9'. The nested job 'deploy' is requesting 'deployments: write', but is only allowed 'deployments: none'.
needs: assemble-channel
if: ${{ inputs.dry_run == false && needs.assemble-channel.outputs.asset_changed == 'true' }}
uses: ./.github/workflows/release-channel.yaml
with:
channel: ${{ inputs.channel }}
dist_artifact: asset-channel-preview
secrets: inherit