Skip to content

publish

publish #31

Workflow file for this run

# =============================================================================
# Publish
# =============================================================================
#
# Single workflow for preview publish AND release cuts. Both triggers are
# manual (workflow_dispatch). The build + npm publish steps run identically for
# both modes. Only the build profile (debug vs release), npm dist-tag, and the
# release-only tail (crates.io, GitHub release assets, R2, git tag) differ.
#
# Triggers and mapping:
# workflow_dispatch (no version) → trigger=branch npm_tag=<sanitized branch> build=debug
# workflow_dispatch (with version) → trigger=release npm_tag=latest build=release
# (npm_tag becomes `rc` if version contains `-rc.`, or
# `next` if `latest=false`)
#
# Preview publishes are fast: debug sidecar build, npm only (the binary ships
# inside the npm platform package), crates dry-run. Releases add the crates.io
# publish, GitHub release assets, R2 mirror, and git tag.
# =============================================================================
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
# Platform list kept in sync with packages/*sidecar*/npm/* and the build
# matrix below. Also consumed by scripts/publish discovery via SIDECAR_PLATFORMS.
SIDECAR_PLATFORMS: "linux-x64-gnu linux-arm64-gnu darwin-x64 darwin-arm64"
jobs:
# ---------------------------------------------------------------------------
# context — resolve PublishContext once, pin as job outputs
# ---------------------------------------------------------------------------
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: 22
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Install publish scripts
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
# ---------------------------------------------------------------------------
# build-sidecar — debug (preview) or release (release) sidecar binaries
# ---------------------------------------------------------------------------
build-sidecar:
needs: [context]
name: "Build sidecar (${{ matrix.platform }})"
strategy:
fail-fast: false
matrix:
include:
- platform: linux-x64-gnu
runner: ubuntu-22.04
target: x86_64-unknown-linux-gnu
- platform: linux-arm64-gnu
runner: ubuntu-22.04-arm
target: aarch64-unknown-linux-gnu
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: . -> target
key: ${{ matrix.target }}-${{ needs.context.outputs.trigger }}
- uses: actions/cache@v4
with:
path: ~/.cargo/.rusty_v8
key: ${{ runner.os }}-${{ matrix.target }}-rusty-v8-${{ hashFiles('Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-rusty-v8-
# Preview sidecar builds use the matching secure-exec branch through
# Cargo path deps. pnpm install is needed in both workspaces because the
# secure-exec V8 bridge build.rs invokes the TS bridge bundler.
- name: Checkout secure-exec sibling
run: |
git clone --depth 1 --branch nathan/workspace-binding-agentos \
https://github.com/rivet-dev/secure-exec.git ../secure-exec
(cd ../secure-exec && pnpm install --frozen-lockfile --filter='!@secure-exec/website')
- run: pnpm install --frozen-lockfile
- name: Build sidecar binary
id: build
run: |
set -euo pipefail
out="target/sidecar-artifacts/${{ matrix.platform }}"
mkdir -p "$out"
if [ "${{ needs.context.outputs.trigger }}" = "release" ]; then
cargo build --release -p agentos-sidecar --target ${{ matrix.target }}
profile="release"
else
cargo build -p agentos-sidecar --target ${{ matrix.target }}
profile="debug"
fi
cp "target/${{ matrix.target }}/${profile}/agentos-sidecar" "$out/agentos-sidecar"
echo "dir=$out" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: sidecar-${{ matrix.platform }}
path: ${{ steps.build.outputs.dir }}
if-no-files-found: error
# ---------------------------------------------------------------------------
# build-plugin — agent-os actor plugin cdylib (debug preview / release)
# ---------------------------------------------------------------------------
# Ships inside the @rivet-dev/agentos-plugin-<platform> npm packages, declared
# as optionalDependencies of @rivet-dev/agentos. The cdylib path-depends on the
# secure-exec crates (sibling repo) AND the RivetKit native-plugin ABI crate
# (r6 sibling, not on crates.io yet), so both siblings are cloned for cargo to
# resolve the workspace path deps.
build-plugin:
needs: [context]
name: "Build plugin (${{ matrix.platform }})"
strategy:
fail-fast: false
matrix:
include:
- platform: linux-x64-gnu
runner: ubuntu-22.04
target: x86_64-unknown-linux-gnu
- platform: linux-arm64-gnu
runner: ubuntu-22.04-arm
target: aarch64-unknown-linux-gnu
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: . -> target
key: plugin-${{ matrix.target }}-${{ needs.context.outputs.trigger }}
- uses: actions/cache@v4
with:
path: ~/.cargo/.rusty_v8
key: ${{ runner.os }}-${{ matrix.target }}-rusty-v8-${{ hashFiles('Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-rusty-v8-
# Preview plugin builds use the matching secure-exec branch through Cargo
# path deps, mirroring the sidecar build.
- name: Checkout secure-exec sibling
run: |
git clone --depth 1 --branch nathan/workspace-binding-agentos \
https://github.com/rivet-dev/secure-exec.git ../secure-exec
(cd ../secure-exec && pnpm install --frozen-lockfile --filter='!@secure-exec/website')
- run: pnpm install --frozen-lockfile
- name: Build plugin cdylib
id: build
run: |
set -euo pipefail
out="target/plugin-artifacts/${{ matrix.platform }}"
mkdir -p "$out"
if [ "${{ needs.context.outputs.trigger }}" = "release" ]; then
cargo build --release -p agentos-actor-plugin --target ${{ matrix.target }}
profile="release"
else
cargo build -p agentos-actor-plugin --target ${{ matrix.target }}
profile="debug"
fi
cp "target/${{ matrix.target }}/${profile}/libagentos_actor_plugin.so" \
"$out/libagentos_actor_plugin.so"
echo "dir=$out" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: plugin-${{ matrix.platform }}
path: ${{ steps.build.outputs.dir }}
if-no-files-found: error
# darwin via osxcross in a Linux container (rivet's approach) — no macOS
# runners. The image builds both the sidecar binary and actor plugin cdylib.
build-sidecar-darwin:
needs: [context]
name: "Build darwin binaries (${{ matrix.platform }})"
strategy:
fail-fast: false
matrix:
include:
- platform: darwin-x64
target: x86_64-apple-darwin
clang: x86_64-apple-darwin20.4
- platform: darwin-arm64
target: aarch64-apple-darwin
clang: aarch64-apple-darwin20.4
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Log in to ghcr.io (pull the osxcross base image)
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Cross-compile via osxcross
run: |
set -euo pipefail
DOCKER_BUILDKIT=1 docker build \
-f docker/build/darwin.Dockerfile \
--build-arg TARGET=${{ matrix.target }} \
--build-arg CLANG=${{ matrix.clang }} \
--build-arg TRIGGER=${{ needs.context.outputs.trigger }} \
-t agentos-darwin-${{ matrix.platform }} .
sidecar_out="target/sidecar-artifacts/${{ matrix.platform }}"
plugin_out="target/plugin-artifacts/${{ matrix.platform }}"
mkdir -p "$sidecar_out" "$plugin_out"
cid=$(docker create agentos-darwin-${{ matrix.platform }})
docker cp "$cid:/artifacts/agentos-sidecar" "$sidecar_out/agentos-sidecar"
docker cp "$cid:/artifacts/libagentos_actor_plugin.dylib" "$plugin_out/libagentos_actor_plugin.dylib"
docker rm "$cid"
test -f "$sidecar_out/agentos-sidecar"
test -f "$plugin_out/libagentos_actor_plugin.dylib"
- uses: actions/upload-artifact@v4
with:
name: sidecar-${{ matrix.platform }}
path: target/sidecar-artifacts/${{ matrix.platform }}
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: plugin-${{ matrix.platform }}
path: target/plugin-artifacts/${{ matrix.platform }}
if-no-files-found: error
# ---------------------------------------------------------------------------
# publish-npm — place binaries, build TS, publish all packages (all triggers)
# ---------------------------------------------------------------------------
publish-npm:
needs: [context, build-sidecar, build-plugin, build-sidecar-darwin]
name: "Publish npm"
if: ${{ !cancelled() && needs.build-sidecar.result == 'success' && needs.build-plugin.result == 'success' && needs.build-sidecar-darwin.result == 'success' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
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:
pattern: sidecar-*
path: artifacts
- uses: actions/download-artifact@v4
with:
pattern: plugin-*
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"
agent_dest="packages/sidecar-binary/npm/${p}"
if [ ! -f "$agent_bin" ]; then
echo "::error::missing agentos-sidecar binary artifact for ${p}"
exit 1
fi
if [ ! -d "$agent_dest" ]; then
echo "::error::missing platform package dir $agent_dest"
exit 1
fi
cp "$agent_bin" "${agent_dest}/agentos-sidecar"
chmod +x "${agent_dest}/agentos-sidecar"
echo "Placed binaries for ${p}"
done
- name: Place plugin cdylib into platform packages
run: |
set -euo pipefail
for p in $SIDECAR_PLATFORMS; do
case "$p" in
darwin-*) lib_name="libagentos_actor_plugin.dylib" ;;
*) lib_name="libagentos_actor_plugin.so" ;;
esac
lib="artifacts/plugin-${p}/${lib_name}"
dest="packages/agentos-plugin/npm/${p}"
if [ ! -f "$lib" ]; then
echo "::error::missing plugin cdylib artifact for ${p}"
exit 1
fi
if [ ! -d "$dest" ]; then
echo "::error::missing plugin platform package dir $dest"
exit 1
fi
cp "$lib" "${dest}/${lib_name}"
echo "Placed plugin cdylib for ${p}"
done
- name: Bump package versions for build (version-only)
run: |
pnpm --filter=publish exec tsx src/ci/bin.ts bump-versions \
--version ${{ needs.context.outputs.version }} \
--version-only
- name: Build TypeScript packages
run: |
npx turbo build \
--filter='!@rivet-dev/agentos-playground' \
--filter='!@agentos/website' \
--filter='!./examples/*'
- name: Finalize package versions for publish (inject optionalDeps)
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: ${{ secrets.NPM_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 — GitHub release + sidecar/pyodide assets + R2 (release only)
# ---------------------------------------------------------------------------
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' && needs.context.outputs.trigger == 'release' }}
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: 22
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
)
# Stage release assets: platform binaries + externalized pyodide assets
# (downloaded by the published execution crate's build.rs).
mkdir -p release-assets
for p in $SIDECAR_PLATFORMS; do
agent_bin="artifacts/sidecar-${p}/agentos-sidecar"
if [ -f "$agent_bin" ]; then
target="${PLATFORM_TARGET[$p]}"
cp "$agent_bin" "release-assets/agentos-sidecar-${target}"
else
echo "::warning::missing agentos-sidecar binary for ${p}"
fi
done
for f in \
pyodide.asm.wasm \
pyodide.asm.js \
python_stdlib.zip \
numpy-2.2.5-cp313-cp313-pyodide_2025_0_wasm32.whl \
pandas-2.3.3-cp313-cp313-pyodide_2025_0_wasm32.whl; do
cp "crates/execution/assets/pyodide/${f}" "release-assets/${f}"
done
- name: Create GitHub release and upload assets
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
# --clobber so re-runs overwrite assets idempotently.
gh release upload "v${VERSION}" release-assets/* --clobber
- name: Upload sidecar binaries to R2 (best-effort)
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 (GitHub release assets are authoritative)."
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 — crates.io (dry-run on preview, full publish on release)
# ---------------------------------------------------------------------------
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: 22
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-
- name: Checkout secure-exec sibling
run: |
git clone --depth 1 --branch nathan/workspace-binding-agentos \
https://github.com/rivet-dev/secure-exec.git ../secure-exec
(cd ../secure-exec && pnpm install --frozen-lockfile --filter='!@secure-exec/website')
- 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: Dry-run crate publish (preview)
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 (release)
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