openai/openai-agents-python

9 workflows · maturity 50% · 7 patterns · GitHub ↗

Security 22.22/100

Practices

✓ Matrix✓ Permissions○ Security scan○ AI review○ Cache✓ Concurrency○ Reusable workflows

Detected patterns

Security dimensions

permissions
22.2
security scan
0
supply chain
0
secret handling
0
harden runner
0

Workflows (9)

docs perms .github/workflows/docs.yml
Triggers
push
Runs on
ubuntu-latest
Jobs
deploy_docs
Actions
astral-sh/setup-uv
Commands
  • if [ "${{ github.event_name }}" != "push" ]; then echo "skip=false" >> "$GITHUB_OUTPUT" exit 0 fi set -euo pipefail before="${{ github.event.before }}" sha="${{ github.sha }}" changed_files=$(git diff --name-only "$before" "$sha" || true) non_docs=$(echo "$changed_files" | grep -vE '^(docs/|mkdocs.yml$)' || true) if [ -n "$non_docs" ]; then echo "skip=true" >> "$GITHUB_OUTPUT" else echo "skip=false" >> "$GITHUB_OUTPUT" fi
  • make sync
  • make deploy-docs
View raw YAML
name: Deploy docs

on:
  push:
    branches:
      - main
    paths:
      - "docs/**"
      - "mkdocs.yml"

permissions:
  contents: write # This allows pushing to gh-pages

jobs:
  deploy_docs:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - name: Determine docs-only push
        id: docs-only
        run: |
          if [ "${{ github.event_name }}" != "push" ]; then
            echo "skip=false" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          set -euo pipefail
          before="${{ github.event.before }}"
          sha="${{ github.sha }}"
          changed_files=$(git diff --name-only "$before" "$sha" || true)
          non_docs=$(echo "$changed_files" | grep -vE '^(docs/|mkdocs.yml$)' || true)
          if [ -n "$non_docs" ]; then
            echo "skip=true" >> "$GITHUB_OUTPUT"
          else
            echo "skip=false" >> "$GITHUB_OUTPUT"
          fi
      - name: Setup uv
        if: steps.docs-only.outputs.skip != 'true'
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
      - name: Install dependencies
        if: steps.docs-only.outputs.skip != 'true'
        run: make sync
      - name: Deploy docs
        if: steps.docs-only.outputs.skip != 'true'
        run: make deploy-docs
issues .github/workflows/issues.yml
Triggers
schedule
Runs on
ubuntu-latest
Jobs
close-issues
Actions
actions/stale
View raw YAML
name: Close inactive issues
on:
  schedule:
    - cron: "30 1 * * *"

jobs:
  close-issues:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f
        with:
          days-before-issue-stale: 7
          days-before-issue-close: 3
          stale-issue-label: "stale"
          exempt-issue-labels: "skip-stale"
          stale-issue-message: "This issue is stale because it has been open for 7 days with no activity."
          close-issue-message: "This issue was closed because it has been inactive for 3 days since being marked as stale."
          any-of-issue-labels: 'question,needs-more-info'
          days-before-pr-stale: 10
          days-before-pr-close: 7
          stale-pr-label: "stale"
          exempt-pr-labels: "skip-stale"
          stale-pr-message: "This PR is stale because it has been open for 10 days with no activity."
          close-pr-message: "This PR was closed because it has been inactive for 7 days since being marked as stale."
          repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-labels perms .github/workflows/pr-labels.yml
Triggers
pull_request_target, workflow_dispatch
Runs on
ubuntu-latest
Jobs
label
Actions
openai/codex-action
Commands
  • echo "This workflow must be dispatched from main." exit 1
  • set -euo pipefail git fetch --no-tags --prune --recurse-submodules=no \ "https://github.com/${PR_HEAD_REPO}.git" \ "${PR_HEAD_SHA}"
  • set -euo pipefail mkdir -p .tmp/pr-labels diff_base_sha="$(git merge-base "$PR_BASE_SHA" "$PR_HEAD_SHA")" echo "diff_base_sha=${diff_base_sha}" >> "$GITHUB_OUTPUT" git diff --name-only "$diff_base_sha" "$PR_HEAD_SHA" > .tmp/pr-labels/changed-files.txt git diff "$diff_base_sha" "$PR_HEAD_SHA" > .tmp/pr-labels/changes.diff python - <<'PY' import json import os import pathlib pathlib.Path(".tmp/pr-labels/pr-context.json").write_text( json.dumps( { "title": os.environ.get("PR_TITLE", ""), "body": os.environ.get("PR_BODY", ""), }, ensure_ascii=False, indent=2, ) + "\n" ) PY
  • set -euo pipefail output_dir=".tmp/codex/outputs" output_file="${output_dir}/pr-labels.json" mkdir -p "$output_dir" echo "output_file=${output_file}" >> "$GITHUB_OUTPUT"
  • python .github/scripts/pr_labels.py
