diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 505591c981..89011bf0f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,17 +14,26 @@ on: - 'stl-preview-head/**' - 'stl-preview-base/**' workflow_dispatch: + inputs: + release_pr: + description: Run required CI for a Release Please branch + required: false + default: false + type: boolean # Exercise the complete supported matrix and the next CPython prerelease # even when the repository has not changed. schedule: - cron: '47 9 * * *' +permissions: + contents: read + jobs: lint: timeout-minutes: 10 name: lint runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') + 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') steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -41,15 +50,16 @@ jobs: run: ./scripts/lint build: - if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') + 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') timeout-minutes: 10 name: build permissions: contents: read - id-token: write runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Rye uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 @@ -88,19 +98,44 @@ jobs: - name: Validate HTTPX2 wheel on Python 3.14 run: python scripts/utils/validate-httpx2-wheel.py - - name: Get GitHub OIDC Token + - name: Stage tarball for Stainless upload if: |- github.repository == 'stainless-sdks/openai-python' && !startsWith(github.ref, 'refs/heads/stl/') + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: stainless-package-tarball + path: dist/ + if-no-files-found: error + retention-days: 1 + + stainless-upload: + name: upload tarball to Stainless + needs: build + if: github.repository == 'stainless-sdks/openai-python' && !startsWith(github.ref, 'refs/heads/stl/') + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Download tarball + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: stainless-package-tarball + path: dist/ + + - name: Get GitHub OIDC Token id: github-oidc uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball - if: |- - github.repository == 'stainless-sdks/openai-python' && - !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} @@ -111,7 +146,7 @@ jobs: timeout-minutes: 15 name: test (Python ${{ matrix.python-version }}) runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + 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 strategy: fail-fast: false matrix: @@ -147,7 +182,7 @@ jobs: timeout-minutes: 20 name: test (HTTPX2) runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + 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 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -224,7 +259,7 @@ jobs: timeout-minutes: 20 name: compatibility (Python ${{ matrix.python-version }}) runs-on: ubuntu-latest - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.release_pr) continue-on-error: ${{ matrix.experimental }} strategy: fail-fast: false diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index fc38031b96..51264e6dfa 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -9,20 +9,98 @@ jobs: name: release if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' runs-on: ubuntu-latest - environment: publish outputs: releases_created: ${{ steps.release.outputs.releases_created }} + release_pr_branch: ${{ steps.release_pr.outputs.branch }} + release_pr_number: ${{ steps.release_pr.outputs.number }} + # Release Please writes its PR branch, tags/releases, PR, and PR labels. permissions: contents: write + issues: write + pull-requests: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - uses: stainless-api/trigger-release-please@bb6677c5a04578eec1ccfd9e1913b5b78ed64c61 # v1.4.0 + - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 id: release with: - repo: ${{ github.event.repository.full_name }} - stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} + token: ${{ secrets.GITHUB_TOKEN }} + target-branch: main + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + + - name: Capture release PR + if: steps.release.outputs.prs_created == 'true' + id: release_pr + env: + RELEASE_PR: ${{ steps.release.outputs.pr }} + run: | + branch="$(jq -er '.headBranchName | select(. == "release-please--branches--main")' <<<"$RELEASE_PR")" + number="$(jq -er '.number | select(type == "number")' <<<"$RELEASE_PR")" + echo "branch=$branch" >> "$GITHUB_OUTPUT" + echo "number=$number" >> "$GITHUB_OUTPUT" + + release-pr-ci: + name: release PR CI + needs: release + if: ${{ needs.release.outputs.release_pr_branch != '' }} + runs-on: ubuntu-latest + # Read the release PR's current base SHA, then dispatch its required + # workflows. Keeping this separate prevents Release Please from receiving + # Actions write access. + permissions: + actions: write + pull-requests: read + + steps: + - name: Run required checks for release PR + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + RELEASE_PR_BRANCH: ${{ needs.release.outputs.release_pr_branch }} + RELEASE_PR_NUMBER: ${{ needs.release.outputs.release_pr_number }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const expectedRef = process.env.RELEASE_PR_BRANCH; + const pullNumber = Number(process.env.RELEASE_PR_NUMBER); + if (!Number.isSafeInteger(pullNumber) || pullNumber <= 0) { + throw new Error('Release Please returned an invalid PR number'); + } + + const { data: pull } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pullNumber, + }); + if (pull.head.ref !== expectedRef) { + throw new Error(`Release PR branch changed: expected ${expectedRef}, got ${pull.head.ref}`); + } + + const ref = pull.head.ref; + await Promise.all([ + github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'ci.yml', + ref, + inputs: { release_pr: 'true' }, + }), + github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'detect-breaking-changes.yml', + ref, + inputs: { + release_pr: 'true', + base_sha: pull.base.sha, + }, + }), + github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'codeql.yml', + ref, + }), + ]); build: name: build @@ -36,6 +114,8 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Rye uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 @@ -64,7 +144,6 @@ jobs: # PyPI Trusted Publishing requires id-token: write. Keep it scoped to this # minimal upload-only job rather than the build job. permissions: - contents: read id-token: write steps: diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index 5f4a5e1271..db660c03cf 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -4,16 +4,36 @@ on: branches: - main - next + workflow_dispatch: + inputs: + release_pr: + description: Run checks for a Release Please branch + required: false + default: false + type: boolean + base_sha: + description: Base commit for breaking-change comparison + required: false + type: string + +permissions: + contents: read jobs: detect_breaking_changes: runs-on: 'ubuntu-latest' name: detect-breaking-changes - if: github.repository == 'openai/openai-python' + if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main')) + env: + BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || inputs.base_sha }} + FETCH_DEPTH: 0 steps: - name: Calculate fetch-depth + if: github.event_name == 'pull_request' + env: + PR_COMMITS: ${{ github.event.pull_request.commits }} run: | - echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV + echo "FETCH_DEPTH=$((PR_COMMITS + 1))" >> "$GITHUB_ENV" - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: @@ -30,17 +50,17 @@ jobs: rye sync --all-features - name: Detect removed symbols run: | - rye run python scripts/detect-breaking-changes.py "${{ github.event.pull_request.base.sha }}" + rye run python scripts/detect-breaking-changes.py "$BASE_SHA" - name: Detect breaking changes run: | test -f ./scripts/detect-breaking-changes || { echo "Missing scripts/detect-breaking-changes"; exit 1; } - ./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }} + ./scripts/detect-breaking-changes "$BASE_SHA" agents_sdk: runs-on: 'ubuntu-latest' name: Detect Agents SDK regressions - if: github.repository == 'openai/openai-python' + if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main')) steps: # Setup this sdk - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 diff --git a/release-please-config.json b/release-please-config.json index 745ef5fd54..2e4b90d047 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -1,12 +1,10 @@ { + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", "packages": { ".": {} }, - "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", "include-v-in-tag": true, "include-component-in-tag": false, - "versioning": "prerelease", - "prerelease": true, "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": false, "pull-request-header": "Automated Release PR", @@ -63,4 +61,4 @@ "extra-files": [ "src/openai/_version.py" ] -} \ No newline at end of file +}