BAML Language Release #2101
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
| name: BAML Language Release | |
| # Channel-aware replacement for the legacy BAML language release workflow. | |
| # Release workflows run packaging smoke tests only; the full suite runs in CI. | |
| on: | |
| workflow_run: | |
| workflows: ["CI - BAML Language"] | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| channel: | |
| description: "Release channel to plan" | |
| type: choice | |
| options: [nightly, canary] | |
| default: nightly | |
| required: true | |
| dry_run: | |
| description: "Build and publish only dry-run artifacts/manifests" | |
| type: boolean | |
| default: true | |
| required: true | |
| permissions: | |
| contents: write | |
| actions: read | |
| id-token: write | |
| concurrency: | |
| group: ${{ inputs.dry_run == true && 'baml-language-release-dryrun' || 'baml-language-release-canary' }} | |
| cancel-in-progress: false | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_NET_RETRY: 10 | |
| CARGO_TERM_COLOR: always | |
| RUSTUP_MAX_RETRIES: 10 | |
| AWS_REGION: us-east-1 | |
| AWS_ROLE_ARN: arn:aws:iam::277707123528:role/pkg-boundaryml-com-github-release | |
| PKG_BUCKET: pkgboundarymlcomstack-pkgboundarymlcomsitebucket4f-ybhvygkqittp | |
| jobs: | |
| plan: | |
| name: Plan release | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'canary') | |
| runs-on: ubuntu-latest | |
| outputs: | |
| channel: ${{ steps.plan.outputs.channel }} | |
| version: ${{ steps.plan.outputs.version }} | |
| pypi_version: ${{ steps.plan.outputs.pypi_version }} | |
| crates_io_version: ${{ steps.plan.outputs.crates_io_version }} | |
| git_tag: ${{ steps.plan.outputs.git_tag }} | |
| release_plan_json: ${{ steps.plan.outputs.release_plan_json }} | |
| source_sha: ${{ steps.plan.outputs.source_sha }} | |
| source_branch: ${{ steps.plan.outputs.source_branch }} | |
| dry_run: ${{ steps.plan.outputs.dry_run }} | |
| wrapper_version: ${{ steps.wrapper.outputs.wrapper_version }} | |
| wrapper_changed: ${{ steps.wrapper.outputs.wrapper_changed }} | |
| dispatch_nightly_after_canary: ${{ steps.plan.outputs.dispatch_nightly_after_canary }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} | |
| fetch-depth: 2 | |
| persist-credentials: false | |
| - name: Compute release plan | |
| id: plan | |
| env: | |
| INPUT_CHANNEL: ${{ inputs.channel }} | |
| INPUT_DRY_RUN: ${{ inputs.dry_run }} | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| channel="${INPUT_CHANNEL:-nightly}" | |
| dry_run="${INPUT_DRY_RUN:-false}" | |
| source_branch="${GITHUB_REF_NAME:-}" | |
| dispatch_nightly_after_canary="false" | |
| if [[ "${{ github.event_name }}" == "workflow_run" ]]; then | |
| channel="nightly" | |
| source_branch="${{ github.event.workflow_run.head_branch }}" | |
| if git rev-parse HEAD^ >/dev/null 2>&1 && git diff --quiet HEAD^ HEAD -- baml_language/release.toml; then | |
| : | |
| elif git rev-parse HEAD^ >/dev/null 2>&1; then | |
| canary_version="$(scripts/baml-language-version compute --channel canary)" | |
| if ! gh release view "baml-language-${canary_version}" >/dev/null 2>&1; then | |
| channel="canary" | |
| dispatch_nightly_after_canary="true" | |
| fi | |
| fi | |
| fi | |
| scripts/baml-language-version plan --channel "$channel" --out release-plan.json | |
| source_sha="$(git rev-parse HEAD)" | |
| version="$(jq -r .canonical_version release-plan.json)" | |
| pypi_version="$(jq -r .registry_versions.pypi release-plan.json)" | |
| crates_io_version="$(jq -r .registry_versions.crates_io release-plan.json)" | |
| git_tag="$(jq -r .git_tag release-plan.json)" | |
| { | |
| echo "channel=$channel" | |
| echo "version=$version" | |
| echo "pypi_version=$pypi_version" | |
| echo "crates_io_version=$crates_io_version" | |
| echo "git_tag=$git_tag" | |
| echo "release_plan_json<<EOF" | |
| cat release-plan.json | |
| echo "EOF" | |
| echo "source_sha=$source_sha" | |
| echo "source_branch=$source_branch" | |
| echo "dry_run=$dry_run" | |
| echo "dispatch_nightly_after_canary=$dispatch_nightly_after_canary" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Check wrapper version | |
| id: wrapper | |
| run: | | |
| set -euo pipefail | |
| wrapper_version="$(python3 - <<'PY' | |
| import tomllib | |
| from pathlib import Path | |
| print(tomllib.loads(Path("baml_language/crates/baml/Cargo.toml").read_text())["package"]["version"]) | |
| PY | |
| )" | |
| latest="" | |
| if curl -fsSL https://pkg.boundaryml.com/manifest/v1/wrapper.json -o wrapper.json; then | |
| latest="$(jq -r .version wrapper.json)" | |
| fi | |
| changed=$([[ "$wrapper_version" != "$latest" ]] && echo true || echo false) | |
| { | |
| echo "wrapper_version=$wrapper_version" | |
| echo "wrapper_changed=$changed" | |
| } >> "$GITHUB_OUTPUT" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-plan | |
| path: release-plan.json | |
| # The toolchain/wrapper build matrix (and, over time, the others) is generated | |
| # from the repository's platform contract (`release/platforms.json`) so the | |
| # target set + per-target runners live in one machine-readable place. | |
| build-matrix: | |
| name: Compute build matrices | |
| needs: [plan] | |
| runs-on: ubuntu-latest | |
| outputs: | |
| toolchain: ${{ steps.gen.outputs.toolchain }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Generate build matrices from the platform contract | |
| id: gen | |
| run: | | |
| set -euo pipefail | |
| # target + toolchain runner (as `os`); `libc` rides along so the musl | |
| # setup step can key on it. | |
| toolchain="$(jq -c '[.targets[] | |
| | select(.artifacts.toolchain != null) | |
| | {target: .triple, os: .artifacts.toolchain.runner, libc: .libc}]' release/platforms.json)" | |
| echo "toolchain=$toolchain" >> "$GITHUB_OUTPUT" | |
| jq . <<<"$toolchain" | |
| build-toolchain: | |
| name: Build toolchain (${{ matrix.target }}) | |
| needs: [plan, build-vsix, build-matrix] | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: ${{ fromJson(needs.build-matrix.outputs.toolchain) }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: release-plan | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: baml-vscode-vsix | |
| path: vsix | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: baml-playground-dist | |
| path: playground-dist | |
| - name: Stamp version | |
| run: scripts/baml-language-version stamp --plan release-plan.json | |
| - name: Setup Rust | |
| uses: ./.github/actions/setup-rust | |
| with: | |
| targets: ${{ matrix.target }} | |
| workspace: baml_language | |
| prefix-key: v5-rust-baml-language-toolchain-${{ matrix.target }} | |
| cache-on-failure: true | |
| enable-cross: false | |
| skip-protoc: true | |
| - name: Setup musl cross toolchain | |
| if: ${{ matrix.libc == 'musl' }} | |
| uses: ./.github/actions/setup-musl-cross | |
| with: | |
| target: ${{ matrix.target }} | |
| - name: Build CLI and pack host | |
| run: cargo build --manifest-path baml_language/Cargo.toml --release --bin baml-cli --bin baml-pack-host --target "${{ matrix.target }}" | |
| - name: Archive layout smoke | |
| run: | | |
| set -euo pipefail | |
| version="${{ needs.plan.outputs.version }}" | |
| target="${{ matrix.target }}" | |
| mkdir -p "staging/bin" "staging/assets" | |
| suffix="" | |
| if [[ "$target" == *windows-msvc ]]; then suffix=".exe"; fi | |
| cp "baml_language/target/$target/release/baml-cli$suffix" "staging/bin/baml-cli$suffix" | |
| cp "baml_language/target/$target/release/baml-pack-host$suffix" "staging/bin/baml-pack-host$suffix" | |
| vsix="$(find vsix -maxdepth 1 -name '*.vsix' -print -quit)" | |
| test -n "$vsix" | |
| cp "$vsix" "staging/assets/baml-vscode.vsix" | |
| mkdir -p "staging/assets/playground" | |
| cp -R playground-dist/. "staging/assets/playground/" | |
| if test -f "staging/bin/baml$suffix"; then | |
| exit 1 | |
| fi | |
| test -f "staging/assets/baml-vscode.vsix" | |
| test -f "staging/assets/playground/index.html" | |
| test -f "staging/assets/playground/assets/index.js" | |
| test -f "staging/assets/playground/assets/index.css" | |
| echo "$version" > staging/VERSION | |
| if [[ "$target" == *windows-msvc ]]; then | |
| (cd staging && 7z a "../baml-language-$version-$target.zip" .) | |
| else | |
| tar -C staging -czf "baml-language-$version-$target.tar.gz" . | |
| fi | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: toolchain-${{ matrix.target }} | |
| path: baml-language-${{ needs.plan.outputs.version }}-${{ matrix.target }}.* | |
| build-vsix: | |
| name: Build platform-neutral VSIX | |
| needs: [plan] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: release-plan | |
| - run: scripts/baml-language-version stamp --plan release-plan.json | |
| - uses: ./.github/actions/setup-node2 | |
| - name: Setup Rust for VSIX WASM build | |
| uses: ./.github/actions/setup-rust | |
| with: | |
| targets: wasm32-unknown-unknown | |
| workspace: baml_language | |
| prefix-key: v5-rust-baml-vsix-wasm | |
| cache-on-failure: true | |
| enable-wasm: true | |
| enable-cross: false | |
| skip-protoc: true | |
| - run: pnpm --dir typescript2 run vscode:package | |
| - name: Normalize VSIX asset name | |
| run: | | |
| set -euo pipefail | |
| version="${{ needs.plan.outputs.version }}" | |
| vsix_files=() | |
| while IFS= read -r vsix; do | |
| vsix_files+=("$vsix") | |
| done < <(find typescript2/app-vscode-ext -maxdepth 1 -type f -name '*.vsix' -print) | |
| if [[ "${#vsix_files[@]}" -ne 1 ]]; then | |
| printf 'expected exactly one VSIX, found %s:\n' "${#vsix_files[@]}" >&2 | |
| printf '%s\n' "${vsix_files[@]}" >&2 | |
| exit 1 | |
| fi | |
| vsix="${vsix_files[0]}" | |
| dest="typescript2/app-vscode-ext/baml-language-$version.vsix" | |
| if [[ "$vsix" != "$dest" ]]; then | |
| mv "$vsix" "$dest" | |
| fi | |
| - name: Reject bundled native binaries | |
| run: | | |
| set -euo pipefail | |
| vsix="typescript2/app-vscode-ext/baml-language-${{ needs.plan.outputs.version }}.vsix" | |
| unzip -l "$vsix" | tee vsix-list.txt | |
| ! grep -E 'dist/baml-cli|baml-cli|baml-pack-host' vsix-list.txt | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: baml-vscode-vsix | |
| path: typescript2/app-vscode-ext/*.vsix | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: baml-playground-dist | |
| path: typescript2/app-vscode-webview/dist | |
| build-wrapper: | |
| name: Build wrapper (${{ matrix.target }}) | |
| # The wrapper is a native release build of the workspace, so it uses the | |
| # same per-target runners as the toolchain and reuses that generated matrix. | |
| needs: [plan, build-matrix] | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: ${{ fromJson(needs.build-matrix.outputs.toolchain) }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Setup Rust | |
| uses: ./.github/actions/setup-rust | |
| with: | |
| targets: ${{ matrix.target }} | |
| workspace: baml_language | |
| prefix-key: v5-rust-baml-wrapper-${{ matrix.target }} | |
| cache-on-failure: true | |
| enable-cross: false | |
| skip-protoc: true | |
| - name: Setup musl cross toolchain | |
| if: ${{ matrix.libc == 'musl' }} | |
| uses: ./.github/actions/setup-musl-cross | |
| with: | |
| target: ${{ matrix.target }} | |
| - name: Build wrapper | |
| run: cargo build --manifest-path baml_language/Cargo.toml --release --bin baml --target "${{ matrix.target }}" | |
| - name: Archive wrapper | |
| run: | | |
| set -euo pipefail | |
| version="${{ needs.plan.outputs.wrapper_version }}" | |
| target="${{ matrix.target }}" | |
| mkdir -p staging/bin | |
| suffix="" | |
| if [[ "$target" == *windows-msvc ]]; then suffix=".exe"; fi | |
| cp "baml_language/target/$target/release/baml$suffix" "staging/bin/baml$suffix" | |
| if test -f "staging/bin/baml-cli$suffix"; then | |
| exit 1 | |
| fi | |
| if [[ "$target" == *windows-msvc ]]; then | |
| (cd staging && 7z a "../baml-wrapper-$version-$target.zip" .) | |
| else | |
| tar -C staging -czf "baml-wrapper-$version-$target.tar.gz" . | |
| fi | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: wrapper-${{ matrix.target }} | |
| path: baml-wrapper-${{ needs.plan.outputs.wrapper_version }}-${{ matrix.target }}.* | |
| build-python-sdk: | |
| name: Build Python SDK | |
| needs: [plan] | |
| uses: ./.github/workflows/build2-python-sdk.reusable.yaml | |
| permissions: | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| build-nodejs-sdk: | |
| name: Build Node.js SDK | |
| needs: [plan] | |
| uses: ./.github/workflows/build2-nodejs-sdk.reusable.yaml | |
| permissions: | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| build-web-sdk: | |
| name: Build Web SDK | |
| needs: [plan] | |
| uses: ./.github/workflows/build2-web-sdk.reusable.yaml | |
| permissions: | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| # Language-neutral engine cdylib, shared by every dylib-loader SDK. | |
| build-bridge-cffi: | |
| name: Build bridge_cffi cdylib | |
| needs: [plan] | |
| uses: ./.github/workflows/build2-bridge-cffi.reusable.yaml | |
| permissions: | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| build-go-sdk: | |
| name: Build Go SDK mirror | |
| needs: [plan] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: release-plan | |
| - name: Stamp version | |
| run: scripts/baml-language-version stamp --plan release-plan.json | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: baml_language/sdks/go/baml_go/go.mod | |
| cache-dependency-path: baml_language/sdks/go/baml_go/go.sum | |
| - name: Assemble and test public module | |
| run: | | |
| set -euo pipefail | |
| scripts/assemble-go-sdk-mirror --out "$RUNNER_TEMP/go-sdk-mirror" | |
| cd "$RUNNER_TEMP/go-sdk-mirror" | |
| test "$(go list -m)" = "github.com/boundaryml/baml-go" | |
| go test ./... | |
| go vet ./... | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: go-sdk-mirror | |
| path: ${{ runner.temp }}/go-sdk-mirror | |
| if-no-files-found: error | |
| # Verify-only: the C++ SDK ships no artifact (headers are vendored at | |
| # generate time); it consumes the shared bridge_cffi artifacts. | |
| verify-cpp-sdk: | |
| name: Verify C++ SDK | |
| needs: [plan, build-bridge-cffi] | |
| uses: ./.github/workflows/verify-cpp-sdk.reusable.yaml | |
| permissions: | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| build-rust-sdk: | |
| name: Build Rust SDK | |
| needs: [plan] | |
| uses: ./.github/workflows/build2-rust-sdk.reusable.yaml | |
| permissions: | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| verify-rust-sdk: | |
| name: Verify Rust SDK | |
| needs: [plan, build-toolchain, build-bridge-cffi, build-rust-sdk] | |
| uses: ./.github/workflows/verify-rust-sdk.reusable.yaml | |
| permissions: | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| all-builds: | |
| name: All builds gate | |
| runs-on: ubuntu-latest | |
| needs: | |
| - plan | |
| - build-toolchain | |
| - build-vsix | |
| - build-wrapper | |
| - build-python-sdk | |
| - build-nodejs-sdk | |
| - build-web-sdk | |
| # The engine cdylib is required — it is the shared artifact every | |
| # dylib-loader SDK depends on. | |
| - build-bridge-cffi | |
| - build-go-sdk | |
| - verify-cpp-sdk | |
| # The Rust SDK is production-ready (crates.io publisher live), so its | |
| # build + consumer verification gate the release: a failure here blocks | |
| # publishing rather than shipping a release the Rust SDK isn't part of. | |
| - build-rust-sdk | |
| - verify-rust-sdk | |
| steps: | |
| - run: echo "all release build jobs completed" | |
| publish-pypi: | |
| name: Publish Python PyPI | |
| needs: [plan, all-builds] | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| # Registry OIDC jobs stay in this top-level workflow because PyPI trusted | |
| # publishing is bound to the workflow identity that performs the upload. | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: python-sdk-* | |
| path: dist | |
| merge-multiple: true | |
| - name: Verify wheel count | |
| run: | | |
| set -euo pipefail | |
| find dist -maxdepth 1 -type f -print | |
| wheel_count=$(find dist -maxdepth 1 -type f -name '*.whl' | wc -l | tr -d ' ') | |
| if [ "$wheel_count" -lt 8 ]; then | |
| echo "::error::Expected at least 8 wheels, found $wheel_count" | |
| exit 1 | |
| fi | |
| echo "Found $wheel_count wheels" | |
| - name: Publish package to PyPI | |
| # Configure PyPI trusted publishing for this workflow filename: | |
| # release-baml-language.yml. Leave the PyPI environment blank unless | |
| # this job grows an explicit GitHub Actions environment. | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ | |
| skip-existing: true | |
| publish-nodejs-sdk: | |
| name: Publish Node.js npm | |
| needs: [plan, build-nodejs-sdk, all-builds] | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| # npm OIDC is pinned to release-baml-language.yml | |
| uses: ./.github/workflows/publish2-nodejs-sdk.yaml | |
| permissions: | |
| id-token: write | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| channel: ${{ needs.plan.outputs.channel }} | |
| publish-web-sdk: | |
| name: Publish Web npm | |
| needs: [plan, build-web-sdk, all-builds] | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| # npm OIDC is pinned to release-baml-language.yml | |
| uses: ./.github/workflows/publish2-web-sdk.yaml | |
| permissions: | |
| id-token: write | |
| contents: read | |
| with: | |
| release_plan_json: ${{ needs.plan.outputs.release_plan_json }} | |
| source_sha: ${{ needs.plan.outputs.source_sha }} | |
| channel: ${{ needs.plan.outputs.channel }} | |
| publish-toolchain-release: | |
| name: Publish toolchain release assets | |
| needs: [plan, all-builds] | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_REPO: ${{ github.repository }} | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: toolchain-* | |
| path: dist/toolchain | |
| merge-multiple: true | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: baml-vscode-vsix | |
| path: dist/vsix | |
| - name: Generate checksum sidecars | |
| run: | | |
| set -euo pipefail | |
| find dist -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.vsix' \) -print0 | | |
| while IFS= read -r -d '' file; do | |
| (cd "$(dirname "$file")" && sha256sum "$(basename "$file")" > "$(basename "$file").sha256") | |
| done | |
| - name: Publish toolchain release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TAG: ${{ needs.plan.outputs.git_tag }} | |
| VERSION: ${{ needs.plan.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release upload "$TAG" dist/toolchain/* dist/vsix/* --clobber | |
| else | |
| gh release create "$TAG" dist/toolchain/* dist/vsix/* \ | |
| --title "BAML Language $VERSION" \ | |
| --notes "BAML language toolchain $VERSION" | |
| fi | |
| publish-wrapper-release: | |
| name: Publish wrapper release assets | |
| needs: [plan, all-builds] | |
| if: ${{ needs.plan.outputs.wrapper_changed == 'true' && needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_REPO: ${{ github.repository }} | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wrapper-* | |
| path: dist/wrapper | |
| merge-multiple: true | |
| - name: Generate checksum sidecars | |
| run: | | |
| set -euo pipefail | |
| find dist/wrapper -type f \( -name '*.tar.gz' -o -name '*.zip' \) -print0 | | |
| while IFS= read -r -d '' file; do | |
| (cd "$(dirname "$file")" && sha256sum "$(basename "$file")" > "$(basename "$file").sha256") | |
| done | |
| - name: Publish wrapper release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| WRAPPER_VERSION: ${{ needs.plan.outputs.wrapper_version }} | |
| run: | | |
| set -euo pipefail | |
| tag="baml-wrapper-$WRAPPER_VERSION" | |
| if gh release view "$tag" >/dev/null 2>&1; then | |
| gh release upload "$tag" dist/wrapper/* --clobber | |
| else | |
| gh release create "$tag" dist/wrapper/* \ | |
| --title "BAML Wrapper $WRAPPER_VERSION" \ | |
| --notes "BAML wrapper $WRAPPER_VERSION" | |
| fi | |
| publish-bridge-cffi-release: | |
| name: Publish bridge_cffi release assets | |
| # After the toolchain publisher so the shared `baml-language-<version>` | |
| # release already exists (both add assets to the same tag). | |
| needs: [plan, all-builds, publish-toolchain-release] | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_REPO: ${{ github.repository }} | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: bridge-cffi-* | |
| path: dist/cffi | |
| merge-multiple: true | |
| - name: Publish bridge_cffi release assets | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TAG: ${{ needs.plan.outputs.git_tag }} | |
| VERSION: ${{ needs.plan.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| shopt -s nullglob | |
| assets=(dist/cffi/*) | |
| if [[ ${#assets[@]} -eq 0 ]]; then | |
| echo "::error::no bridge_cffi assets downloaded" | |
| exit 1 | |
| fi | |
| # The toolchain publisher created the shared release; create it only if | |
| # a cffi-only rerun somehow got here first. | |
| if ! gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release create "$TAG" --title "BAML Language $VERSION" \ | |
| --notes "BAML language $VERSION" | |
| fi | |
| # Immutable upload: add missing assets, verify identical bytes for any | |
| # that already exist, and fail on mismatch. Never overwrite (unlike the | |
| # toolchain job's --clobber): a published release asset is immutable, and | |
| # the loader downloads these by URL. | |
| existing="$(gh release view "$TAG" --json assets --jq '.assets[].name')" | |
| for f in "${assets[@]}"; do | |
| name="$(basename "$f")" | |
| if grep -qxF "$name" <<<"$existing"; then | |
| tmp="$(mktemp)" | |
| gh release download "$TAG" --pattern "$name" --output "$tmp" --clobber | |
| if cmp -s "$f" "$tmp"; then | |
| echo "asset already present and identical: $name" | |
| else | |
| echo "::error::release asset $name exists with different content" | |
| exit 1 | |
| fi | |
| rm -f "$tmp" | |
| else | |
| gh release upload "$TAG" "$f" | |
| echo "uploaded: $name" | |
| fi | |
| done | |
| publish-go-sdk: | |
| name: Publish Go SDK mirror | |
| needs: [plan, build-go-sdk, all-builds] | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: go-sdk-mirror | |
| path: /tmp/go-sdk-package | |
| - name: Clone BoundaryML/baml-go | |
| env: | |
| DEPLOY_KEY: ${{ secrets.BAML_GO_DEPLOY_KEY }} | |
| run: | | |
| set -euo pipefail | |
| test -n "$DEPLOY_KEY" | |
| mkdir -p ~/.ssh | |
| printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/baml_go_mirror_key | |
| chmod 600 ~/.ssh/baml_go_mirror_key | |
| ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null | |
| export GIT_SSH_COMMAND="ssh -i ~/.ssh/baml_go_mirror_key -o IdentitiesOnly=yes" | |
| git clone git@github.com:BoundaryML/baml-go.git /tmp/baml-go | |
| if ! git -C /tmp/baml-go rev-parse --verify HEAD >/dev/null 2>&1; then | |
| git -C /tmp/baml-go switch --orphan main | |
| fi | |
| - name: Publish immutable module tag | |
| env: | |
| SOURCE_SHA: ${{ needs.plan.outputs.source_sha }} | |
| VERSION: ${{ needs.plan.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| export GIT_SSH_COMMAND="ssh -i ~/.ssh/baml_go_mirror_key -o IdentitiesOnly=yes" | |
| tag="v$VERSION" | |
| rsync -a --delete --exclude .git /tmp/go-sdk-package/ /tmp/baml-go/ | |
| cd /tmp/baml-go | |
| git add -A | |
| if git rev-parse --verify "refs/tags/$tag" >/dev/null 2>&1; then | |
| if git diff --cached --quiet "$tag" --; then | |
| echo "$tag already exists with identical module content" | |
| exit 0 | |
| fi | |
| echo "::error::$tag already exists with different module content" | |
| exit 1 | |
| fi | |
| if ! git diff --cached --quiet; then | |
| git -c user.name="github-actions[bot]" \ | |
| -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \ | |
| commit -m "Sync BoundaryML/baml@$SOURCE_SHA for v$VERSION" | |
| fi | |
| git -c user.name="github-actions[bot]" \ | |
| -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \ | |
| tag -a "$tag" -m "$tag" | |
| git push --atomic origin main "$tag" | |
| publish-crates-io: | |
| name: Publish Rust crates.io | |
| # After the cffi asset publisher: the loader downloads the engine cdylib | |
| # from the `baml-language-<version>` GitHub release on first call, so those | |
| # assets must be live before the crate that fetches them lands on crates.io. | |
| needs: [plan, all-builds, build-rust-sdk, verify-rust-sdk, publish-bridge-cffi-release] | |
| # Publishes on every real canary release, like PyPI/npm above. | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| # crates.io trusted publishing is bound to this top-level workflow identity, | |
| # like PyPI/npm above. Match the builder's runner + toolchain so the | |
| # repackaged `.crate` is byte-reproducible. | |
| runs-on: ubuntu-22.04 | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Stamp release plan | |
| env: | |
| RELEASE_PLAN_JSON: ${{ needs.plan.outputs.release_plan_json }} | |
| run: | | |
| set -euo pipefail | |
| printf '%s\n' "$RELEASE_PLAN_JSON" > release-plan.json | |
| scripts/baml-language-version stamp --plan release-plan.json | |
| - name: Setup Rust | |
| uses: ./.github/actions/setup-rust | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: rust-sdk-crate | |
| path: dist/rust | |
| - name: Repackage and prove bytes match the verified crate | |
| id: crate | |
| working-directory: baml_language | |
| run: | | |
| set -euo pipefail | |
| verified="$(find "$GITHUB_WORKSPACE/dist/rust" -name 'baml_bridge-*.crate' | head -n1)" | |
| if [[ -z "$verified" ]]; then | |
| echo "::error::verified crate not found under dist/rust" | |
| find "$GITHUB_WORKSPACE/dist/rust" -type f || true | |
| exit 1 | |
| fi | |
| # Repackage from the stamped source (no rebuild: --no-verify) and prove | |
| # the bytes are identical to the crate the verifier already smoked. | |
| # crates.io forces repackaging, so this is the contract's "prove the | |
| # package inputs match the verified artifact" step; if cargo ever stops | |
| # producing a reproducible `.crate` here it fails closed rather than | |
| # publishing unverified bytes. | |
| rm -f target/package/baml_bridge-*.crate | |
| cargo package -p baml_bridge --no-verify --allow-dirty | |
| repacked="$(find target/package -name 'baml_bridge-*.crate' | head -n1)" | |
| have="$(sha256sum "$repacked" | cut -d' ' -f1)" | |
| want="$(sha256sum "$verified" | cut -d' ' -f1)" | |
| if [[ "$have" != "$want" ]]; then | |
| echo "::error::repackaged crate ($have) != verified crate ($want)" | |
| exit 1 | |
| fi | |
| echo "sha256=$have" >> "$GITHUB_OUTPUT" | |
| echo "crate matches verified artifact ($have)" | |
| - name: Skip if this exact version is already on crates.io | |
| id: exists | |
| env: | |
| VERSION: ${{ needs.plan.outputs.crates_io_version }} | |
| WANT_CKSUM: ${{ steps.crate.outputs.sha256 }} | |
| run: | | |
| set -euo pipefail | |
| # Sparse-index path for a 4+ char name is <first2>/<next2>/<name>. | |
| index="$(curl -fsSL "https://index.crates.io/ba/ml/baml_bridge" || true)" | |
| entry="$(printf '%s\n' "$index" | jq -c "select(.vers == \"$VERSION\")" 2>/dev/null || true)" | |
| if [[ -z "$entry" ]]; then | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| echo "baml_bridge $VERSION not yet on crates.io; will publish" | |
| else | |
| have="$(printf '%s' "$entry" | jq -r .cksum)" | |
| if [[ "$have" == "$WANT_CKSUM" ]]; then | |
| echo "publish=false" >> "$GITHUB_OUTPUT" | |
| echo "baml_bridge $VERSION already published with identical bytes; skipping" | |
| else | |
| echo "::error::baml_bridge $VERSION exists on crates.io with different content" | |
| exit 1 | |
| fi | |
| fi | |
| - name: Mint crates.io trusted-publishing token | |
| if: ${{ steps.exists.outputs.publish == 'true' }} | |
| id: auth | |
| uses: rust-lang/crates-io-auth-action@v1 | |
| - name: Publish baml_bridge to crates.io | |
| if: ${{ steps.exists.outputs.publish == 'true' }} | |
| working-directory: baml_language | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} | |
| run: | | |
| set -euo pipefail | |
| # Publish the already-verified crate; --no-verify skips a rebuild (the | |
| # verifier already smoked it). No cargo-release, no git tags. | |
| cargo publish -p baml_bridge --no-verify --allow-dirty | |
| publish-pkg-boundaryml-com: | |
| name: Publish pkg.boundaryml.com manifests | |
| needs: [plan, all-builds, publish-pypi, publish-nodejs-sdk, publish-web-sdk, publish-toolchain-release, publish-bridge-cffi-release, publish-go-sdk, publish-crates-io, publish-wrapper-release, publish-homebrew, publish-aur] | |
| if: ${{ always() && needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' && needs.publish-pypi.result == 'success' && needs.publish-nodejs-sdk.result == 'success' && needs.publish-web-sdk.result == 'success' && needs.publish-toolchain-release.result == 'success' && needs.publish-bridge-cffi-release.result == 'success' && needs.publish-go-sdk.result == 'success' && (needs.publish-crates-io.result == 'success' || needs.publish-crates-io.result == 'skipped') && (needs.plan.outputs.wrapper_changed != 'true' || (needs.publish-wrapper-release.result == 'success' && needs.publish-homebrew.result == 'success' && needs.publish-aur.result == 'success')) }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: release-plan | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: toolchain-* | |
| path: dist/toolchain | |
| merge-multiple: true | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: bridge-cffi-* | |
| path: dist/cffi | |
| merge-multiple: true | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wrapper-* | |
| path: dist/wrapper | |
| merge-multiple: true | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: baml-vscode-vsix | |
| path: dist/vsix | |
| - uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ env.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| role-session-name: baml-language-release-${{ github.run_id }} | |
| - name: Generate release manifests | |
| run: | | |
| set -euo pipefail | |
| args=() | |
| if [[ "${{ needs.plan.outputs.wrapper_changed }}" == "true" ]]; then | |
| args+=(--wrapper-changed) | |
| fi | |
| # Record sdks.rust only when the crate actually published this run, so | |
| # the manifest never promises a baml_bridge version absent from crates.io. | |
| if [[ "${{ needs.publish-crates-io.result }}" == "success" ]]; then | |
| args+=(--crates-io-version "${{ needs.plan.outputs.crates_io_version }}") | |
| fi | |
| scripts/baml-release-manifests \ | |
| --toolchain-dir dist/toolchain \ | |
| --cffi-dir dist/cffi \ | |
| --wrapper-dir dist/wrapper \ | |
| --vsix-dir dist/vsix \ | |
| --out manifests \ | |
| --channel "${{ needs.plan.outputs.channel }}" \ | |
| --version "${{ needs.plan.outputs.version }}" \ | |
| --pypi-version "${{ needs.plan.outputs.pypi_version }}" \ | |
| --wrapper-version "${{ needs.plan.outputs.wrapper_version }}" \ | |
| "${args[@]}" | |
| - name: Upload install scripts, index, and manifests | |
| run: | | |
| set -euo pipefail | |
| upload_immutable() { | |
| local source="$1" | |
| local dest="$2" | |
| local tmp | |
| local err | |
| tmp="$(mktemp)" | |
| err="$(mktemp)" | |
| if aws s3api head-object --bucket "$PKG_BUCKET" --key "$dest" >/dev/null 2>"$err"; then | |
| if ! aws s3 cp "s3://$PKG_BUCKET/$dest" "$tmp" >/dev/null 2>>"$err"; then | |
| echo "::error::immutable object exists but could not be read: $dest" | |
| cat "$err" | |
| rm -f "$tmp" "$err" | |
| exit 1 | |
| fi | |
| if cmp -s "$source" "$tmp"; then | |
| echo "immutable object already exists and matches: $dest" | |
| else | |
| echo "::error::immutable object exists with different content: $dest" | |
| rm -f "$tmp" "$err" | |
| exit 1 | |
| fi | |
| elif grep -Eq '(\(404\)|Not Found|NoSuchKey)' "$err"; then | |
| aws s3 cp "$source" "s3://$PKG_BUCKET/$dest" \ | |
| --content-type application/json \ | |
| --cache-control "public, max-age=86400, immutable" | |
| else | |
| echo "::error::failed to inspect immutable object before upload: $dest" | |
| cat "$err" | |
| rm -f "$tmp" "$err" | |
| exit 1 | |
| fi | |
| rm -f "$tmp" "$err" | |
| } | |
| version="${{ needs.plan.outputs.version }}" | |
| channel="${{ needs.plan.outputs.channel }}" | |
| upload_immutable "manifests/version/$version.json" "manifest/v1/version/$version.json" | |
| aws s3 cp "manifests/$channel.json" "s3://$PKG_BUCKET/manifest/v1/$channel.json" \ | |
| --content-type application/json \ | |
| --cache-control "public, max-age=60, must-revalidate" | |
| if [[ -f manifests/wrapper.json ]]; then | |
| aws s3 cp manifests/wrapper.json "s3://$PKG_BUCKET/manifest/v1/wrapper.json" \ | |
| --content-type application/json \ | |
| --cache-control "public, max-age=60, must-revalidate" | |
| fi | |
| aws s3 cp scripts/install.sh "s3://$PKG_BUCKET/install.sh" --content-type text/x-shellscript --cache-control "public, max-age=60, must-revalidate" | |
| aws s3 cp scripts/install.ps1 "s3://$PKG_BUCKET/install.ps1" --content-type text/plain --cache-control "public, max-age=60, must-revalidate" | |
| aws s3 cp tools/pkg_boundaryml_com/data/index.html "s3://$PKG_BUCKET/index.html" --content-type text/html --cache-control "public, max-age=300" | |
| smoke-go-release: | |
| name: Smoke published Go SDK (${{ matrix.os }}) | |
| needs: [plan, publish-go-sdk, publish-pkg-boundaryml-com] | |
| if: ${{ needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-14, windows-latest] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: baml_language/sdks/go/baml_go/go.mod | |
| cache: false | |
| - name: Prove installation and runtime call outside the repository | |
| run: | | |
| python scripts/smoke-go-release.py \ | |
| --version "${{ needs.plan.outputs.version }}" \ | |
| --work-dir "$RUNNER_TEMP/baml-go-release-smoke" | |
| dispatch-nightly-after-canary: | |
| name: Queue nightly after canary publish | |
| needs: [plan, publish-pkg-boundaryml-com, smoke-go-release] | |
| if: ${{ needs.plan.outputs.dispatch_nightly_after_canary == 'true' && needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: write | |
| contents: read | |
| steps: | |
| - name: Dispatch nightly release run | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| gh workflow run release-baml-language.yml \ | |
| --ref canary \ | |
| -f channel=nightly \ | |
| -f dry_run=false | |
| publish-homebrew: | |
| name: Publish Homebrew wrapper formula | |
| needs: [plan, all-builds, publish-wrapper-release] | |
| if: ${{ needs.plan.outputs.wrapper_changed == 'true' && needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wrapper-* | |
| path: dist/wrapper | |
| merge-multiple: true | |
| - name: Generate Homebrew formula | |
| run: | | |
| set -euo pipefail | |
| scripts/baml-package-manager-artifacts \ | |
| --wrapper-dir dist/wrapper \ | |
| --version "${{ needs.plan.outputs.wrapper_version }}" \ | |
| --out package-manager | |
| - name: Push formula to BoundaryML/homebrew-tap | |
| env: | |
| TOKEN: ${{ secrets.HOMEBREW_BAML_DISPATCH_TOKEN }} | |
| WRAPPER_VERSION: ${{ needs.plan.outputs.wrapper_version }} | |
| run: | | |
| set -euo pipefail | |
| test -n "$TOKEN" | |
| git clone "https://x-access-token:${TOKEN}@github.com/BoundaryML/homebrew-tap.git" tap | |
| cp package-manager/homebrew/Formula/baml.rb tap/Formula/baml.rb | |
| cd tap | |
| git config user.name "boundaryml-release-bot" | |
| git config user.email "boundaryml-release-bot@users.noreply.github.com" | |
| git add Formula/baml.rb | |
| git commit -m "Update baml wrapper to ${WRAPPER_VERSION}" || exit 0 | |
| git push origin HEAD | |
| publish-aur: | |
| name: Publish AUR wrapper packages | |
| needs: [plan, all-builds, publish-wrapper-release] | |
| if: ${{ needs.plan.outputs.wrapper_changed == 'true' && needs.plan.outputs.dry_run == 'false' && needs.plan.outputs.source_branch == 'canary' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wrapper-* | |
| path: dist/wrapper | |
| merge-multiple: true | |
| - name: Configure AUR SSH | |
| env: | |
| AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} | |
| run: | | |
| set -euo pipefail | |
| test -n "$AUR_SSH_PRIVATE_KEY" | |
| mkdir -p ~/.ssh | |
| printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur | |
| chmod 600 ~/.ssh/aur | |
| ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts | |
| printf 'Host aur.archlinux.org\n IdentityFile ~/.ssh/aur\n User aur\n' >> ~/.ssh/config | |
| - name: Generate AUR package files | |
| run: | | |
| set -euo pipefail | |
| source_sha="$(curl -fsSL "https://github.com/BoundaryML/baml/archive/refs/tags/baml-wrapper-${{ needs.plan.outputs.wrapper_version }}.tar.gz" | sha256sum | awk '{print $1}')" | |
| scripts/baml-package-manager-artifacts \ | |
| --wrapper-dir dist/wrapper \ | |
| --version "${{ needs.plan.outputs.wrapper_version }}" \ | |
| --source-sha256 "$source_sha" \ | |
| --out package-manager | |
| - name: Push AUR repos | |
| env: | |
| WRAPPER_VERSION: ${{ needs.plan.outputs.wrapper_version }} | |
| run: | | |
| set -euo pipefail | |
| for pkg in baml baml-bin; do | |
| git clone "ssh://aur@aur.archlinux.org/${pkg}.git" "aur-${pkg}" | |
| cp "package-manager/aur/${pkg}/PKGBUILD" "aur-${pkg}/PKGBUILD" | |
| cp "package-manager/aur/${pkg}/.SRCINFO" "aur-${pkg}/.SRCINFO" | |
| cd "aur-${pkg}" | |
| git config user.name "boundaryml-release-bot" | |
| git config user.email "boundaryml-release-bot@users.noreply.github.com" | |
| git add PKGBUILD .SRCINFO | |
| git commit -m "Update baml wrapper to ${WRAPPER_VERSION}" || { cd ..; continue; } | |
| git push origin HEAD | |
| cd .. | |
| done | |
| dry-run-artifacts: | |
| name: Generate dry-run manifests and package files | |
| needs: [plan, all-builds] | |
| if: ${{ needs.plan.outputs.dry_run == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: toolchain-* | |
| path: dist/toolchain | |
| merge-multiple: true | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: bridge-cffi-* | |
| path: dist/cffi | |
| merge-multiple: true | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wrapper-* | |
| path: dist/wrapper | |
| merge-multiple: true | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: baml-vscode-vsix | |
| path: dist/vsix | |
| - name: Generate dry-run files | |
| run: | | |
| set -euo pipefail | |
| args=() | |
| if [[ "${{ needs.plan.outputs.wrapper_changed }}" == "true" ]]; then | |
| args+=(--wrapper-changed) | |
| fi | |
| # Dry-run rehearses the real manifest shape: a real run publishes the | |
| # Rust SDK and records sdks.rust, so include it here too. | |
| args+=(--crates-io-version "${{ needs.plan.outputs.crates_io_version }}") | |
| scripts/baml-release-manifests \ | |
| --toolchain-dir dist/toolchain \ | |
| --cffi-dir dist/cffi \ | |
| --wrapper-dir dist/wrapper \ | |
| --vsix-dir dist/vsix \ | |
| --out dry-run/manifest/v1 \ | |
| --channel "${{ needs.plan.outputs.channel }}" \ | |
| --version "${{ needs.plan.outputs.version }}" \ | |
| --pypi-version "${{ needs.plan.outputs.pypi_version }}" \ | |
| --wrapper-version "${{ needs.plan.outputs.wrapper_version }}" \ | |
| "${args[@]}" | |
| scripts/baml-package-manager-artifacts \ | |
| --wrapper-dir dist/wrapper \ | |
| --version "${{ needs.plan.outputs.wrapper_version }}" \ | |
| --out dry-run/package-manager | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: dry-run-release-files | |
| path: dry-run | |
| - uses: aws-actions/configure-aws-credentials@v4 | |
| if: ${{ needs.plan.outputs.source_branch == 'canary' }} | |
| with: | |
| role-to-assume: ${{ env.AWS_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| role-session-name: baml-language-dry-run-${{ github.run_id }} | |
| - name: Upload public dry-run manifests | |
| if: ${{ needs.plan.outputs.source_branch == 'canary' }} | |
| run: | | |
| set -euo pipefail | |
| aws s3 sync dry-run/manifest/v1 "s3://$PKG_BUCKET/dryrun/${{ github.run_id }}/manifest/v1" \ | |
| --content-type application/json \ | |
| --cache-control "public, max-age=300" | |
| dry-run-summary: | |
| name: Dry-run summary | |
| needs: [plan, all-builds, dry-run-artifacts] | |
| if: ${{ needs.plan.outputs.dry_run == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: | | |
| base="https://pkg.boundaryml.com/dryrun/${{ github.run_id }}/manifest/v1" | |
| { | |
| if [[ "${{ github.ref }}" == "refs/heads/canary" ]]; then | |
| echo "Dry-run manifest base: $base" | |
| else | |
| echo "Dry-run files were uploaded as the dry-run-release-files workflow artifact." | |
| fi | |
| echo | |
| echo 'Validation example:' | |
| echo '```bash' | |
| echo "BAML_HOME=\"\$(mktemp -d)\" BAML_MANIFEST_BASE_URL=\"$base\" baml toolchain use ${{ needs.plan.outputs.channel }}" | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" |