View raw YAML
name: Auto label PRs

on:
  pull_request_target:
    types:
      - opened
      - reopened
      - synchronize
      - ready_for_review
  workflow_dispatch:
    inputs:
      pr_number:
        description: "PR number to label."
        required: true
        type: number

permissions:
  contents: read
  issues: write
  pull-requests: write

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - name: Ensure main workflow
        if: ${{ github.event_name == 'workflow_dispatch' && github.ref != 'refs/heads/main' }}
        run: |
          echo "This workflow must be dispatched from main."
          exit 1

      - name: Resolve PR context
        id: pr
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
        env:
          MANUAL_PR_NUMBER: ${{ inputs.pr_number || '' }}
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const isManual = context.eventName === 'workflow_dispatch';
            let pr;
            if (isManual) {
              const prNumber = Number(process.env.MANUAL_PR_NUMBER);
              if (!prNumber) {
                core.setFailed('workflow_dispatch requires pr_number input.');
                return;
              }
              const { data } = await github.rest.pulls.get({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: prNumber,
              });
              pr = data;
            } else {
              pr = context.payload.pull_request;
            }
            if (!pr) {
              core.setFailed('Missing pull request context.');
              return;
            }
            const headRepo = pr.head.repo.full_name;
            const repoFullName = `${context.repo.owner}/${context.repo.repo}`;
            core.setOutput('pr_number', pr.number);
            core.setOutput('base_sha', pr.base.sha);
            core.setOutput('head_sha', pr.head.sha);
            core.setOutput('head_repo', headRepo);
            core.setOutput('is_fork', headRepo !== repoFullName);
            core.setOutput('title', pr.title || '');
            core.setOutput('body', pr.body || '');

      - name: Checkout base
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          fetch-depth: 0
          ref: ${{ steps.pr.outputs.base_sha }}
      - name: Fetch PR head
        env:
          PR_HEAD_REPO: ${{ steps.pr.outputs.head_repo }}
          PR_HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
        run: |
          set -euo pipefail
          git fetch --no-tags --prune --recurse-submodules=no \
            "https://github.com/${PR_HEAD_REPO}.git" \
            "${PR_HEAD_SHA}"
      - name: Collect PR diff
        id: diff
        env:
          PR_BASE_SHA: ${{ steps.pr.outputs.base_sha }}
          PR_HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
          PR_TITLE: ${{ steps.pr.outputs.title }}
          PR_BODY: ${{ steps.pr.outputs.body }}
        run: |
          set -euo pipefail
          mkdir -p .tmp/pr-labels
          diff_base_sha="$(git merge-base "$PR_BASE_SHA" "$PR_HEAD_SHA")"
          echo "diff_base_sha=${diff_base_sha}" >> "$GITHUB_OUTPUT"
          git diff --name-only "$diff_base_sha" "$PR_HEAD_SHA" > .tmp/pr-labels/changed-files.txt
          git diff "$diff_base_sha" "$PR_HEAD_SHA" > .tmp/pr-labels/changes.diff
          python - <<'PY'
          import json
          import os
          import pathlib

          pathlib.Path(".tmp/pr-labels/pr-context.json").write_text(
              json.dumps(
                  {
                      "title": os.environ.get("PR_TITLE", ""),
                      "body": os.environ.get("PR_BODY", ""),
                  },
                  ensure_ascii=False,
                  indent=2,
              )
              + "\n"
          )
          PY
      - name: Prepare Codex output
        id: codex-output
        run: |
          set -euo pipefail
          output_dir=".tmp/codex/outputs"
          output_file="${output_dir}/pr-labels.json"
          mkdir -p "$output_dir"
          echo "output_file=${output_file}" >> "$GITHUB_OUTPUT"
      - name: Run Codex labeling
        id: run_codex
        if: ${{ (github.event_name == 'workflow_dispatch' || steps.pr.outputs.is_fork != 'true') && github.actor != 'dependabot[bot]' }}
        uses: openai/codex-action@086169432f1d2ab2f4057540b1754d550f6a1189
        with:
          openai-api-key: ${{ secrets.PROD_OPENAI_API_KEY }}
          prompt-file: .github/codex/prompts/pr-labels.md
          output-file: ${{ steps.codex-output.outputs.output_file }}
          output-schema-file: .github/codex/schemas/pr-labels.json
          # Keep the legacy Linux sandbox path until the default bubblewrap path
          # works reliably on GitHub-hosted Ubuntu runners.
          codex-args: '["--enable","use_legacy_landlock"]'
          safety-strategy: drop-sudo
          sandbox: read-only
      - name: Apply labels
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
          PR_BASE_SHA: ${{ steps.diff.outputs.diff_base_sha }}
          PR_HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
          CODEX_OUTPUT_PATH: ${{ steps.codex-output.outputs.output_file }}
          CODEX_CONCLUSION: ${{ steps.run_codex.conclusion }}
        run: |
          python .github/scripts/pr_labels.py

      - name: Comment on manual run failure
        if: ${{ github.event_name == 'workflow_dispatch' && always() }}
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
        env:
          PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
          JOB_STATUS: ${{ job.status }}
          RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          CODEX_CONCLUSION: ${{ steps.run_codex.conclusion }}
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const marker = '<!-- pr-labels-manual-run -->';
            const jobStatus = process.env.JOB_STATUS;
            if (jobStatus === 'success') {
              return;
            }
            const prNumber = Number(process.env.PR_NUMBER);
            if (!prNumber) {
              core.setFailed('Missing PR number for manual run comment.');
              return;
            }
            const body = [
              marker,
              'Manual PR labeling failed.',
              `Job status: ${jobStatus}.`,
              `Run: ${process.env.RUN_URL}.`,
              `Codex labeling: ${process.env.CODEX_CONCLUSION}.`,
            ].join('\n');
            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: prNumber,
              per_page: 100,
            });
            const existing = comments.find(
              (comment) =>
                comment.user?.login === 'github-actions[bot]' &&
                comment.body?.includes(marker),
            );
            if (existing) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: existing.id,
                body,
              });
              core.info(`Updated existing comment ${existing.id}`);
              return;
            }
            const { data: created } = await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: prNumber,
              body,
            });
            core.info(`Created comment ${created.id}`);
