|
1 | | -# Placeholder so the release-py.yml workflow_dispatch trigger is registered on the |
2 | | -# default branch. This lets the full workflow be dispatched from its feature branch |
3 | | -# ("Use workflow from: <branch>") before merge. The functional workflow — which |
4 | | -# publishes the bt-publishing-test dummy PyPI package end-to-end — replaces this |
5 | | -# stub when the Python release PR lands. |
| 1 | +# |
| 2 | +# Releases bt-publishing-test — a dummy PyPI package used to validate Braintrust |
| 3 | +# release workflow tooling end-to-end. This does NOT release any real Braintrust |
| 4 | +# Python SDK. |
| 5 | +# |
| 6 | +# This workflow serves two purposes: |
| 7 | +# 1. End-to-end testing of the composite actions in actions/release/ |
| 8 | +# 2. Reference implementation of the Python-specific release template |
| 9 | +# |
| 10 | +# Generic steps are provided by composite actions in actions/release/. |
| 11 | +# Only the sections marked with inline comments need to change for other packages. |
| 12 | +# |
| 13 | +# ───────────────────────────────────────────────────────────────────────────── |
| 14 | +# SETUP REQUIREMENTS |
| 15 | +# ───────────────────────────────────────────────────────────────────────────── |
| 16 | +# |
| 17 | +# GitHub Environments (repo Settings → Environments): |
| 18 | +# publish |
| 19 | +# - Required reviewers: sdk-eng team or named maintainers |
| 20 | +# - Prevent self-review: enabled |
| 21 | +# - Deployment branches: main only |
| 22 | +# publish-dry-run |
| 23 | +# - Required reviewers: at least yourself (to test the approval gate) |
| 24 | +# - Allow administrators to bypass: enabled (for testing flexibility) |
| 25 | +# - Deployment branches: no restriction |
| 26 | +# |
| 27 | +# Repository Variables (Settings → Secrets and variables → Variables): |
| 28 | +# SLACK_SDK_RELEASE_CHANNEL Slack channel ID for release notifications |
| 29 | +# |
| 30 | +# Repository Secrets (Settings → Secrets and variables → Secrets): |
| 31 | +# SLACK_BOT_TOKEN Org-level secret — Brainbot Slack app token |
| 32 | +# Must be invited to SLACK_SDK_RELEASE_CHANNEL |
| 33 | +# |
| 34 | +# PyPI Trusted Publishing (OIDC — no token needed): |
| 35 | +# Configure for your package at pypi.org → project → Settings → Publishing, |
| 36 | +# or pre-register a "pending publisher" before the first publish: |
| 37 | +# Owner: braintrustdata (or your org) |
| 38 | +# Repository: sdk-actions (or your SDK repo) |
| 39 | +# Workflow: release-py.yml (or your workflow filename) |
| 40 | +# Environment: publish |
| 41 | +# The publish job needs id-token: write for OIDC + PEP 740 attestations. |
| 42 | +# |
| 43 | +# ───────────────────────────────────────────────────────────────────────────── |
| 44 | + |
6 | 45 | name: Release Python (bt-publishing-test) |
7 | 46 |
|
8 | 47 | on: |
| 48 | + # Auto dry-run on PRs that touch the generated actions or the workflows, to |
| 49 | + # smoke-test the actions end-to-end. (A template edited without regenerating |
| 50 | + # is caught separately by the CI check-generated job, which runs on every PR.) |
| 51 | + pull_request: |
| 52 | + paths: |
| 53 | + - 'actions/**' |
| 54 | + - '.github/workflows/**' |
9 | 55 | workflow_dispatch: |
10 | 56 | inputs: |
11 | 57 | _instructions: |
|
21 | 67 | description: "Anchor for release notes: a tag name or commit SHA. If SHA, notes will be empty." |
22 | 68 | type: string |
23 | 69 | required: false |
| 70 | + # bt-publishing-test: fixed SHA anchor prevents indefinite changelog accumulation. |
| 71 | + # Update this when you want to reset the notes baseline. |
24 | 72 | default: '58a397193105516446c3042361913655bcece509' |
25 | 73 | dry_run: |
26 | 74 | description: "Dry run: Build without tagging or publishing" |
27 | 75 | type: boolean |
28 | 76 | default: false |
29 | 77 |
|
30 | 78 | jobs: |
31 | | - placeholder: |
| 79 | + bump: |
32 | 80 | runs-on: ubuntu-24.04 |
33 | 81 | timeout-minutes: 5 |
34 | 82 | permissions: {} |
| 83 | + outputs: |
| 84 | + version: ${{ steps.bump.outputs.version }} |
| 85 | + steps: |
| 86 | + # bt-publishing-test: derives next version by querying PyPI and incrementing the patch. |
| 87 | + # Falls back to 0.0.1 on first publish (project not found). Remove this job |
| 88 | + # in real SDK workflows; version comes from the version bump PR. |
| 89 | + - name: Bump version |
| 90 | + id: bump |
| 91 | + run: | |
| 92 | + # Project may not exist yet (first release / new name); default to 0.0.0 |
| 93 | + # so the first publish is 0.0.1. |
| 94 | + CURRENT=$(curl -sf https://pypi.org/pypi/bt-publishing-test/json 2>/dev/null | jq -r '.info.version // empty' || true) |
| 95 | + CURRENT=${CURRENT:-0.0.0} |
| 96 | + IFS='.' read -r major minor patch <<< "$CURRENT" |
| 97 | + echo "version=$major.$minor.$((patch + 1))" >> $GITHUB_OUTPUT |
| 98 | +
|
| 99 | + validate: |
| 100 | + needs: bump |
| 101 | + runs-on: ubuntu-24.04 |
| 102 | + timeout-minutes: 10 |
| 103 | + permissions: |
| 104 | + contents: read |
| 105 | + outputs: |
| 106 | + release_tag: ${{ steps.validate-release.outputs.release_tag }} |
| 107 | + prev_release: ${{ steps.validate-release.outputs.prev_release }} |
| 108 | + branch: ${{ steps.validate-release.outputs.branch }} |
| 109 | + on_release_branch: ${{ steps.validate-release.outputs.on_release_branch }} |
| 110 | + commit_message: ${{ steps.validate-release.outputs.commit_message }} |
35 | 111 | steps: |
36 | | - - name: Placeholder |
37 | | - run: echo "Placeholder — registers workflow_dispatch. The functional release-py.yml lands in the Python release PR." |
| 112 | + # bt-publishing-test: checkout required to resolve local ./actions/... references. |
| 113 | + # Real SDK workflows use external braintrustdata/sdk-actions/...@sha references |
| 114 | + # and do not need this step. |
| 115 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 116 | + |
| 117 | + - name: Validate release |
| 118 | + id: validate-release |
| 119 | + uses: ./actions/release/lang/py/validate |
| 120 | + with: |
| 121 | + # bt-publishing-test: version passed directly from bump job — no version_file read needed. |
| 122 | + # In a real Python project, drop `version`; it is read from version_file. |
| 123 | + version: ${{ needs.bump.outputs.version }} |
| 124 | + sha: ${{ inputs.sha || github.event.pull_request.head.sha }} |
| 125 | + dry_run: ${{ inputs.dry_run || github.event_name == 'pull_request' }} |
| 126 | + working_directory: test/release/py |
| 127 | + python_version: test/release/py/.python-version # exercises version-file sourcing (vs explicit version) |
| 128 | + package_name: bt-publishing-test # exercises the PyPI-availability fail-fast check |
| 129 | + |
| 130 | + prepare: |
| 131 | + needs: validate |
| 132 | + runs-on: ubuntu-24.04 |
| 133 | + timeout-minutes: 5 |
| 134 | + permissions: |
| 135 | + contents: write # required for releases/generate-notes API |
| 136 | + outputs: |
| 137 | + pr_list: ${{ steps.prepare.outputs.pr_list }} |
| 138 | + notes: ${{ steps.prepare.outputs.notes }} |
| 139 | + steps: |
| 140 | + # bt-publishing-test: checkout required because actions are referenced locally (./actions/...). |
| 141 | + # Real SDK workflows reference actions externally (braintrustdata/sdk-actions/...@sha) |
| 142 | + # and do not need this step. |
| 143 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 144 | + |
| 145 | + - name: Prepare release |
| 146 | + id: prepare |
| 147 | + uses: ./actions/release/prepare |
| 148 | + with: |
| 149 | + release_tag: ${{ needs.validate.outputs.release_tag }} |
| 150 | + sha: ${{ inputs.sha || github.event.pull_request.head.sha }} |
| 151 | + # On PR auto dry-runs, anchor notes to the head SHA: prepare treats a |
| 152 | + # full SHA as "notes unavailable" (no tag range), so the changelog |
| 153 | + # doesn't accumulate in the step summary on every PR. |
| 154 | + prev_release: ${{ inputs.prev_release || github.event.pull_request.head.sha || needs.validate.outputs.prev_release }} |
| 155 | + |
| 156 | + notify-pending: |
| 157 | + needs: [validate, prepare] |
| 158 | + runs-on: ubuntu-24.04 |
| 159 | + timeout-minutes: 5 |
| 160 | + permissions: {} |
| 161 | + steps: |
| 162 | + # bt-publishing-test: checkout required for local action resolution (see prepare job comment) |
| 163 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 164 | + |
| 165 | + - name: Notify release pending |
| 166 | + uses: ./actions/release/notify-pending |
| 167 | + with: |
| 168 | + sha: ${{ inputs.sha || github.event.pull_request.head.sha }} |
| 169 | + release_tag: ${{ needs.validate.outputs.release_tag }} |
| 170 | + prev_release: ${{ needs.validate.outputs.prev_release }} |
| 171 | + branch: ${{ needs.validate.outputs.branch }} |
| 172 | + on_release_branch: ${{ needs.validate.outputs.on_release_branch }} |
| 173 | + commit_message: ${{ needs.validate.outputs.commit_message }} |
| 174 | + pr_list: ${{ needs.prepare.outputs.pr_list }} |
| 175 | + notes: ${{ needs.prepare.outputs.notes }} |
| 176 | + dry_run: ${{ inputs.dry_run || github.event_name == 'pull_request' }} |
| 177 | + # Suppress Slack on PR auto dry-runs (still exercises message-building; just no real post). |
| 178 | + # Gate on != pull_request: GHA's `A && '' || B` returns B (the '' is falsy), so suppression |
| 179 | + # must be the trailing '' branch, not the "true" value. |
| 180 | + slack_token: ${{ github.event_name != 'pull_request' && secrets.SLACK_BOT_TOKEN || '' }} |
| 181 | + slack_channel: ${{ github.event_name != 'pull_request' && vars.SLACK_SDK_RELEASE_CHANNEL || '' }} |
| 182 | + # slack_mention: '@sdk-eng' |
| 183 | + label: 'bt-publishing-test' # distinguishes this from other packages in this repo |
| 184 | + emoji: ':python:' |
| 185 | + |
| 186 | + publish: |
| 187 | + needs: [bump, validate, prepare, notify-pending] |
| 188 | + runs-on: ubuntu-24.04 |
| 189 | + timeout-minutes: 15 |
| 190 | + # No environment on PR dry-runs (avoids the approval gate AND the accumulating |
| 191 | + # deployment records); the gated environments apply only to manual dispatch. |
| 192 | + # Note the structure: empty must be the trailing `|| ''` branch, since GHA's |
| 193 | + # `cond && '' || X` would fall through to X (the '' is falsy). |
| 194 | + environment: >- |
| 195 | + ${{ github.event_name != 'pull_request' |
| 196 | + && (inputs.dry_run && 'publish-dry-run' || 'publish') |
| 197 | + || '' }} |
| 198 | + permissions: |
| 199 | + contents: write |
| 200 | + id-token: write # OIDC trusted publishing + PEP 740 attestations |
| 201 | + steps: |
| 202 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 203 | + with: |
| 204 | + ref: ${{ inputs.sha || github.event.pull_request.head.sha }} |
| 205 | + fetch-depth: 0 |
| 206 | + |
| 207 | + # bt-publishing-test: write the run-number version into version.py. The publish job |
| 208 | + # checks out fresh, so the file still reflects the placeholder version. |
| 209 | + # Real SDK workflows omit this — the version is committed in the bump PR. |
| 210 | + - name: Apply version to version.py |
| 211 | + env: |
| 212 | + VERSION: ${{ needs.bump.outputs.version }} |
| 213 | + run: | |
| 214 | + printf 'VERSION = "%s"\n' "$VERSION" > test/release/py/src/bt_publishing_test/version.py |
| 215 | +
|
| 216 | + - name: Publish |
| 217 | + uses: ./actions/release/lang/py/publish |
| 218 | + with: |
| 219 | + sha: ${{ inputs.sha || github.event.pull_request.head.sha }} |
| 220 | + checkout: 'false' # bt-publishing-test: skip internal checkout — version patching above must survive into the build |
| 221 | + working_directory: test/release/py |
| 222 | + python_version: test/release/py/.python-version |
| 223 | + dry_run: ${{ inputs.dry_run || github.event_name == 'pull_request' }} |
| 224 | + release_tag: ${{ needs.validate.outputs.release_tag }} |
| 225 | + github_release: 'false' # bt-publishing-test: omit GitHub release to avoid tag/release pollution from repeated test runs |
| 226 | + version: ${{ needs.bump.outputs.version }} |
| 227 | + package_name: bt-publishing-test |
| 228 | + label: 'bt-publishing-test' # distinguishes this from other packages in this repo |
| 229 | + notes: ${{ needs.prepare.outputs.notes }} |
| 230 | + prev_release: ${{ needs.validate.outputs.prev_release }} |
| 231 | + branch: ${{ needs.validate.outputs.branch }} |
| 232 | + on_release_branch: ${{ needs.validate.outputs.on_release_branch }} |
| 233 | + pr_list: ${{ needs.prepare.outputs.pr_list }} |
| 234 | + # Suppress Slack on PR auto dry-runs (still exercises message-building; just no real post). |
| 235 | + # Gate on != pull_request: GHA's `A && '' || B` returns B (the '' is falsy), so suppression |
| 236 | + # must be the trailing '' branch, not the "true" value. |
| 237 | + slack_token: ${{ github.event_name != 'pull_request' && secrets.SLACK_BOT_TOKEN || '' }} |
| 238 | + slack_channel: ${{ github.event_name != 'pull_request' && vars.SLACK_SDK_RELEASE_CHANNEL || '' }} |
| 239 | + # slack_mention: '@sdk-eng' |
| 240 | + # failure_slack_mention: '@sdk-eng' |
| 241 | + emoji: ':python:' |
0 commit comments