name: backport on: pull_request_target: types: [closed, labeled] permissions: contents: read pull-requests: write jobs: backport: name: Backport merged PR runs-on: ubuntu-latest if: > github.event.pull_request.merged == true && ( github.event.action == 'closed' || startsWith(github.event.label.name, 'backport ') ) steps: - name: Generate backport App token id: backport-app uses: actions/create-github-app-token@v3 with: client-id: ${{ vars.XCAT_BACKPORT_APP_CLIENT_ID }} private-key: ${{ secrets.XCAT_BACKPORT_APP_PRIVATE_KEY }} permission-contents: write permission-pull-requests: write permission-workflows: write - uses: actions/checkout@v6 with: token: ${{ steps.backport-app.outputs.token }} - name: Create backport pull requests id: backport uses: korthout/backport-action@v3 with: github_token: ${{ steps.backport-app.outputs.token }} auto_merge_enabled: true - name: Approve backport pull requests if: steps.backport.outputs.created_pull_numbers != '' uses: actions/github-script@v8 env: BACKPORT_APP_SLUG: ${{ steps.backport-app.outputs.app-slug }} CREATED_PULL_NUMBERS: ${{ steps.backport.outputs.created_pull_numbers }} with: github-token: ${{ github.token }} script: | const expectedAuthor = `${process.env.BACKPORT_APP_SLUG}[bot]`; const pullNumbers = process.env.CREATED_PULL_NUMBERS .trim() .split(/\s+/) .map(Number); for (const pull_number of pullNumbers) { if (!Number.isSafeInteger(pull_number) || pull_number <= 0) { throw new Error(`Invalid backport pull request number: ${pull_number}`); } const { data: pull } = await github.rest.pulls.get({ ...context.repo, pull_number, }); if (pull.user?.login !== expectedAuthor) { throw new Error( `Refusing to approve #${pull_number}: expected author ${expectedAuthor}`, ); } if (pull.head.repo?.full_name !== `${context.repo.owner}/${context.repo.repo}`) { throw new Error( `Refusing to approve #${pull_number}: head repository does not match`, ); } if (pull.state !== 'open' || pull.draft) { throw new Error( `Refusing to approve #${pull_number}: pull request is not open and ready`, ); } if (pull.auto_merge === null || pull.auto_merge === undefined) { throw new Error( `Refusing to approve #${pull_number}: auto-merge is not enabled`, ); } await github.rest.pulls.createReview({ ...context.repo, pull_number, commit_id: pull.head.sha, event: 'APPROVE', body: 'Automated approval for a conflict-free backport of an already merged pull request.', }); } - name: Fail when a backport needs intervention if: > steps.backport.outputs.was_successful_by_target != '' && steps.backport.outputs.was_successful != 'true' run: exit 1