publish perms .github/workflows/publish.yml
Triggers
release
Runs on
ubuntu-latest
Jobs
publish
Actions
astral-sh/setup-uv, pypa/gh-action-pypi-publish
Commands
  • make sync
  • uv build
View raw YAML
name: Publish to PyPI

on:
  release:
    types:
      - published

permissions:
  contents: read

jobs:
  publish:
    environment:
      name: pypi
      url: https://pypi.org/p/openai-agents
    permissions:
      id-token: write # Important for trusted publishing to PyPI
    runs-on: ubuntu-latest
    env:
      OPENAI_API_KEY: fake-for-tests

    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - name: Setup uv
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
      - name: Install dependencies
        run: make sync
      - name: Build package
        run: uv build
      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
release-pr perms .github/workflows/release-pr.yml
Triggers
workflow_dispatch
Runs on
ubuntu-latest
Jobs
release-pr
Actions
astral-sh/setup-uv, openai/codex-action
Commands
  • git fetch origin --tags --prune
  • branch="release/v${RELEASE_VERSION}" if git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then echo "Branch $branch already exists on origin." >&2 exit 1 fi
  • python - <<'PY' import os import pathlib import re import sys version = os.environ["RELEASE_VERSION"] if version.startswith("v"): print("Version must not start with 'v' (use x.y.z...).", file=sys.stderr) sys.exit(1) if ".." in version: print("Version contains consecutive dots (use x.y.z...).", file=sys.stderr) sys.exit(1) if not re.match(r"^\d+\.\d+(\.\d+)*([a-zA-Z0-9\.-]+)?$", version): print( "Version must be semver-like (e.g., 0.6.6, 0.6.6-rc1, 0.6.6.dev1).", file=sys.stderr, ) sys.exit(1) path = pathlib.Path("pyproject.toml") text = path.read_text() updated, count = re.subn( r'(?m)^version\s*=\s*"[^\"]+"', f'version = "{version}"', text, ) if count != 1: print("Expected to update exactly one version line.", file=sys.stderr) sys.exit(1) if updated == text: print("Version already set; no changes made.", file=sys.stderr) sys.exit(1) path.write_text(updated) PY
  • make sync
  • git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com"
  • branch="release/v${RELEASE_VERSION}" git checkout -b "$branch" git add pyproject.toml uv.lock if git diff --cached --quiet; then echo "No changes to commit." >&2 exit 1 fi git commit -m "Bump version to ${RELEASE_VERSION}" git push --set-upstream origin "$branch"
  • set -euo pipefail output_dir=".tmp/codex/outputs" output_file="${output_dir}/release-review.md" mkdir -p "$output_dir" echo "output_file=${output_file}" >> "$GITHUB_OUTPUT"
  • python - <<'PY' import os import pathlib report = pathlib.Path(os.environ["RELEASE_REVIEW_PATH"]).read_text() pathlib.Path("pr-body.md").write_text(report) PY
