Skip to content

publish

publish #37

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/*,
# packages/agentos-plugin/npm/*, and the build matrices below. Also consumed
# by scripts/publish discovery via SIDECAR_PLATFORMS.
# Linux arm64 is deferred until its portability fix is verified end-to-end.
SIDECAR_PLATFORMS: "linux-x64-gnu darwin-arm64 darwin-x64"
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: darwin-arm64
runner: macos-14
target: aarch64-apple-darwin
- platform: darwin-x64
runner: macos-13
target: x86_64-apple-darwin
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-
# All Rust dependencies resolve from published registries — no sibling
# checkouts: the secure-exec runtime crates from crates.io (0.3.0, which
# vendor the prebuilt V8 bridge) and the RivetKit native-plugin ABI crate
# from crates.io (rivet-actor-plugin-abi). pnpm install is still needed for
# the workspace tooling consumed by the build.
- 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
lib_name: libagentos_actor_plugin.so
- platform: darwin-arm64
runner: macos-14
target: aarch64-apple-darwin
lib_name: libagentos_actor_plugin.dylib
- platform: darwin-x64
runner: macos-13
target: x86_64-apple-darwin
lib_name: libagentos_actor_plugin.dylib
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-
# All Rust deps resolve from crates.io (secure-exec 0.3.0 + the
# rivet-actor-plugin-abi ABI crate) — no sibling checkouts required.
- 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}/${{ matrix.lib_name }}" \
"$out/${{ matrix.lib_name }}"
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
# ---------------------------------------------------------------------------
# publish-npm — place binaries, build TS, publish all packages (all triggers)
# ---------------------------------------------------------------------------
publish-npm:
needs: [context, build-sidecar, build-plugin]
name: "Publish npm"
if: ${{ !cancelled() && needs.build-sidecar.result == 'success' && needs.build-plugin.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]
name: "Release assets"
if: ${{ !cancelled() && needs.build-sidecar.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-arm64]=aarch64-apple-darwin
[darwin-x64]=x86_64-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-
# All Rust deps resolve from crates.io (secure-exec 0.3.0 + the
# rivet-actor-plugin-abi ABI crate) — no sibling checkouts required.
- 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