name: Codex Update

on:
  schedule:
    - cron: "0 5 * * *"
  workflow_dispatch:

permissions:
  contents: read
  packages: read

env:
  CODEX_PACKAGE: "@openai/codex"
  CODEX_UPDATE_LABEL: "codex-update"
  GITHUB_BOT_USERNAME: "github-actions[bot]"

jobs:
  check:
    runs-on: ubuntu-latest
    outputs:
      should_update: ${{ steps.check.outputs.should_update }}
      version: ${{ steps.check.outputs.version }}
      branch: ${{ steps.check.outputs.branch }}
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0

      - name: Check if update is required
        id: check
        run: |
          RAW=$(jq -r --arg pkg "$CODEX_PACKAGE" '.dependencies[$pkg]' package.json)
          CURRENT="${RAW#[\^~]}"
          LATEST=$(npm view "$CODEX_PACKAGE" version)

          if [ "$CURRENT" = "$LATEST" ]; then
            echo "Package is up to date ($CURRENT)"
            exit 0
          fi

          BRANCH="codex-update/$LATEST"

          if git ls-remote --exit-code --heads origin "refs/heads/$BRANCH" >/dev/null 2>&1; then
            echo "Branch $BRANCH already exists"
            exit 0
          fi

          echo "version=$LATEST" >> "$GITHUB_OUTPUT"
          echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
          echo "should_update=true" >> "$GITHUB_OUTPUT"

  update:
    needs: check
    if: needs.check.outputs.should_update == 'true'
    runs-on: ubuntu-latest
    env:
      BRANCH: ${{ needs.check.outputs.branch }}
      VERSION: ${{ needs.check.outputs.version }}
    steps:
      - name: Generate GitHub token
        uses: actions/create-github-app-token@v3
        id: generate-token
        with:
          app-id: ${{ secrets.RELEASE_PLZ_APP_ID }}
          private-key: ${{ secrets.RELEASE_PLZ_APP_PRIVATE_KEY }}

      - uses: actions/checkout@v7
        with:
          token: ${{ steps.generate-token.outputs.token }}

      - name: Configure git user and prepare branch
        run: |
          git config user.name "$GITHUB_BOT_USERNAME"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git checkout -b "$BRANCH" --

      - name: Install new version of package and commit
        run: |
          npm install "$CODEX_PACKAGE@$VERSION"
          npm run generate-types
          git add package.json package-lock.json src/app-server
          git commit -m "Update codex to $VERSION"

      - name: Finalize update with Codex
        id: codex-finalize
        uses: openai/codex-action@52fe01ec70a42f454c9d2ebd47598f9fd6893d56
        with:
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}
          codex-version: ${{ env.VERSION }}
          output-file: pr-body.md
          codex-args: >-
            -c sandbox_workspace_write.writable_roots=["${{ github.workspace }}/.git"]
          prompt: >
            Finalize the update using codex-update-compat skill.
            Commit only tracked file changes, and never create an empty commit.
            If there are changes, the commit message should mention that types or/and tests after the update were fixed.
            When creating the final message do not mention:
            * Validation run details.
            * Commits.
            * Hyperlinks.
            Mention only previously failed tests with failure reasons, also what you changed and why.

      - name: Push branch updates
        run: |
          git push -- origin "$BRANCH"

      - name: Create PR
        env:
          GH_TOKEN: ${{ steps.generate-token.outputs.token }}
        run: |
          echo -e "[What's new](https://github.com/openai/codex/releases/tag/rust-v${VERSION})\n\n$(cat pr-body.md 2>/dev/null || true)" > pr-body.md

          gh pr create \
            --base main \
            --title "Update codex to $VERSION" \
            --body-file pr-body.md \
            --label "$CODEX_UPDATE_LABEL"

      - name: Close obsolete PRs
        env:
          GH_TOKEN: ${{ steps.generate-token.outputs.token }}
        run: |
          set -euo pipefail

          git fetch origin main --no-tags

          gh pr list \
            --state open \
            --label "$CODEX_UPDATE_LABEL" \
            --json number,headRefName \
            --jq '.[] | [.number, .headRefName] | @tsv' |
          while IFS=$'\t' read -r pr_number head_ref; do
            if [[ "$head_ref" == "$BRANCH" || "$head_ref" != codex-update/* ]]; then
              continue
            fi

            git fetch origin "$head_ref" --no-tags

            # If there are any non-bot commits, don't close the PR
            if git log --format='%cn' "origin/main..origin/${head_ref}" | grep -Fvxq "$GITHUB_BOT_USERNAME"; then
              continue
            fi

            gh pr close "$pr_number" \
              --delete-branch \
              --comment "Closing obsolete update PR"
          done