View raw YAML
name: Create release PR

on:
  workflow_dispatch:
    inputs:
      version:
        description: "Version to release (e.g., 0.6.6)"
        required: true

permissions:
  contents: write
  pull-requests: write

jobs:
  release-pr:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          fetch-depth: 0
          ref: main
      - name: Setup uv
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
      - name: Fetch tags
        run: git fetch origin --tags --prune
      - name: Ensure release branch does not exist
        env:
          RELEASE_VERSION: ${{ inputs.version }}
        run: |
          branch="release/v${RELEASE_VERSION}"
          if git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then
            echo "Branch $branch already exists on origin." >&2
            exit 1
          fi
      - name: Update version
        env:
          RELEASE_VERSION: ${{ inputs.version }}
        run: |
          python - <<'PY'
          import os
          import pathlib
          import re
          import sys

          version = os.environ["RELEASE_VERSION"]
          if version.startswith("v"):
              print("Version must not start with 'v' (use x.y.z...).", file=sys.stderr)
              sys.exit(1)
          if ".." in version:
              print("Version contains consecutive dots (use x.y.z...).", file=sys.stderr)
              sys.exit(1)
          if not re.match(r"^\d+\.\d+(\.\d+)*([a-zA-Z0-9\.-]+)?$", version):
              print(
                  "Version must be semver-like (e.g., 0.6.6, 0.6.6-rc1, 0.6.6.dev1).",
                  file=sys.stderr,
              )
              sys.exit(1)
          path = pathlib.Path("pyproject.toml")
          text = path.read_text()
          updated, count = re.subn(
              r'(?m)^version\s*=\s*"[^\"]+"',
              f'version = "{version}"',
              text,
          )
          if count != 1:
              print("Expected to update exactly one version line.", file=sys.stderr)
              sys.exit(1)
          if updated == text:
              print("Version already set; no changes made.", file=sys.stderr)
              sys.exit(1)
          path.write_text(updated)
          PY
      - name: Sync dependencies
        run: make sync
      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
      - name: Create release branch and commit
        env:
          RELEASE_VERSION: ${{ inputs.version }}
        run: |
          branch="release/v${RELEASE_VERSION}"
          git checkout -b "$branch"
          git add pyproject.toml uv.lock
          if git diff --cached --quiet; then
            echo "No changes to commit." >&2
            exit 1
          fi
          git commit -m "Bump version to ${RELEASE_VERSION}"
          git push --set-upstream origin "$branch"
      - name: Prepare Codex output
        id: codex-output
        run: |
          set -euo pipefail
          output_dir=".tmp/codex/outputs"
          output_file="${output_dir}/release-review.md"
          mkdir -p "$output_dir"
          echo "output_file=${output_file}" >> "$GITHUB_OUTPUT"
      - name: Run Codex release review
        uses: openai/codex-action@086169432f1d2ab2f4057540b1754d550f6a1189
        with:
          openai-api-key: ${{ secrets.PROD_OPENAI_API_KEY }}
          prompt-file: .github/codex/prompts/release-review.md
          output-file: ${{ steps.codex-output.outputs.output_file }}
          # Keep the legacy Linux sandbox path until the default bubblewrap path
          # works reliably on GitHub-hosted Ubuntu runners.
          codex-args: '["--enable","use_legacy_landlock"]'
          safety-strategy: drop-sudo
          sandbox: read-only
      - name: Build PR body
        env:
          RELEASE_REVIEW_PATH: ${{ steps.codex-output.outputs.output_file }}
        run: |
          python - <<'PY'
          import os
          import pathlib

          report = pathlib.Path(os.environ["RELEASE_REVIEW_PATH"]).read_text()
          pathlib.Path("pr-body.md").write_text(report)
          PY
      - name: Create or update PR
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          RELEASE_VERSION: ${{ inputs.version }}
        run: |
          set -euo pipefail
          head_branch="release/v${RELEASE_VERSION}"
          milestone_name="$(python .github/scripts/select-release-milestone.py --version "$RELEASE_VERSION")"
          pr_number="$(gh pr list --head "$head_branch" --base "main" --json number --jq '.[0].number // empty')"
          if [ -z "$pr_number" ]; then
            create_args=(
              --title "Release ${RELEASE_VERSION}"
              --body-file pr-body.md
              --base "main"
              --head "$head_branch"
              --label "project"
            )
            if [ -n "$milestone_name" ]; then
              create_args+=(--milestone "$milestone_name")
            fi
            if ! gh pr create "${create_args[@]}"; then
              echo "PR create with label/milestone failed; retrying without them." >&2
              gh pr create \
                --title "Release ${RELEASE_VERSION}" \
                --body-file pr-body.md \
                --base "main" \
                --head "$head_branch"
            fi
          else
            edit_args=(
              --title "Release ${RELEASE_VERSION}"
              --body-file pr-body.md
              --add-label "project"
            )
            if [ -n "$milestone_name" ]; then
              edit_args+=(--milestone "$milestone_name")
            fi
            if ! gh pr edit "$pr_number" "${edit_args[@]}"; then
              echo "PR edit with label/milestone failed; retrying without them." >&2
              gh pr edit "$pr_number" --title "Release ${RELEASE_VERSION}" --body-file pr-body.md
            fi
          fi
