Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/workflows/create-release-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
# This workflow is triggered via repository_dispatch from the
# build-and-release workflow in backup-utils-private. It copies
# docs and README.md from backup-utils-private to a release dev branch
# in backup-utils, then creates a PR using GITHUB_TOKEN so that the
# PR author is github-actions[bot]. This allows the app token in
# backup-utils-private to approve the PR as a different actor.
name: Create Release PR

on:
repository_dispatch:
types: [create-release-pr]

permissions:
contents: write
pull-requests: write

jobs:
create-release-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.RELEASE_CONTROLLER_APP_ID }}
private-key: ${{ secrets.RELEASE_CONTROLLER_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "backup-utils-private"

- name: Checkout backup-utils
uses: actions/checkout@v5

- name: Checkout backup-utils-private
uses: actions/checkout@v5
with:
token: ${{ steps.app-token.outputs.token }}
repository: github/backup-utils-private
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow uses ${{ github.repository_owner }} when minting the GitHub App token, but hardcodes repository: github/backup-utils-private for the checkout. Using the same owner variable for the checkout target avoids drift if the repo is ever moved or this workflow is reused elsewhere.

Suggested change
repository: github/backup-utils-private
repository: ${{ github.repository_owner }}/backup-utils-private

Copilot uses AI. Check for mistakes.
path: './backup-utils-private'

- name: Replace docs directory with backup-utils-private docs
run: |
rm -rf docs
cp -r backup-utils-private/docs docs

- name: Replace README.md with backup-utils-private README.md
run: |
rm README.md
cp backup-utils-private/README.md README.md

- name: Delete backup-utils-private
run: |
rm -rf backup-utils-private

- name: Create release branch commit
run: |
version="${{ github.event.client_payload.version }}"
branch="release/$version"
Comment on lines +56 to +57
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github.event.client_payload.version is interpolated directly into the shell script. If the dispatch payload contains characters like quotes/newlines, this can break out of the assignment and lead to shell injection (and potential secret exfiltration). Pass the value via env: (or read it from $GITHUB_EVENT_PATH with jq) and validate/sanitize it (e.g., enforce a semver/allowed-charset pattern) before using it in git/gh commands.

This issue also appears on line 70 of the same file.

Copilot uses AI. Check for mistakes.
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -B "$branch"
git add docs README.md
git commit --allow-empty -m "$version release"
git push --force-with-lease --set-upstream origin "$branch"
Comment on lines +61 to +63
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git push --force-with-lease can fail on reruns when the remote release/$version branch already exists but hasn’t been fetched into a local origin/release/... tracking ref (common with actions/checkout default fetch-depth: 1). If you intend this workflow to be idempotent, fetch the remote branch first (if it exists) or adjust the push strategy so reruns can update the branch reliably.

Copilot uses AI. Check for mistakes.

- name: Create or find release pull request
id: release-pr
env:
GH_TOKEN: ${{ github.token }}
run: |
version="${{ github.event.client_payload.version }}"
branch="release/$version"
pr_number="$(gh pr list --head "$branch" --base master --json number --jq '.[0].number')"
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gh pr list ... --jq '.[0].number' returns null (string) when no PRs are found, not an empty string. With the current -z check, pr_number becomes null and the workflow will skip PR creation and later try to merge a PR numbered null. Update the jq filter / conditional to treat null as empty.

Suggested change
pr_number="$(gh pr list --head "$branch" --base master --json number --jq '.[0].number')"
pr_number="$(gh pr list --head "$branch" --base master --json number --jq '.[0].number // empty')"

Copilot uses AI. Check for mistakes.

if [ -z "$pr_number" ]; then
pr_url="$(gh pr create \
--base master \
--head "$branch" \
--title "$version release" \
--body "Automated release PR for v$version.")"
pr_number="${pr_url##*/}"
fi

echo "number=$pr_number" >> "$GITHUB_OUTPUT"

- name: Enable auto-merge on release pull request
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr merge \
--squash \
--auto \
"${{ steps.release-pr.outputs.number }}"