publish #31
Workflow file for this run
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
| # ============================================================================= | |
| # 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 |