release-pr-update perms .github/workflows/release-pr-update.yml
Triggers
push
Runs on
ubuntu-latest
Jobs
update-release-pr
Actions
openai/codex-action
Commands
  • git fetch origin --tags --prune
  • git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com"
  • set -euo pipefail base_branch="main" prs_json="$(gh pr list \ --base "$base_branch" \ --state open \ --search "head:release/v" \ --limit 200 \ --json number,headRefName,isCrossRepository,headRepositoryOwner)" count="$(echo "$prs_json" | jq '[.[] | select(.isCrossRepository == false) | select(.headRefName|startswith("release/v"))] | length')" if [ "$count" -eq 0 ]; then echo "found=false" >> "$GITHUB_OUTPUT" exit 0 fi if [ "$count" -gt 1 ]; then echo "Multiple release PRs found; expected a single release PR." >&2 exit 1 fi number="$(echo "$prs_json" | jq -r '.[] | select(.isCrossRepository == false) | select(.headRefName|startswith("release/v")) | .number')" branch="$(echo "$prs_json" | jq -r '.[] | select(.isCrossRepository == false) | select(.headRefName|startswith("release/v")) | .headRefName')" echo "found=true" >> "$GITHUB_OUTPUT" echo "number=$number" >> "$GITHUB_OUTPUT" echo "branch=$branch" >> "$GITHUB_OUTPUT"
  • set -euo pipefail git fetch origin main "$RELEASE_BRANCH" git checkout -B "$RELEASE_BRANCH" "origin/$RELEASE_BRANCH" git rebase origin/main
  • set -euo pipefail output_dir=".tmp/codex/outputs" output_file="${output_dir}/release-review.md" mkdir -p "$output_dir" echo "output_file=${output_file}" >> "$GITHUB_OUTPUT"
  • set -euo pipefail git push --force-with-lease origin "$RELEASE_BRANCH" gh pr edit "$PR_NUMBER" --body-file "$RELEASE_REVIEW_PATH" version="${RELEASE_BRANCH#release/v}" milestone_name="$(python .github/scripts/select-release-milestone.py --version "$version")" if [ -n "$milestone_name" ]; then if ! gh pr edit "$PR_NUMBER" --add-label "project" --milestone "$milestone_name"; then echo "PR label/milestone update failed; continuing without changes." >&2 fi else if ! gh pr edit "$PR_NUMBER" --add-label "project"; then echo "PR label update failed; continuing without changes." >&2 fi fi
View raw YAML
name: Update release PR on main updates

on:
  push:
    branches:
      - main

concurrency:
  group: release-pr-update
  cancel-in-progress: true

permissions:
  contents: write
  pull-requests: write

