Skip to content

publish

publish #173

Workflow file for this run

name: publish
on:
workflow_dispatch:
inputs:
version:
description: "Version to release (e.g. 0.2.0 or 0.2.0-rc.1). Leave empty for preview publish on the dispatched branch."
required: false
type: string
latest:
description: "Tag as @latest (release only)"
required: true
type: boolean
default: true
concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: false
env:
R2_BUCKET: rivet-releases
R2_ENDPOINT: https://2a94c6a0ced8d35ea63cddc86c2681e7.r2.cloudflarestorage.com
SIDECAR_PLATFORMS: "linux-x64-gnu linux-arm64-gnu darwin-x64 darwin-arm64"
RUST_TOOLCHAIN: "1.91.1"
LINUX_GNU_LLVM_VERSION: "22"
LINUX_GNU_SYSROOT_TAG: "sysroot-20250207"
jobs:
context:
name: Context
runs-on: ubuntu-latest
outputs:
trigger: ${{ steps.ctx.outputs.trigger }}
version: ${{ steps.ctx.outputs.version }}
npm_tag: ${{ steps.ctx.outputs.npm_tag }}
sha: ${{ steps.ctx.outputs.sha }}
latest: ${{ steps.ctx.outputs.latest }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: pnpm install --frozen-lockfile --filter=publish
- id: ctx
name: Resolve publish context
run: pnpm --filter=publish exec tsx src/ci/bin.ts context-output
wasm-commands:
name: WASM Commands
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- uses: dtolnay/rust-toolchain@nightly
with:
targets: wasm32-wasip1
- uses: Swatinem/rust-cache@v2
with:
workspaces: toolchain -> toolchain/target
cache-workspace-crates: true
key: wasm-commands-${{ hashFiles('toolchain/Cargo.lock') }}
- run: pnpm install --frozen-lockfile --filter '@rivet-dev/agentos-runtime-core...'
- run: make -C toolchain commands
- run: node packages/runtime-core/scripts/copy-wasm-commands.mjs --require
- uses: actions/upload-artifact@v4
with:
name: wasm-commands
path: packages/runtime-core/commands
if-no-files-found: error
codex-wasm:
name: Codex WASI
runs-on: [self-hosted, agentos-builder]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2026-03-01
components: rust-src
targets: wasm32-wasip1
- name: Install toolchain build prerequisites
run: |
if ! command -v cmake >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install --yes cmake
fi
- run: make -C toolchain codex
- uses: actions/upload-artifact@v4
with:
name: codex-wasm
path: software/codex/wasm
if-no-files-found: error
build-sidecar:
needs: [context]
name: "Build linux native artifacts (${{ matrix.platform }})"
strategy:
fail-fast: false
matrix:
include:
- platform: linux-x64-gnu
runner: [self-hosted, agentos-builder]
target: x86_64-unknown-linux-gnu
- platform: linux-arm64-gnu
runner: [self-hosted, agentos-builder-arm64]
target: aarch64-unknown-linux-gnu
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
with:
name: agentos-${{ matrix.platform }}
keep-state: ${{ contains(matrix.runner, 'self-hosted') }}
- name: Resolve build profile
id: mode
run: |
set -euo pipefail
if [ "${{ needs.context.outputs.trigger }}" = "release" ]; then
echo "profile=release" >> "$GITHUB_OUTPUT"
else
echo "profile=debug" >> "$GITHUB_OUTPUT"
fi
- name: Build linux artifacts
uses: docker/build-push-action@v6
with:
context: .
file: docker/build/linux-gnu.Dockerfile
build-args: |
TARGET=${{ matrix.target }}
BUILD_PROFILE=${{ steps.mode.outputs.profile }}
CACHE_PLATFORM=${{ matrix.platform }}
RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}
LINUX_GNU_LLVM_VERSION=${{ env.LINUX_GNU_LLVM_VERSION }}
LINUX_GNU_SYSROOT_TAG=${{ env.LINUX_GNU_SYSROOT_TAG }}
tags: agentos-linux-${{ matrix.platform }}
load: true
- name: Extract linux artifacts
id: extract
run: |
set -euo pipefail
sidecar_out="target/sidecar-artifacts/${{ matrix.platform }}"
mkdir -p "$sidecar_out"
cid=$(docker create agentos-linux-${{ matrix.platform }})
docker cp "$cid:/artifacts/agentos-sidecar" "$sidecar_out/agentos-sidecar"
docker cp "$cid:/artifacts/agentos-native-sidecar" "$sidecar_out/agentos-native-sidecar"
docker rm "$cid"
test -f "$sidecar_out/agentos-sidecar"
test -f "$sidecar_out/agentos-native-sidecar"
echo "sidecar_dir=$sidecar_out" >> "$GITHUB_OUTPUT"
- name: Check glibc floor
run: |
scripts/ci/check-linux-glibc-floor.sh \
"${{ steps.extract.outputs.sidecar_dir }}/agentos-sidecar" \
"${{ steps.extract.outputs.sidecar_dir }}/agentos-native-sidecar"
- name: Smoke-run binaries in old Linux containers
run: |
scripts/ci/smoke-linux-artifacts.sh binary \
"${{ steps.extract.outputs.sidecar_dir }}/agentos-sidecar" \
"${{ steps.extract.outputs.sidecar_dir }}/agentos-native-sidecar"
- uses: actions/upload-artifact@v4
with:
name: sidecar-${{ matrix.platform }}
path: ${{ steps.extract.outputs.sidecar_dir }}
if-no-files-found: error
build-sidecar-darwin:
needs: [context]
name: "Build darwin binaries (${{ matrix.platform }})"
strategy:
fail-fast: false
matrix:
include:
- platform: darwin-x64
runner: [self-hosted, agentos-builder]
target: x86_64-apple-darwin
clang: x86_64-apple-darwin20.4
- platform: darwin-arm64
runner: [self-hosted, agentos-builder]
target: aarch64-apple-darwin
clang: aarch64-apple-darwin20.4
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
with:
name: agentos-${{ matrix.platform }}
keep-state: true
- name: Log in to ghcr.io
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Compute build profile
id: mode
run: |
set -euo pipefail
if [ "${{ needs.context.outputs.trigger }}" = "release" ]; then
echo "profile=release" >> "$GITHUB_OUTPUT"
else
echo "profile=debug" >> "$GITHUB_OUTPUT"
fi
- name: Cross-compile via osxcross
uses: docker/build-push-action@v6
with:
context: .
file: docker/build/darwin.Dockerfile
build-args: |
TARGET=${{ matrix.target }}
CLANG=${{ matrix.clang }}
BUILD_PROFILE=${{ steps.mode.outputs.profile }}
CACHE_PLATFORM=${{ matrix.platform }}
RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}
tags: agentos-darwin-${{ matrix.platform }}
load: true
- name: Extract darwin artifacts
run: |
set -euo pipefail
sidecar_out="target/sidecar-artifacts/${{ matrix.platform }}"
mkdir -p "$sidecar_out"
cid=$(docker create agentos-darwin-${{ matrix.platform }})
docker cp "$cid:/artifacts/agentos-sidecar" "$sidecar_out/agentos-sidecar"
docker cp "$cid:/artifacts/agentos-native-sidecar" "$sidecar_out/agentos-native-sidecar"
docker rm "$cid"
test -f "$sidecar_out/agentos-sidecar"
test -f "$sidecar_out/agentos-native-sidecar"
case "${{ matrix.platform }}" in
darwin-x64) expected_arch="x86_64" ;;
darwin-arm64) expected_arch="arm64" ;;
*) echo "unknown darwin platform: ${{ matrix.platform }}" >&2; exit 2 ;;
esac
file "$sidecar_out/agentos-sidecar" | grep -F "$expected_arch"
file "$sidecar_out/agentos-native-sidecar" | grep -F "$expected_arch"
- uses: actions/upload-artifact@v4
with:
name: sidecar-${{ matrix.platform }}
path: target/sidecar-artifacts/${{ matrix.platform }}
if-no-files-found: error
publish-npm:
needs: [context, wasm-commands, codex-wasm, build-sidecar, build-sidecar-darwin]
name: Publish npm
if: ${{ !cancelled() && needs.wasm-commands.result == 'success' && needs.codex-wasm.result == 'success' && needs.build-sidecar.result == 'success' && needs.build-sidecar-darwin.result == 'success' }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@v4
with:
name: wasm-commands
path: packages/runtime-core/commands
- name: Restore canonical WASM command staging
run: |
set -euo pipefail
commands_dir="toolchain/target/wasm32-wasip1/release/commands"
mkdir -p "$commands_dir"
cp -a packages/runtime-core/commands/. "$commands_dir/"
test -s "$commands_dir/sh"
- uses: actions/download-artifact@v4
with:
name: codex-wasm
path: software/codex/wasm
- uses: actions/download-artifact@v4
with:
pattern: sidecar-*
path: artifacts
- name: Place sidecar binaries into platform packages
run: |
set -euo pipefail
for p in $SIDECAR_PLATFORMS; do
agent_bin="artifacts/sidecar-${p}/agentos-sidecar"
runtime_bin="artifacts/sidecar-${p}/agentos-native-sidecar"
agent_dest="packages/sidecar-binary/npm/${p}"
runtime_dest="packages/runtime-sidecar/npm/${p}"
test -f "$agent_bin"
test -f "$runtime_bin"
test -d "$agent_dest"
test -d "$runtime_dest"
cp "$agent_bin" "${agent_dest}/agentos-sidecar"
chmod +x "${agent_dest}/agentos-sidecar"
cp "$runtime_bin" "${runtime_dest}/agentos-native-sidecar"
chmod +x "${runtime_dest}/agentos-native-sidecar"
done
- name: Bump package versions for build
run: |
pnpm --filter=publish exec tsx src/ci/bin.ts bump-versions \
--version ${{ needs.context.outputs.version }} \
--version-only
- run: node packages/runtime-core/scripts/copy-wasm-commands.mjs --require
- name: Build TypeScript packages
env:
AGENTOS_SKIP_NATIVE_META_BUILD: "1"
run: |
# Browser support is retained in-tree but intentionally disabled until
# it has a reactor/security design independent of the native sidecar.
npx turbo build \
--filter='!@rivet-dev/agentos-browser' \
--filter='!@rivet-dev/agentos-runtime-browser' \
--filter='!@rivet-dev/agentos-playground' \
--filter='!./examples/*' \
--filter='!./examples/quickstart/*'
- name: Finalize package versions for publish
run: |
pnpm --filter=publish exec tsx src/ci/bin.ts bump-versions \
--version ${{ needs.context.outputs.version }}
- name: Publish npm packages
env:
NODE_AUTH_TOKEN: ""
run: |
pnpm --filter=publish exec tsx src/ci/bin.ts publish-npm \
--tag ${{ needs.context.outputs.npm_tag }} \
--parallel 16 \
--retries 3 \
${{ needs.context.outputs.trigger == 'release' && '--release-mode' || '' }}
release-assets:
needs: [context, build-sidecar, build-sidecar-darwin]
name: Release assets
if: ${{ !cancelled() && needs.build-sidecar.result == 'success' && needs.build-sidecar-darwin.result == 'success' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: pnpm install --frozen-lockfile --filter=publish
- uses: actions/download-artifact@v4
with:
pattern: sidecar-*
path: artifacts
- name: Stage release assets
env:
VERSION: ${{ needs.context.outputs.version }}
run: |
set -euo pipefail
declare -A PLATFORM_TARGET=(
[linux-x64-gnu]=x86_64-unknown-linux-gnu
[linux-arm64-gnu]=aarch64-unknown-linux-gnu
[darwin-x64]=x86_64-apple-darwin
[darwin-arm64]=aarch64-apple-darwin
)
mkdir -p release-assets
for p in $SIDECAR_PLATFORMS; do
target="${PLATFORM_TARGET[$p]}"
cp "artifacts/sidecar-${p}/agentos-sidecar" "release-assets/agentos-sidecar-${target}"
cp "artifacts/sidecar-${p}/agentos-native-sidecar" "release-assets/agentos-native-sidecar-${target}"
done
chmod +x release-assets/agentos-sidecar-* release-assets/agentos-native-sidecar-*
if [ -d crates/execution/assets/pyodide ]; then
mkdir -p release-assets/pyodide
cp -R crates/execution/assets/pyodide/. release-assets/pyodide/
fi
- name: Create GitHub release and upload assets
if: ${{ needs.context.outputs.trigger == 'release' }}
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.context.outputs.version }}
NPM_TAG: ${{ needs.context.outputs.npm_tag }}
run: |
set -euo pipefail
if ! gh release view "v${VERSION}" >/dev/null 2>&1; then
PRERELEASE=""
if [ "${NPM_TAG}" = "rc" ]; then PRERELEASE="--prerelease"; fi
gh release create "v${VERSION}" --title "v${VERSION}" --generate-notes $PRERELEASE
fi
# Upload FILES only — a bare `release-assets/*` glob matches the
# pyodide directory itself and `gh release upload` cannot upload dirs.
find release-assets -maxdepth 1 -type f -print0 | xargs -0 gh release upload "v${VERSION}" --clobber
if [ -d release-assets/pyodide ]; then
find release-assets/pyodide -maxdepth 1 -type f -print0 | xargs -0 gh release upload "v${VERSION}" --clobber
fi
- name: Upload release assets to R2
if: ${{ needs.context.outputs.trigger == 'release' }}
env:
R2_RELEASES_ACCESS_KEY_ID: ${{ secrets.R2_RELEASES_ACCESS_KEY_ID }}
R2_RELEASES_SECRET_ACCESS_KEY: ${{ secrets.R2_RELEASES_SECRET_ACCESS_KEY }}
VERSION: ${{ needs.context.outputs.version }}
SHA: ${{ needs.context.outputs.sha }}
LATEST: ${{ needs.context.outputs.latest }}
run: |
set -uo pipefail
if [ -z "${R2_RELEASES_ACCESS_KEY_ID:-}" ] || [ -z "${R2_RELEASES_SECRET_ACCESS_KEY:-}" ]; then
echo "R2 credentials not configured; skipping R2 upload."
exit 0
fi
set -e
pnpm --filter=publish exec tsx src/ci/bin.ts upload-r2 \
--source "$GITHUB_WORKSPACE/release-assets" --sha "$SHA"
pnpm --filter=publish exec tsx src/ci/bin.ts copy-r2 \
--sha "$SHA" --version "$VERSION" --latest "$LATEST"
publish-crates:
needs: [context, build-sidecar, release-assets]
name: Publish crates.io
if: ${{ !cancelled() && needs.build-sidecar.result == 'success' && (needs.release-assets.result == 'success' || needs.release-assets.result == 'skipped') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: . -> target
- uses: actions/cache@v4
with:
path: ~/.cargo/.rusty_v8
key: ${{ runner.os }}-x86_64-unknown-linux-gnu-rusty-v8-${{ hashFiles('Cargo.lock') }}
restore-keys: |
${{ runner.os }}-x86_64-unknown-linux-gnu-rusty-v8-
- run: pnpm install --frozen-lockfile
- name: Bump Cargo versions
run: |
pnpm --filter=publish exec tsx src/ci/bin.ts bump-versions \
--version ${{ needs.context.outputs.version }} \
--version-only
- name: Stage vendored V8 bridge bundles and base filesystem
run: |
set -euo pipefail
# crates/{execution,v8-runtime}/assets/generated is gitignored; the
# published crates fall back to the vendored bundle (build-support
# copy_vendored_bundle) and panic if it is absent. Stage it here.
for crate in execution v8-runtime; do
out="crates/${crate}/assets/generated"
mkdir -p "$out"
node packages/build-tools/scripts/build-v8-bridge.mjs --out-dir "$out"
done
git add -f crates/execution/assets/generated crates/v8-runtime/assets/generated
# The single committed base filesystem lives in the vfs crate; the
# kernel and native-sidecar crates vendor a copy for isolated publish
# builds (their build.rs falls back to assets/base-filesystem.json).
mkdir -p crates/kernel/assets crates/native-sidecar/assets
cp crates/vfs/assets/base-filesystem.json crates/kernel/assets/base-filesystem.json
cp crates/vfs/assets/base-filesystem.json crates/native-sidecar/assets/base-filesystem.json
- name: Dry-run crate publish
if: ${{ needs.context.outputs.trigger != 'release' }}
run: |
pnpm --filter=publish exec tsx src/ci/bin.ts publish-crates \
--version ${{ needs.context.outputs.version }} \
--dry-run \
--allow-dirty
- name: Publish crates
if: ${{ needs.context.outputs.trigger == 'release' }}
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
pnpm --filter=publish exec tsx src/ci/bin.ts publish-crates \
--version ${{ needs.context.outputs.version }} \
--allow-dirty