Skip to content

Commit 24b7cd1

Browse files
ci: migrate release-please to upstream action (#3568)
## Summary - replace `stainless-api/trigger-release-please` with the upstream `googleapis/release-please-action`, pinned to verified v5.0.0 commit `45996ed` - run release-please explicitly against `main` with the repository's manifest configuration - use the built-in `GITHUB_TOKEN` explicitly and remove the `STAINLESS_API_KEY` action input - switch the config schema to upstream release-please and remove Stainless's prerelease/beta settings - explicitly dispatch required checks for the generated `release-please--branches--main` PR - keep the `publish` environment scoped to the PyPI publish job ## Impact Release PRs and GitHub releases are now managed entirely by the open-source release-please action. Stable release versions continue to update `CHANGELOG.md`, `.release-please-manifest.json`, `pyproject.toml`, and `src/openai/_version.py`; successful GitHub release creation still gates the existing build and trusted PyPI publish jobs. Release Please deliberately uses `GITHUB_TOKEN`. Because GitHub suppresses ordinary push and pull-request events created by that token, a separate least-privilege job explicitly dispatches CI, breaking-change detection, and CodeQL against the generated release PR. It fetches the PR's live base SHA immediately before dispatch and restricts this path to the exact `release-please--branches--main` ref. ## Permission audit - release: `contents: write` for the release branch, tags, and GitHub releases; `pull-requests: write` for release PRs; `issues: write` for PR labels - release PR checks: `actions: write` only to dispatch workflows and `pull-requests: read` only to obtain the current base SHA and verify the generated head branch - CI and breaking-change workflows: default `contents: read`; the secret-bearing examples job is not dispatched for release PRs - CI build: `contents: read` only, with checkout credential persistence disabled; the Stainless mirror's OIDC upload is isolated in a separate Stainless-only job - CodeQL: `contents: read` and `security-events: write` in its existing dedicated workflow - package build: `contents: read` only, with checkout credential persistence disabled - publish: `id-token: write` only for PyPI Trusted Publishing - no App private key, Stainless credential, or PyPI credential is exposed to the release workflow ## Validation - release-please v17.6.0 `debug-config --dry-run`: loaded the Python strategy with stable/default versioning and target branch `main` - release-please v17.6.0 `release-pr --dry-run`: generated the expected stable branch and version-file changes - release-please config validated against the exact v17.6.0 upstream JSON schema; current manifest baseline is `2.52.1` - pinned action interface, outputs, branch naming, and documented permissions verified against upstream source - actionlint and YAML/JSON parsing - `git diff --check` - thermo-nuclear code-quality review: no findings ## Cutover prerequisites completed - existing Stainless release PR #3562 was reviewed, merged, and released successfully as [v2.52.1](https://github.com/openai/openai-python/releases/tag/v2.52.1); the current release workflow completed GitHub release creation, build, and PyPI publishing in [run 30835897995](https://github.com/openai/openai-python/actions/runs/30835897995) - GitHub rejected its built-in Actions integration as a ruleset bypass actor, so active legacy ruleset `15951502` now excludes only `refs/heads/release-please--branches--main`; all other `release-please--*` branches and the two original bypass actors remain unchanged - the migration branch includes the published `2.52.1` manifest/version baseline ## After merge - remove the now-unused `STAINLESS_API_KEY` secret from the repository/environment - any future migration to the `openai-sdks` App should be handled separately; this PR intentionally uses only `GITHUB_TOKEN`
1 parent f83b1f4 commit 24b7cd1

4 files changed

Lines changed: 158 additions & 26 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,26 @@ on:
1414
- 'stl-preview-head/**'
1515
- 'stl-preview-base/**'
1616
workflow_dispatch:
17+
inputs:
18+
release_pr:
19+
description: Run required CI for a Release Please branch
20+
required: false
21+
default: false
22+
type: boolean
1723
# Exercise the complete supported matrix and the next CPython prerelease
1824
# even when the repository has not changed.
1925
schedule:
2026
- cron: '47 9 * * *'
2127

28+
permissions:
29+
contents: read
30+
2231
jobs:
2332
lint:
2433
timeout-minutes: 10
2534
name: lint
2635
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
27-
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
36+
if: (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
2837
steps:
2938
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
3039

@@ -41,15 +50,16 @@ jobs:
4150
run: ./scripts/lint
4251

4352
build:
44-
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
53+
if: (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
4554
timeout-minutes: 10
4655
name: build
4756
permissions:
4857
contents: read
49-
id-token: write
5058
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
5159
steps:
5260
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
61+
with:
62+
persist-credentials: false
5363

5464
- name: Set up Rye
5565
uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
@@ -88,19 +98,44 @@ jobs:
8898
- name: Validate HTTPX2 wheel on Python 3.14
8999
run: python scripts/utils/validate-httpx2-wheel.py
90100

91-
- name: Get GitHub OIDC Token
101+
- name: Stage tarball for Stainless upload
92102
if: |-
93103
github.repository == 'stainless-sdks/openai-python' &&
94104
!startsWith(github.ref, 'refs/heads/stl/')
105+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
106+
with:
107+
name: stainless-package-tarball
108+
path: dist/
109+
if-no-files-found: error
110+
retention-days: 1
111+
112+
stainless-upload:
113+
name: upload tarball to Stainless
114+
needs: build
115+
if: github.repository == 'stainless-sdks/openai-python' && !startsWith(github.ref, 'refs/heads/stl/')
116+
runs-on: ubuntu-latest
117+
permissions:
118+
contents: read
119+
id-token: write
120+
121+
steps:
122+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
123+
with:
124+
persist-credentials: false
125+
126+
- name: Download tarball
127+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
128+
with:
129+
name: stainless-package-tarball
130+
path: dist/
131+
132+
- name: Get GitHub OIDC Token
95133
id: github-oidc
96134
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
97135
with:
98136
script: core.setOutput('github_token', await core.getIDToken());
99137

100138
- name: Upload tarball
101-
if: |-
102-
github.repository == 'stainless-sdks/openai-python' &&
103-
!startsWith(github.ref, 'refs/heads/stl/')
104139
env:
105140
URL: https://pkg.stainless.com/s
106141
AUTH: ${{ steps.github-oidc.outputs.github_token }}
@@ -111,7 +146,7 @@ jobs:
111146
timeout-minutes: 15
112147
name: test (Python ${{ matrix.python-version }})
113148
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
114-
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
149+
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
115150
strategy:
116151
fail-fast: false
117152
matrix:
@@ -147,7 +182,7 @@ jobs:
147182
timeout-minutes: 20
148183
name: test (HTTPX2)
149184
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
150-
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
185+
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
151186
steps:
152187
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
153188

@@ -224,7 +259,7 @@ jobs:
224259
timeout-minutes: 20
225260
name: compatibility (Python ${{ matrix.python-version }})
226261
runs-on: ubuntu-latest
227-
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
262+
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.release_pr)
228263
continue-on-error: ${{ matrix.experimental }}
229264
strategy:
230265
fail-fast: false

‎.github/workflows/create-releases.yml‎

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,98 @@ jobs:
99
name: release
1010
if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python'
1111
runs-on: ubuntu-latest
12-
environment: publish
1312
outputs:
1413
releases_created: ${{ steps.release.outputs.releases_created }}
14+
release_pr_branch: ${{ steps.release_pr.outputs.branch }}
15+
release_pr_number: ${{ steps.release_pr.outputs.number }}
16+
# Release Please writes its PR branch, tags/releases, PR, and PR labels.
1517
permissions:
1618
contents: write
19+
issues: write
20+
pull-requests: write
1721

1822
steps:
19-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
20-
21-
- uses: stainless-api/trigger-release-please@bb6677c5a04578eec1ccfd9e1913b5b78ed64c61 # v1.4.0
23+
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
2224
id: release
2325
with:
24-
repo: ${{ github.event.repository.full_name }}
25-
stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}
26+
token: ${{ secrets.GITHUB_TOKEN }}
27+
target-branch: main
28+
config-file: release-please-config.json
29+
manifest-file: .release-please-manifest.json
30+
31+
- name: Capture release PR
32+
if: steps.release.outputs.prs_created == 'true'
33+
id: release_pr
34+
env:
35+
RELEASE_PR: ${{ steps.release.outputs.pr }}
36+
run: |
37+
branch="$(jq -er '.headBranchName | select(. == "release-please--branches--main")' <<<"$RELEASE_PR")"
38+
number="$(jq -er '.number | select(type == "number")' <<<"$RELEASE_PR")"
39+
echo "branch=$branch" >> "$GITHUB_OUTPUT"
40+
echo "number=$number" >> "$GITHUB_OUTPUT"
41+
42+
release-pr-ci:
43+
name: release PR CI
44+
needs: release
45+
if: ${{ needs.release.outputs.release_pr_branch != '' }}
46+
runs-on: ubuntu-latest
47+
# Read the release PR's current base SHA, then dispatch its required
48+
# workflows. Keeping this separate prevents Release Please from receiving
49+
# Actions write access.
50+
permissions:
51+
actions: write
52+
pull-requests: read
53+
54+
steps:
55+
- name: Run required checks for release PR
56+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
57+
env:
58+
RELEASE_PR_BRANCH: ${{ needs.release.outputs.release_pr_branch }}
59+
RELEASE_PR_NUMBER: ${{ needs.release.outputs.release_pr_number }}
60+
with:
61+
github-token: ${{ secrets.GITHUB_TOKEN }}
62+
script: |
63+
const expectedRef = process.env.RELEASE_PR_BRANCH;
64+
const pullNumber = Number(process.env.RELEASE_PR_NUMBER);
65+
if (!Number.isSafeInteger(pullNumber) || pullNumber <= 0) {
66+
throw new Error('Release Please returned an invalid PR number');
67+
}
68+
69+
const { data: pull } = await github.rest.pulls.get({
70+
owner: context.repo.owner,
71+
repo: context.repo.repo,
72+
pull_number: pullNumber,
73+
});
74+
if (pull.head.ref !== expectedRef) {
75+
throw new Error(`Release PR branch changed: expected ${expectedRef}, got ${pull.head.ref}`);
76+
}
77+
78+
const ref = pull.head.ref;
79+
await Promise.all([
80+
github.rest.actions.createWorkflowDispatch({
81+
owner: context.repo.owner,
82+
repo: context.repo.repo,
83+
workflow_id: 'ci.yml',
84+
ref,
85+
inputs: { release_pr: 'true' },
86+
}),
87+
github.rest.actions.createWorkflowDispatch({
88+
owner: context.repo.owner,
89+
repo: context.repo.repo,
90+
workflow_id: 'detect-breaking-changes.yml',
91+
ref,
92+
inputs: {
93+
release_pr: 'true',
94+
base_sha: pull.base.sha,
95+
},
96+
}),
97+
github.rest.actions.createWorkflowDispatch({
98+
owner: context.repo.owner,
99+
repo: context.repo.repo,
100+
workflow_id: 'codeql.yml',
101+
ref,
102+
}),
103+
]);
26104
27105
build:
28106
name: build
@@ -36,6 +114,8 @@ jobs:
36114

37115
steps:
38116
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
117+
with:
118+
persist-credentials: false
39119

40120
- name: Set up Rye
41121
uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
@@ -64,7 +144,6 @@ jobs:
64144
# PyPI Trusted Publishing requires id-token: write. Keep it scoped to this
65145
# minimal upload-only job rather than the build job.
66146
permissions:
67-
contents: read
68147
id-token: write
69148

70149
steps:

‎.github/workflows/detect-breaking-changes.yml‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,36 @@ on:
44
branches:
55
- main
66
- next
7+
workflow_dispatch:
8+
inputs:
9+
release_pr:
10+
description: Run checks for a Release Please branch
11+
required: false
12+
default: false
13+
type: boolean
14+
base_sha:
15+
description: Base commit for breaking-change comparison
16+
required: false
17+
type: string
18+
19+
permissions:
20+
contents: read
721

822
jobs:
923
detect_breaking_changes:
1024
runs-on: 'ubuntu-latest'
1125
name: detect-breaking-changes
12-
if: github.repository == 'openai/openai-python'
26+
if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main'))
27+
env:
28+
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || inputs.base_sha }}
29+
FETCH_DEPTH: 0
1330
steps:
1431
- name: Calculate fetch-depth
32+
if: github.event_name == 'pull_request'
33+
env:
34+
PR_COMMITS: ${{ github.event.pull_request.commits }}
1535
run: |
16-
echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV
36+
echo "FETCH_DEPTH=$((PR_COMMITS + 1))" >> "$GITHUB_ENV"
1737
1838
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
1939
with:
@@ -30,17 +50,17 @@ jobs:
3050
rye sync --all-features
3151
- name: Detect removed symbols
3252
run: |
33-
rye run python scripts/detect-breaking-changes.py "${{ github.event.pull_request.base.sha }}"
53+
rye run python scripts/detect-breaking-changes.py "$BASE_SHA"
3454
3555
- name: Detect breaking changes
3656
run: |
3757
test -f ./scripts/detect-breaking-changes || { echo "Missing scripts/detect-breaking-changes"; exit 1; }
38-
./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }}
58+
./scripts/detect-breaking-changes "$BASE_SHA"
3959
4060
agents_sdk:
4161
runs-on: 'ubuntu-latest'
4262
name: Detect Agents SDK regressions
43-
if: github.repository == 'openai/openai-python'
63+
if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main'))
4464
steps:
4565
# Setup this sdk
4666
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

‎release-please-config.json‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
{
2+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
23
"packages": {
34
".": {}
45
},
5-
"$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json",
66
"include-v-in-tag": true,
77
"include-component-in-tag": false,
8-
"versioning": "prerelease",
9-
"prerelease": true,
108
"bump-minor-pre-major": true,
119
"bump-patch-for-minor-pre-major": false,
1210
"pull-request-header": "Automated Release PR",
@@ -63,4 +61,4 @@
6361
"extra-files": [
6462
"src/openai/_version.py"
6563
]
66-
}
64+
}

0 commit comments

Comments
 (0)