jobs:
  update-release-pr:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          fetch-depth: 0
      - name: Fetch tags
        run: git fetch origin --tags --prune
      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
      - name: Find release PR
        id: find
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail
          base_branch="main"
          prs_json="$(gh pr list \
            --base "$base_branch" \
            --state open \
            --search "head:release/v" \
            --limit 200 \
            --json number,headRefName,isCrossRepository,headRepositoryOwner)"
          count="$(echo "$prs_json" | jq '[.[] | select(.isCrossRepository == false) | select(.headRefName|startswith("release/v"))] | length')"
          if [ "$count" -eq 0 ]; then
            echo "found=false" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          if [ "$count" -gt 1 ]; then
            echo "Multiple release PRs found; expected a single release PR." >&2
            exit 1
          fi
          number="$(echo "$prs_json" | jq -r '.[] | select(.isCrossRepository == false) | select(.headRefName|startswith("release/v")) | .number')"
          branch="$(echo "$prs_json" | jq -r '.[] | select(.isCrossRepository == false) | select(.headRefName|startswith("release/v")) | .headRefName')"
          echo "found=true" >> "$GITHUB_OUTPUT"
          echo "number=$number" >> "$GITHUB_OUTPUT"
          echo "branch=$branch" >> "$GITHUB_OUTPUT"
      - name: Rebase release branch
        if: steps.find.outputs.found == 'true'
        env:
          RELEASE_BRANCH: ${{ steps.find.outputs.branch }}
        run: |
          set -euo pipefail
          git fetch origin main "$RELEASE_BRANCH"
          git checkout -B "$RELEASE_BRANCH" "origin/$RELEASE_BRANCH"
          git rebase origin/main
      - name: Prepare Codex output
        if: steps.find.outputs.found == 'true'
        id: codex-output
        run: |
          set -euo pipefail
          output_dir=".tmp/codex/outputs"
          output_file="${output_dir}/release-review.md"
          mkdir -p "$output_dir"
          echo "output_file=${output_file}" >> "$GITHUB_OUTPUT"
      - name: Run Codex release review
        if: steps.find.outputs.found == 'true'
        uses: openai/codex-action@086169432f1d2ab2f4057540b1754d550f6a1189
        with:
          openai-api-key: ${{ secrets.PROD_OPENAI_API_KEY }}
          prompt-file: .github/codex/prompts/release-review.md
          output-file: ${{ steps.codex-output.outputs.output_file }}
          # Keep the legacy Linux sandbox path until the default bubblewrap path
          # works reliably on GitHub-hosted Ubuntu runners.
          codex-args: '["--enable","use_legacy_landlock"]'
          safety-strategy: drop-sudo
          sandbox: read-only
      - name: Update PR body and push
        if: steps.find.outputs.found == 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ steps.find.outputs.number }}
          RELEASE_BRANCH: ${{ steps.find.outputs.branch }}
          RELEASE_REVIEW_PATH: ${{ steps.codex-output.outputs.output_file }}
        run: |
          set -euo pipefail
          git push --force-with-lease origin "$RELEASE_BRANCH"
          gh pr edit "$PR_NUMBER" --body-file "$RELEASE_REVIEW_PATH"
          version="${RELEASE_BRANCH#release/v}"
          milestone_name="$(python .github/scripts/select-release-milestone.py --version "$version")"
          if [ -n "$milestone_name" ]; then
            if ! gh pr edit "$PR_NUMBER" --add-label "project" --milestone "$milestone_name"; then
              echo "PR label/milestone update failed; continuing without changes." >&2
            fi
          else
            if ! gh pr edit "$PR_NUMBER" --add-label "project"; then
              echo "PR label update failed; continuing without changes." >&2
            fi
          fi
release-tag perms .github/workflows/release-tag.yml
Triggers
pull_request
Runs on
ubuntu-latest
Jobs
tag-release
Commands
  • if [ -z "$MERGE_SHA" ]; then echo "merge_commit_sha is empty; refusing to tag to avoid tagging the wrong commit." >&2 exit 1 fi
  • git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com"
  • git fetch origin --tags --prune
  • python - <<'PY' import os import pathlib import sys import tomllib path = pathlib.Path("pyproject.toml") data = tomllib.loads(path.read_text()) version = data.get("project", {}).get("version") if not version: print("Missing project.version in pyproject.toml.", file=sys.stderr) sys.exit(1) head_ref = os.environ.get("HEAD_REF", "") if head_ref.startswith("release/v"): expected = head_ref[len("release/v") :] if expected != version: print( f"Version mismatch: branch {expected} vs pyproject {version}.", file=sys.stderr, ) sys.exit(1) output_path = pathlib.Path(os.environ["GITHUB_OUTPUT"]) output_path.write_text(f"version={version}\n") PY
  • if git tag -l "v${VERSION}" | grep -q "v${VERSION}"; then echo "Tag v${VERSION} already exists; skipping." exit 0 fi git tag -a "v${VERSION}" -m "Release v${VERSION}" git push origin "v${VERSION}"
View raw YAML
name: Tag release on merge

on:
  pull_request:
    types:
      - closed
    branches:
      - main

permissions:
  contents: write

