name: 'Util: Sync API Docs'

on:
  # Triggers for the master branch if relevant Public API files have changed
  push:
    branches:
      - master
    paths:
      # Trigger if:
      # - any of the public API files change
      - 'packages/cli/src/public-api/**/*.{css,yaml,yml}'
      # - the build script or dependencies change
      - 'packages/cli/package.json'
      # - any main dependencies change
      - 'pnpm-lock.yaml'

  # Allow manual trigger
  workflow_dispatch:

jobs:
  sync-public-api:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      actions: read # Needed for `gh` to read the workflow history

    steps:
      - name: Checkout Main n8n Repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0 # Fetch all history for git log

      - name: Setup Node.js and install dependencies
        uses: ./.github/actions/setup-nodejs
        with:
          build-command: ''

      - name: Build Public API Schema
        run: pnpm run build:data
        working-directory: ./packages/cli

      - name: Verify OpenAPI schema exists
        id: verify_file
        run: |
          if [[ -f "packages/cli/dist/public-api/v1/openapi.yml" ]]; then
            echo "OpenAPI file found: packages/cli/dist/public-api/v1/openapi.yml"
            echo "file_exists=true" >> "$GITHUB_OUTPUT"
          else
            echo "ERROR: OpenAPI file not found at packages/cli/dist/public-api/v1/openapi.yml after build."
            echo "file_exists=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Generate GitHub App Token
        if: steps.verify_file.outputs.file_exists == 'true'
        id: generate_token
        uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
        with:
          app-id: ${{ secrets.N8N_ASSISTANT_APP_ID }}
          private-key: ${{ secrets.N8N_ASSISTANT_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}
          repositories: n8n-docs

      - name: Checkout Docs Repository
        if: steps.verify_file.outputs.file_exists == 'true'
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          repository: n8n-io/n8n-docs
          token: ${{ steps.generate_token.outputs.token }}
          path: public-docs

      - name: Copy OpenAPI file to Docs Repo
        if: steps.verify_file.outputs.file_exists == 'true'
        run: |
          # Destination path within the 'public-docs' checkout directory
          DOCS_TARGET_PATH="public-docs/docs/api/v1/openapi.yml"

          echo "Copying 'packages/cli/dist/public-api/v1/openapi.yml' to '${DOCS_TARGET_PATH}'"
          cp packages/cli/dist/public-api/v1/openapi.yml "${DOCS_TARGET_PATH}"

      - name: Find Relevant Source Commits
        id: find_source_commits
        if: steps.verify_file.outputs.file_exists == 'true'
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          echo "Finding last successful workflow run..."
          LAST_SUCCESS_SHA=$(gh run list \
            --workflow "${{ github.workflow }}" \
            --branch "${{ github.ref_name }}" \
            --status "success" \
            --json "headSha" \
            --jq '.[0].headSha // empty' \
            -L 1)

          RELEVANT_COMMITS=""
          if [[ -n "$LAST_SUCCESS_SHA" ]]; then
            echo "Last successful commit: $LAST_SUCCESS_SHA"
            echo "Current commit: ${{ github.sha }}"

            # Find all commits between the last success and now that touched the relevant files
            # NOTE: The pathspecs here mirror the workflow's 'paths' trigger for precision
            RELEVANT_COMMITS=$(git log --pretty=format:"- [%h](https://github.com/${{ github.repository }}/commit/%H) %s" "${LAST_SUCCESS_SHA}..${{ github.sha }}" -- \
              'packages/cli/src/public-api/**/*.css' \
              'packages/cli/src/public-api/**/*.yaml' \
              'packages/cli/src/public-api/**/*.yml')
          fi

          if [[ -z "$RELEVANT_COMMITS" ]]; then
            if [[ -z "$LAST_SUCCESS_SHA" ]]; then
              echo "No previous successful run found. Using current commit as source."
            else
              echo "No commits touching source files found. Linking to trigger commit (likely a dependency update)."
            fi
            FULL_SHA="${{ github.sha }}"
            SHORT_SHA="${FULL_SHA:0:7}"
            SOURCE_LINK="Source commit: [$SHORT_SHA](https://github.com/${{ github.repository }}/commit/$FULL_SHA)"
          else
            echo "Found relevant source commits:"
            echo "$RELEVANT_COMMITS"
            # Build the string manually to avoid heredoc capturing leading whitespace from YAML
            SOURCE_LINK="Source commit(s):"$'\n'"$RELEVANT_COMMITS"
          fi

          {
            echo "source_link<<EOF"
            echo "$SOURCE_LINK"
            echo "EOF"
          } >> "$GITHUB_OUTPUT"

      - name: Create PR in Docs Repo
        if: steps.verify_file.outputs.file_exists == 'true'
        env:
          GH_ACTOR: ${{ github.actor }}

        # Pin v7.0.8
        uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
        with:
          token: ${{ steps.generate_token.outputs.token }}

          path: public-docs
          commit-message: 'feat(public-api): Update Public API schema'
          committer: GitHub <noreply@github.com>
          author: ${{ env.GH_ACTOR }} <${{ github.actor_id }}+${{ env.GH_ACTOR }}@users.noreply.github.com>
          signoff: false

          # Create a single branch for multiple PRs
          branch: 'chore/sync-public-api-schema'
          delete-branch: false

          title: 'chore: Update Public API schema'
          body: |
            Automated update of the Public API OpenAPI YAML schema.

            This PR was generated by a GitHub Action in the [${{ github.repository }} repository](https://github.com/${{ github.repository }}).
            ${{ steps.find_source_commits.outputs.source_link }}

            Please review the changes and merge if appropriate.
