name: release
on:
push:
tags:
- 'v*'
branches: [main]
permissions:
contents: write
jobs:
release-please:
name: Release Please
if: startsWith(github.ref, 'refs/heads/')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}
build:
needs: release-please
if: >-
always() &&
(startsWith(github.ref, 'refs/tags/') ||
needs.release-please.outputs.releases_created == 'true')
env:
VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || needs.release-please.outputs.tag_name }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-15-intel
target: x86_64-apple-darwin
- os: macos-15
target: aarch64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.VERSION }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Require tag to match Cargo.toml
shell: bash
run: |
set -euo pipefail
package_version=$(sed -n 's/^version = "\([^"]*\)"$/\1/p' Cargo.toml | head -n 1)
test "$VERSION" = "v$package_version"
- name: Build archive and checksum
shell: bash
run: |
set -euo pipefail
cargo build --release --locked --target '${{ matrix.target }}'
archive="tambo-$VERSION-${{ matrix.target }}.tar.gz"
mkdir dist
tar -czf "dist/$archive" -C 'target/${{ matrix.target }}/release' tambo caboodle
if command -v sha256sum >/dev/null; then
(cd dist && sha256sum "$archive" > "$archive.sha256")
else
(cd dist && shasum -a 256 "$archive" > "$archive.sha256")
fi
# One compatibility release for existing Caboodle update-self clients.
legacy="caboodle-$VERSION-${{ matrix.target }}.tar.gz"
cp "dist/$archive" "dist/$legacy"
if command -v sha256sum >/dev/null; then
(cd dist && sha256sum "$legacy" > "$legacy.sha256")
else
(cd dist && shasum -a 256 "$legacy" > "$legacy.sha256")
fi
- name: Build pinned macOS arm64 job runtime
if: matrix.target == 'aarch64-apple-darwin'
shell: bash
run: |
set -euo pipefail
python3 scripts/fetch-macos-job-runtime-inputs.py --directory "$RUNNER_TEMP/job-inputs"
python3 scripts/build-macos-job-runtime.py \
--python-archive "$RUNNER_TEMP/job-inputs/python.tar.gz" \
--shantytown-archive "$RUNNER_TEMP/job-inputs/shantytown.tar.gz" \
--seeds-archive "$RUNNER_TEMP/job-inputs/seeds.tar.gz" \
--output "$RUNNER_TEMP/macos-arm64-job-runtime"
python3 scripts/verify-macos-job-runtime.py --directory "$RUNNER_TEMP/macos-arm64-job-runtime" --native
pin=$(python3 -c 'import json; print(json.load(open("docs/operator-packets/e579/runtime/artifact.json"))["manifest_sha256"])')
'target/${{ matrix.target }}/release/tambo' job-runtime --directory "$RUNNER_TEMP/macos-arm64-job-runtime" --manifest-sha256 "$pin"
cp "$RUNNER_TEMP/macos-arm64-job-runtime.tar.gz" "$RUNNER_TEMP/macos-arm64-job-runtime.tar.gz.sha256" dist/
- uses: actions/upload-artifact@v4
with:
name: release-${{ matrix.target }}
path: dist/*
if-no-files-found: error
publish:
needs: [release-please, build]
if: >-
always() && needs.build.result == 'success' &&
(startsWith(github.ref, 'refs/tags/') ||
needs.release-please.outputs.releases_created == 'true')
runs-on: ubuntu-latest
env:
VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || needs.release-please.outputs.tag_name }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.VERSION }}
- uses: actions/download-artifact@v4
with:
pattern: release-*
path: dist
merge-multiple: true
- name: Verify complete release metadata
env:
TAMBO_EXPECT_TARGETS: x86_64-unknown-linux-gnu x86_64-apple-darwin aarch64-apple-darwin
TAMBO_EXPECT_JOB_RUNTIME: "1"
run: scripts/check-release-assets.sh "$VERSION" dist
- name: Publish immutable GitHub release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
# release-please path: it created a DRAFT release for this tag; attach the
# verified assets, then publish it. Tag-push path: create it as before.
if gh release view "$VERSION" --json isDraft --jq .isDraft 2>/dev/null | grep -qx true; then
gh release upload "$VERSION" dist/* --clobber
gh release edit "$VERSION" --draft=false
else
gh release create "$VERSION" dist/* --verify-tag --generate-notes
fi
publish-crate:
name: Publish Tambo through crates.io Trusted Publishing
needs: [release-please, publish]
if: always() && needs.publish.result == 'success'
runs-on: ubuntu-latest
env:
VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || needs.release-please.outputs.tag_name }}
timeout-minutes: 20
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.VERSION }}
- uses: dtolnay/rust-toolchain@stable
- name: Require the canonical repository and matching tag
shell: bash
run: |
set -euo pipefail
test "$GITHUB_REPOSITORY" = intigraph/tambo
version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "tambo") | .version')
test "$VERSION" = "v$version"
echo "TAMBO_PUBLISH_VERSION=$version" >> "$GITHUB_ENV"
- name: Build and verify the registry package
shell: bash
run: |
set -euo pipefail
cargo package --locked -p tambo
package_sha=$(sha256sum "target/package/tambo-$TAMBO_PUBLISH_VERSION.crate" | awk '{print $1}')
echo "TAMBO_PACKAGE_SHA256=$package_sha" >> "$GITHUB_ENV"
- name: Check whether the account bootstrap already published this version
id: registry
shell: bash
run: |
set -euo pipefail
if scripts/check-crate-version.sh tambo "$TAMBO_PUBLISH_VERSION" "$TAMBO_PACKAGE_SHA256"; then
echo 'already=true' >> "$GITHUB_OUTPUT"
else
rc=$?
test "$rc" = 1
echo 'already=false' >> "$GITHUB_OUTPUT"
fi
- name: Authenticate with crates.io
if: steps.registry.outputs.already != 'true'
id: crates-auth
uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec - name: Publish the verified package
if: steps.registry.outputs.already != 'true'
run: cargo publish --locked -p tambo
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }}
- name: Verify registry visibility
shell: bash
run: |
set -euo pipefail
for attempt in $(seq 1 10); do
if scripts/check-crate-version.sh tambo "$TAMBO_PUBLISH_VERSION" "$TAMBO_PACKAGE_SHA256"; then
exit 0
fi
test "$attempt" = 10 || sleep 15
done
echo 'Registry visibility is UNKNOWN; publication is not accepted.' >&2
exit 1
repository-pendant-admission:
name: Record repository pendant admission
needs: [release-please, publish]
if: always() && needs.publish.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 2
permissions: {}
env:
ADMISSION: ${{ vars.TAMBO_PENDANT_PUBLICATION }}
steps:
- name: Record admission state
run: |
if [ "$ADMISSION" = enabled ]; then
state="admitted: the trusted host publisher must deliver and Verify repository pendant runs"
else
state="NOT ADMITTED: pendant publication not admitted for tambo (no trusted producer), so no pendant is expected and Verify repository pendant is skipped by its condition (not a pass; aegis-dics)"
fi
echo "repository pendant publication: $state"
echo "Repository pendant publication: $state" >> "$GITHUB_STEP_SUMMARY"
repository-pendant:
name: Verify repository pendant
needs: [release-please, publish]
if: always() && needs.publish.result == 'success' && vars.TAMBO_PENDANT_PUBLICATION == 'enabled'
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
env:
RELEASE_TAG: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || needs.release-please.outputs.tag_name }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.RELEASE_TAG }}
fetch-depth: 0
- name: Verify the bounded public pointer corpus
run: python3 scripts/verify-repository-share.py "$RELEASE_TAG" "$(git rev-parse HEAD)"
notify-tap:
name: Notify Homebrew tap
needs: [release-please, publish]
if: >-
always() &&
github.event_name != 'pull_request' &&
needs.publish.result == 'success' &&
(startsWith(github.ref, 'refs/tags/') ||
needs.release-please.outputs.releases_created == 'true')
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
env:
REPO: ${{ github.repository }}
TAG: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || needs.release-please.outputs.tag_name }}
GH_TOKEN: ${{ github.token }}
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_DISPATCH_TOKEN }}
steps:
- name: Dispatch stack-release to scbrown/homebrew-tap
shell: bash
run: |
set -euo pipefail
if [ -z "${TAP_TOKEN:-}" ]; then
echo "::warning title=Homebrew tap not notified::HOMEBREW_TAP_DISPATCH_TOKEN is not set (aegis-7q7b). The tap's hourly schedule will reconcile."
exit 0
fi
if [[ ! "$REPO" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] ||
[[ ! "$TAG" =~ ^([A-Za-z0-9]+-)*v[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
echo "::warning title=Homebrew tap not notified::the release repository or tag is not well formed."
exit 0
fi
# Only a published, non-draft, non-prerelease release is announced.
if ! state="$(gh api "repos/$REPO/releases/tags/$TAG" --jq '"\(.draft) \(.prerelease)"')"; then
echo "::warning title=Homebrew tap not notified::could not read the release for $TAG."
exit 0
fi
if [ "$state" != "false false" ]; then
echo "Release $TAG is not a published stable release (draft prerelease: $state); not notifying."
exit 0
fi
body="$(jq -nc --arg repo "$REPO" --arg tag "$TAG" \
'{event_type: "stack-release", client_payload: {repo: $repo, tag: $tag}}')"
# Fixed URL, no redirects followed (no -L). The token reaches curl on
# stdin (--config -), never on a command line or in the log.
status="$(printf 'header = "Authorization: Bearer %s"\n' "$TAP_TOKEN" |
curl --config - --silent --show-error --fail --proto '=https' --max-time 30 \
--output /dev/null --write-out '%{http_code}' \
--request POST \
--header 'Accept: application/vnd.github+json' \
--header 'X-GitHub-Api-Version: 2022-11-28' \
--data "$body" \
https://api.github.com/repos/scbrown/homebrew-tap/dispatches)" || true
if [ "$status" != 204 ]; then
echo "::warning title=Homebrew tap not notified::dispatch answered HTTP ${status:-none}, not 204. The tap's hourly schedule will reconcile."
exit 0
fi
echo "Notified scbrown/homebrew-tap: $REPO $TAG"