jobs:
  tag-release:
    if: >-
      github.event.pull_request.merged == true &&
      startsWith(github.event.pull_request.head.ref, 'release/v')
    runs-on: ubuntu-latest
    steps:
      - name: Validate merge commit
        env:
          MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
        run: |
          if [ -z "$MERGE_SHA" ]; then
            echo "merge_commit_sha is empty; refusing to tag to avoid tagging the wrong commit." >&2
            exit 1
          fi
      - name: Checkout merge commit
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.merge_commit_sha }}
      - name: Setup Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
        with:
          python-version: "3.11"
      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
      - name: Fetch tags
        run: git fetch origin --tags --prune
      - name: Resolve version
        id: version
        env:
          HEAD_REF: ${{ github.event.pull_request.head.ref }}
        run: |
          python - <<'PY'
          import os
          import pathlib
          import sys
          import tomllib

          path = pathlib.Path("pyproject.toml")
          data = tomllib.loads(path.read_text())
          version = data.get("project", {}).get("version")
          if not version:
            print("Missing project.version in pyproject.toml.", file=sys.stderr)
            sys.exit(1)

          head_ref = os.environ.get("HEAD_REF", "")
          if head_ref.startswith("release/v"):
            expected = head_ref[len("release/v") :]
            if expected != version:
              print(
                  f"Version mismatch: branch {expected} vs pyproject {version}.",
                  file=sys.stderr,
              )
              sys.exit(1)

          output_path = pathlib.Path(os.environ["GITHUB_OUTPUT"])
          output_path.write_text(f"version={version}\n")
          PY
      - name: Create tag
        env:
          VERSION: ${{ steps.version.outputs.version }}
        run: |
          if git tag -l "v${VERSION}" | grep -q "v${VERSION}"; then
            echo "Tag v${VERSION} already exists; skipping."
            exit 0
          fi
          git tag -a "v${VERSION}" -m "Release v${VERSION}"
          git push origin "v${VERSION}"
tests matrix perms .github/workflows/tests.yml
Triggers
push, pull_request
Runs on
ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
lint, typecheck, tests, build-docs
Matrix
python-version→ 3.10, 3.11, 3.12, 3.13, 3.14
Actions
astral-sh/setup-uv, astral-sh/setup-uv, astral-sh/setup-uv, astral-sh/setup-uv
Commands
  • ./.github/scripts/detect-changes.sh code "${{ github.event.pull_request.base.sha || github.event.before }}" "${{ github.sha }}"
  • make sync
  • make format-check
  • make lint
  • echo "Skipping lint for non-code changes."
  • ./.github/scripts/detect-changes.sh code "${{ github.event.pull_request.base.sha || github.event.before }}" "${{ github.sha }}"
  • make sync
  • make typecheck
View raw YAML
name: Tests

on:
  push:
    branches:
      - main
  pull_request:
    # All PRs, including stacked PRs

permissions:
  contents: read

env:
  UV_FROZEN: "1"

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - name: Detect code changes
        id: changes
        run: ./.github/scripts/detect-changes.sh code "${{ github.event.pull_request.base.sha || github.event.before }}" "${{ github.sha }}"
      - name: Setup uv
        if: steps.changes.outputs.run == 'true'
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
      - name: Install dependencies
        if: steps.changes.outputs.run == 'true'
        run: make sync
      - name: Verify formatting
        if: steps.changes.outputs.run == 'true'
        run: make format-check
      - name: Run lint
        if: steps.changes.outputs.run == 'true'
        run: make lint
      - name: Skip lint
        if: steps.changes.outputs.run != 'true'
        run: echo "Skipping lint for non-code changes."

  typecheck:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - name: Detect code changes
        id: changes
        run: ./.github/scripts/detect-changes.sh code "${{ github.event.pull_request.base.sha || github.event.before }}" "${{ github.sha }}"
      - name: Setup uv
        if: steps.changes.outputs.run == 'true'
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
      - name: Install dependencies
        if: steps.changes.outputs.run == 'true'
        run: make sync
      - name: Run typecheck
        if: steps.changes.outputs.run == 'true'
        run: make typecheck
      - name: Skip typecheck
        if: steps.changes.outputs.run != 'true'
        run: echo "Skipping typecheck for non-code changes."

  tests:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version:
          - "3.10"
          - "3.11"
          - "3.12"
          - "3.13"
          - "3.14"
    env:
      OPENAI_API_KEY: fake-for-tests
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - name: Detect code changes
        id: changes
        run: ./.github/scripts/detect-changes.sh code "${{ github.event.pull_request.base.sha || github.event.before }}" "${{ github.sha }}"
      - name: Setup uv
        if: steps.changes.outputs.run == 'true'
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        if: steps.changes.outputs.run == 'true'
        run: make sync
      - name: Run tests with coverage
        if: steps.changes.outputs.run == 'true' && matrix.python-version == '3.12'
        run: make coverage
      - name: Run tests
        if: steps.changes.outputs.run == 'true' && matrix.python-version != '3.12'
        run: make tests
      - name: Run async teardown stability tests
        if: steps.changes.outputs.run == 'true' && (matrix.python-version == '3.10' || matrix.python-version == '3.14')
        run: make tests-asyncio-stability
      - name: Skip tests
        if: steps.changes.outputs.run != 'true'
        run: echo "Skipping tests for non-code changes."

  build-docs:
    runs-on: ubuntu-latest
    env:
      OPENAI_API_KEY: fake-for-tests
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - name: Detect docs changes
        id: changes
        run: ./.github/scripts/detect-changes.sh docs "${{ github.event.pull_request.base.sha || github.event.before }}" "${{ github.sha }}"
      - name: Setup uv
        if: steps.changes.outputs.run == 'true'
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
      - name: Install dependencies
        if: steps.changes.outputs.run == 'true'
        run: make sync
      - name: Build docs
        if: steps.changes.outputs.run == 'true'
        run: make build-docs
      - name: Skip docs build
        if: steps.changes.outputs.run != 'true'
        run: echo "Skipping docs build for non-docs changes."
