BAML Language Release #1326
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 | |
| BAML_AGENT_SKILLS_REPOSITORY: BoundaryML/baml-skill | |
| 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 }} | |
| 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 .pypi_version release-plan.json)" | |
| git_tag="$(jq -r .git_tag release-plan.json)" | |
| { | |
| echo "channel=$channel" | |
| echo "version=$version" | |
| echo "pypi_version=$pypi_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 | |
| build-toolchain: | |
| name: Build toolchain (${{ matrix.target }}) | |
| needs: [plan, build-vsix] | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: aarch64-apple-darwin | |
| os: macos-14 | |
| - target: x86_64-apple-darwin | |
| os: macos-15-intel | |
| - target: x86_64-unknown-linux-gnu | |
| os: ubuntu-latest | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-24.04-arm | |
| - target: x86_64-unknown-linux-musl | |
| os: ubuntu-latest | |
| before: | | |
| sudo apt-get update | |
| sudo apt-get install -y musl-tools | |
| echo "CC_x86_64_unknown_linux_musl=musl-gcc" >> "$GITHUB_ENV" | |
| echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc" >> "$GITHUB_ENV" | |
| - target: aarch64-unknown-linux-musl | |
| os: ubuntu-24.04-arm | |
| before: | | |
| sudo apt-get update | |
| sudo apt-get install -y musl-tools | |
| echo "CC_aarch64_unknown_linux_musl=musl-gcc" >> "$GITHUB_ENV" | |
| echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc" >> "$GITHUB_ENV" | |
| - target: x86_64-pc-windows-msvc | |
| os: windows-latest | |
| - target: aarch64-pc-windows-msvc | |
| os: windows-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: | |
| 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: Build tools setup | |
| if: matrix.before | |
| run: ${{ matrix.before }} | |
| - 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-agent-skills: | |
| name: Build agent skills | |
| needs: [plan] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.plan.outputs.source_sha }} | |
| persist-credentials: false | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ env.BAML_AGENT_SKILLS_REPOSITORY }} | |
| path: agent-skills-source | |
| persist-credentials: false | |
| - name: Package agent skills | |
| env: | |
| VERSION: ${{ needs.plan.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| scripts/package-agent-skills \ | |
| --source agent-skills-source \ | |
| --version "$VERSION" \ | |
| --out-dir dist/agent-skills | |
| archive="dist/agent-skills/baml-agent-skills-$VERSION.tar.gz" | |
| tar -tzf "$archive" | tee agent-skills-list.txt | |
| grep -E '/SKILL\.md$' agent-skills-list.txt | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: agent-skills | |
| path: dist/agent-skills/* | |
| build-wrapper: | |
| name: Build wrapper (${{ matrix.target }}) | |
| needs: [plan] | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: aarch64-apple-darwin | |
| os: macos-14 | |
| - target: x86_64-apple-darwin | |
| os: macos-15-intel | |
| - target: x86_64-unknown-linux-gnu | |
| os: ubuntu-latest | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-24.04-arm | |
| - target: x86_64-unknown-linux-musl | |
| os: ubuntu-latest | |
| before: | | |
| sudo apt-get update | |
| sudo apt-get install -y musl-tools | |
| echo "CC_x86_64_unknown_linux_musl=musl-gcc" >> "$GITHUB_ENV" | |
| echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc" >> "$GITHUB_ENV" | |
| - target: aarch64-unknown-linux-musl | |
| os: ubuntu-24.04-arm | |
| before: | | |
| sudo apt-get update | |
| sudo apt-get install -y musl-tools | |
| echo "CC_aarch64_unknown_linux_musl=musl-gcc" >> "$GITHUB_ENV" | |
| echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc" >> "$GITHUB_ENV" | |
| - target: x86_64-pc-windows-msvc | |
| os: windows-latest | |
| - target: aarch64-pc-windows-msvc | |
| os: windows-latest | |
| 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: Build tools setup | |
| if: matrix.before | |
| run: ${{ matrix.before }} | |
| - 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 }} | |
| 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-agent-skills | |
| 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-toolchain-release: | |
| name: Publish toolchain release assets | |
| needs: [plan, all-builds, build-agent-skills] | |
| 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 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: agent-skills | |
| path: dist/agent-skills | |
| - 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/* dist/agent-skills/* --clobber | |
| else | |
| gh release create "$TAG" dist/toolchain/* dist/vsix/* dist/agent-skills/* \ | |
| --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-pkg-boundaryml-com: | |
| name: Publish pkg.boundaryml.com manifests | |
| needs: [plan, all-builds, publish-pypi, publish-nodejs-sdk, publish-toolchain-release, 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-toolchain-release.result == 'success' && (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: 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 | |
| scripts/baml-release-manifests \ | |
| --toolchain-dir dist/toolchain \ | |
| --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" | |
| dispatch-nightly-after-canary: | |
| name: Queue nightly after canary publish | |
| needs: [plan, publish-pkg-boundaryml-com] | |
| 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: 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 | |
| scripts/baml-release-manifests \ | |
| --toolchain-dir dist/toolchain \ | |
| --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" |