update-docs perms .github/workflows/update-docs.yml
Triggers
push, workflow_dispatch
Runs on
ubuntu-latest
Jobs
update-docs
Actions
astral-sh/setup-uv, peter-evans/create-pull-request
Commands
  • make sync
  • mode="${{ inputs.translate_mode || 'only-changes' }}" uv run docs/scripts/translate_docs.py --mode "$mode" uv run mkdocs build
  • git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add docs/ if git diff --cached --quiet; then echo "No changes to commit" echo "committed=false" >> "$GITHUB_OUTPUT" else git commit -m "Update all translated document pages" echo "committed=true" >> "$GITHUB_OUTPUT" fi
View raw YAML
name: "Update Translated Docs"

# This GitHub Actions job automates the process of updating all translated document pages. Please note the following:
# 1. The translation results may vary each time; some differences in detail are expected.
# 2. When you add a new page to the left-hand menu, **make sure to manually update mkdocs.yml** to include the new item.
# 3. If you switch to a different LLM (for example, from o3 to a newer model), be sure to conduct thorough testing before making the switch.

# To add more languages, you will update the following:
# 1.  Add '!docs/{lang}/**' to `on.push.paths` in this file
# 2. Update mkdocs.yml to have the new language
# 3. Update docs/scripts/translate_docs.py to have the new language

on:
  push:
    branches:
      - main
    paths:
      - 'docs/**'
      - mkdocs.yml
      - '!docs/ja/**'
      - '!docs/ko/**'
      - '!docs/zh/**'
  workflow_dispatch:
    inputs:
      translate_mode:
        description: "Translation mode"
        type: choice
        options:
          - only-changes
          - full
        default: only-changes

permissions:
  contents: write
  pull-requests: write

jobs:
  update-docs:
    if: "!contains(github.event.head_commit.message, 'Update all translated document pages')"
    name: Build and Push Translated Docs
    runs-on: ubuntu-latest
    timeout-minutes: 30
    env:
      PROD_OPENAI_API_KEY: ${{ secrets.PROD_OPENAI_API_KEY }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          fetch-depth: 0
      - name: Setup uv
        uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098
        with:
          enable-cache: true
      - name: Install dependencies
        run: make sync
      - name: Build translated docs
        run: |
          mode="${{ inputs.translate_mode || 'only-changes' }}"
          uv run docs/scripts/translate_docs.py --mode "$mode"
          uv run mkdocs build

      - name: Commit changes
        id: commit
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add docs/
          if git diff --cached --quiet; then
            echo "No changes to commit"
            echo "committed=false" >> "$GITHUB_OUTPUT"
          else
            git commit -m "Update all translated document pages"
            echo "committed=true" >> "$GITHUB_OUTPUT"
          fi

      - name: Create Pull Request
        if: steps.commit.outputs.committed == 'true'
        uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
        with:
          commit-message: "Update translated document pages"
          title: "docs: update translated document pages"
          body: |
            Automated update of translated documentation.

            Triggered by commit: [${{ github.event.head_commit.id }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.event.head_commit.id }}).
            Message: `${{ github.event.head_commit.message }}`
          branch: update-translated-docs-${{ github.run_id }}
          delete-branch: true