Replace cozystack-bot PAT with cozystack-ci GitHub App#2351
Conversation
Replace the cozystack-bot personal access token (GH_PAT) with a GitHub App (cozystack-ci) installation token across all CI/CD workflows. This improves security by using short-lived, scoped tokens instead of a long-lived PAT tied to a user account. Changes: - tags.yaml: use app token in all 3 jobs (prepare-release, generate-changelog, update-website-docs) - auto-release.yaml: use app token for daily patch releases - pull-requests-release.yaml: use app token for release finalization The cozystack-ci GitHub App (ID: 3297617) is installed org-wide with contents:write and pull-requests:write permissions. Secrets COZYSTACK_CI_APP_ID and COZYSTACK_CI_PRIVATE_KEY are set at org level. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
Note Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported. |
📝 WalkthroughWalkthroughThree GitHub Actions workflows migrated from GitHub PAT authentication to GitHub App token authentication. The change includes replacing Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/pull-requests-release.yaml:
- Around line 26-32: The workflow still references secrets.GH_PAT elsewhere and
will break if that secret is removed; update the pull-requests workflow to use
the GitHub App token produced by the create-github-app-token step instead of
GH_PAT: replace all occurrences of secrets.GH_PAT with the app token output
(i.e., use the step id app-token and reference ${{ steps.app-token.outputs.token
}} or pass that output between jobs if needed), ensure any job inputs/env vars
expecting GH_PAT are wired to steps.app-token.outputs.token, and verify required
permissions/owner settings remain correct (look for references to GH_PAT in
pull-requests.yaml and switch them to the app-token output).
- Around line 56-64: In the "Create tag on merge commit" step, ensure the
checkout's persisted auth header (http.https://github.com/.extraheader) is
cleared before performing the tag and push so the push uses APP_TOKEN rather
than the checkout token; add a command to unset that extraheader immediately
after setting the remote URL and before running git tag/tag push that references
APP_TOKEN and steps.get_tag.outputs.tag (and github.sha) so downstream workflows
trigger correctly.
In @.github/workflows/tags.yaml:
- Around line 28-34: The GitHub App token steps (e.g., the "Generate GitHub App
token" step with id "app-token" using actions/create-github-app-token@v1 and
owner: cozystack) currently omit a repositories: list and therefore grant access
to all org repos; update each such step (including the ones used by
prepare-release, generate-changelog, and update-website-docs) to include an
explicit repositories: array limited to only the repo(s) the job needs (for
prepare-release and generate-changelog add the current repo only; for
update-website-docs keep cozystack/cozystack and cozystack/website), ensuring
least privilege for the tokens.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 29f37968-45bf-4a37-b8db-b06bf41cee22
📒 Files selected for processing (3)
.github/workflows/auto-release.yaml.github/workflows/pull-requests-release.yaml.github/workflows/tags.yaml
| - name: Generate GitHub App token | ||
| id: app-token | ||
| uses: actions/create-github-app-token@v1 | ||
| with: | ||
| app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} | ||
| private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} | ||
| owner: cozystack |
There was a problem hiding this comment.
Finish the PAT migration before deleting GH_PAT.
The post-merge checklist removes GH_PAT, but the snippets from .github/workflows/pull-requests.yaml:128-135 and .github/workflows/pull-requests.yaml:188-198 still read secrets.GH_PAT. Merging this and then deleting the secret will break that workflow, so please migrate pull-requests.yaml in the same PR or defer the secret cleanup.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/pull-requests-release.yaml around lines 26 - 32, The
workflow still references secrets.GH_PAT elsewhere and will break if that secret
is removed; update the pull-requests workflow to use the GitHub App token
produced by the create-github-app-token step instead of GH_PAT: replace all
occurrences of secrets.GH_PAT with the app token output (i.e., use the step id
app-token and reference ${{ steps.app-token.outputs.token }} or pass that output
between jobs if needed), ensure any job inputs/env vars expecting GH_PAT are
wired to steps.app-token.outputs.token, and verify required permissions/owner
settings remain correct (look for references to GH_PAT in pull-requests.yaml and
switch them to the app-token output).
| - name: Create tag on merge commit | ||
| env: | ||
| GH_PAT: ${{ secrets.GH_PAT }} | ||
| APP_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| run: | | ||
| git config user.name "cozystack-bot" | ||
| git config user.email "217169706[email protected]" | ||
| git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} | ||
| git config user.name "cozystack-ci[bot]" | ||
| git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" | ||
| git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} | ||
| git tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }} | ||
| git push -f origin ${{ steps.get_tag.outputs.tag }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the file mentioned in the review
fd -t f "pull-requests-release.yaml" -x cat -n {}Repository: cozystack/cozystack
Length of output: 45
🏁 Script executed:
# Let's also find other workflow files to compare
fd -t f "\.github/workflows" -t f "\.ya?ml$" | head -20Repository: cozystack/cozystack
Length of output: 158
🏁 Script executed:
# Search for the git config --unset-all command in workflows
rg "git config --unset-all.*extraheader" .github/workflows/Repository: cozystack/cozystack
Length of output: 433
🏁 Script executed:
# Find workflow files correctly
find .github/workflows -type f -name "*.yaml" -o -name "*.yml"Repository: cozystack/cozystack
Length of output: 346
🏁 Script executed:
# Read the pull-requests-release.yaml file
cat -n .github/workflows/pull-requests-release.yamlRepository: cozystack/cozystack
Length of output: 9513
🏁 Script executed:
# Read tags.yaml to compare
cat -n .github/workflows/tags.yamlRepository: cozystack/cozystack
Length of output: 22623
🌐 Web query:
actions/checkout@v4 persist-credentials http.https.github.com.extraheader authentication
💡 Result:
actions/checkout@v4 “persists credentials” by configuring Git auth in the checked-out repo’s git config so later steps can run authenticated git fetch/push without re-supplying a token. In practice it sets an HTTP header via http.https://github.com/.extraheader (an AUTHORIZATION: basic … header) and removes it in the post-job cleanup; set persist-credentials: false to opt out. [1][3]
That extraheader auth takes precedence over putting a token in the remote URL, and trying to “override” it with git -c http.https://github.com/.extraheader=... can result in multiple AUTHORIZATION headers being sent. [2]
If you need to authenticate with a different token after checkout, the usual options are:
- Disable persistence up-front:
- uses: actions/checkout@v4 with: persist-credentials: false
- Or unset the header before your own git commands:
git config --local --unset-all http.https://github.com/.extraheader || true
Then configure your preferred auth method (PAT via checkout.token, a credential helper, SSH, etc.). [1][2]
Sources: [1] [2] [3]
🌐 Web query:
github actions checkout token precedence environment variable APP_TOKEN workflow triggering
💡 Result:
-
actions/checkouttoken precedence: the action authenticates using itswith: tokeninput. If you don’t set it, it defaults to${{ github.token }}(the run’sGITHUB_TOKEN). (github.com)- So an environment variable like
APP_TOKENdoes nothing by itself unless you explicitly wire it in, e.g.token: ${{ env.APP_TOKEN }}.
- So an environment variable like
-
Why your “APP_TOKEN” push/checkout may still act like
GITHUB_TOKEN: by default checkout persists the credentials it used into the repo’s git config (persist-credentials: true). Latergitcommands will keep using those credentials unless you change them (or setpersist-credentials: falseand configure your own auth). (github.com) -
Workflow triggering behavior: if you’re expecting a later workflow to run due to a
push/tag/PR created from a workflow, GitHub documents that to trigger workflows “from within a workflow run” you should use a GitHub App installation token or a PAT, notGITHUB_TOKEN. (docs.github.com)
Citations:
- 1: https://github.com/actions/checkout?utm_source=openai
- 2: https://github.com/orgs/community/discussions/179107?utm_source=openai
- 3: https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow?utm_source=openai
Unset checkout's persisted auth before the tag push.
actions/checkout@v4 persists its auth token by default via http.https://github.com/.extraheader, which takes precedence over the remote URL auth. Since line 62 only rewrites the remote URL to use APP_TOKEN, this push will still authenticate with the checkout token (by default GITHUB_TOKEN); if that happens, the tag push will not trigger downstream workflows (which requires App token or PAT auth). Add the same git config --unset-all http.https://github.com/.extraheader || true that other migrated workflows already use.
🔧 Minimal fix
git config user.name "cozystack-ci[bot]"
git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com"
git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}
+ git config --unset-all http.https://github.com/.extraheader || true
git tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }}
git push -f origin ${{ steps.get_tag.outputs.tag }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/pull-requests-release.yaml around lines 56 - 64, In the
"Create tag on merge commit" step, ensure the checkout's persisted auth header
(http.https://github.com/.extraheader) is cleared before performing the tag and
push so the push uses APP_TOKEN rather than the checkout token; add a command to
unset that extraheader immediately after setting the remote URL and before
running git tag/tag push that references APP_TOKEN and steps.get_tag.outputs.tag
(and github.sha) so downstream workflows trigger correctly.
| - name: Generate GitHub App token | ||
| id: app-token | ||
| uses: actions/create-github-app-token@v1 | ||
| with: | ||
| app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} | ||
| private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} | ||
| owner: cozystack |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the tags.yaml file at the specified line ranges
cd .github/workflows 2>/dev/null && pwd || echo "Not found in expected location"
fd -n "tags.yaml" . 2>/dev/null | head -5Repository: cozystack/cozystack
Length of output: 99
🏁 Script executed:
# Search for the tags.yaml file in the repo
fd -type f "tags.yaml"Repository: cozystack/cozystack
Length of output: 233
🏁 Script executed:
# Get the content of tags.yaml and examine the specific line ranges
git ls-files | grep -E "tags.yaml|workflows" | head -10Repository: cozystack/cozystack
Length of output: 346
🏁 Script executed:
# Read the tags.yaml file to verify the content
cat -n .github/workflows/tags.yaml | head -50Repository: cozystack/cozystack
Length of output: 1914
🏁 Script executed:
# Check the specific line ranges mentioned in the review (28-34, 250-256, 414-420)
cat -n .github/workflows/tags.yaml | sed -n '25,35p;248,258p;412,422p'Repository: cozystack/cozystack
Length of output: 1274
🏁 Script executed:
# Let's verify what jobs use these tokens
cat -n .github/workflows/tags.yaml | grep -B 20 "name: Generate GitHub App token" | grep "^[[:space:]]*[0-9]*[[:space:]]*jobs:\|name:"Repository: cozystack/cozystack
Length of output: 322
🏁 Script executed:
# Get more context on the jobs to understand their scope
cat -n .github/workflows/tags.yaml | sed -n '200,270p'Repository: cozystack/cozystack
Length of output: 3112
🌐 Web query:
actions/create-github-app-token v1 owner repositories scope documentation
💡 Result:
In actions/create-github-app-token@v1, the repo scope is controlled by the owner and repositories inputs:
owner(optional): the account (user/org) where the GitHub App is installed; defaults to the current repository owner if omitted. [1]repositories(optional): comma- or newline-separated repository names to scope the token to (within the givenowner). [1]
Scoping rules (what the token can access):
- If
owneris set andrepositoriesis empty → token is scoped to all repositories in that owner’s installation. [1] - If both
ownerandrepositoriesare empty → token is scoped to only the current repository. [1] - If
repositoriesis set (typically along withowner) → token is scoped to only those repositories. [1]
This maps to GitHub’s Installation Access Token behavior, where you can optionally restrict the token to specific repositories via the repositories (or repository_ids) parameters when creating the installation token. [2]
Example (scope to specific repos):
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: my-org
repositories: |
repo1
repo2[1]
Sources: [1] actions/create-github-app-token README/docs, [2] GitHub Docs: “Generating an installation access token for a GitHub App”.
🏁 Script executed:
# Get the update-website-docs job to see what it does
cat -n .github/workflows/tags.yaml | sed -n '407,500p'Repository: cozystack/cozystack
Length of output: 3754
🏁 Script executed:
# Let's see what the whole update-website-docs job does and what repos it accesses
cat -n .github/workflows/tags.yaml | tail -100Repository: cozystack/cozystack
Length of output: 4876
🏁 Script executed:
# Let's check other workflows for the same pattern
grep -r "actions/create-github-app-token" .github/workflows/ -A 3Repository: cozystack/cozystack
Length of output: 1629
🏁 Script executed:
# Count how many workflows have this pattern
grep -l "actions/create-github-app-token" .github/workflows/*.yaml | wc -lRepository: cozystack/cozystack
Length of output: 64
🏁 Script executed:
# Check if auto-release.yaml has owner set without repositories
cat .github/workflows/auto-release.yaml | grep -A 5 "actions/create-github-app-token"Repository: cozystack/cozystack
Length of output: 269
🏁 Script executed:
# Check if pull-requests-release.yaml has owner set without repositories
cat .github/workflows/pull-requests-release.yaml | grep -A 5 "actions/create-github-app-token"Repository: cozystack/cozystack
Length of output: 269
🏁 Script executed:
# Verify what the update-website-docs job accesses beyond cozystack/website
cat -n .github/workflows/tags.yaml | sed -n '437,483p'Repository: cozystack/cozystack
Length of output: 2437
Restrict GitHub App tokens to necessary repositories only.
Each of the three actions/create-github-app-token steps sets owner: cozystack while leaving repositories empty, which scopes the token to all repositories in the organization's installation. prepare-release and generate-changelog only need access to the current repository; update-website-docs explicitly accesses only cozystack/cozystack and cozystack/website at lines 440 and 478–482. Add explicit repositories: entries to each step to follow the principle of least privilege. The same pattern occurs in auto-release.yaml and pull-requests-release.yaml.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/tags.yaml around lines 28 - 34, The GitHub App token steps
(e.g., the "Generate GitHub App token" step with id "app-token" using
actions/create-github-app-token@v1 and owner: cozystack) currently omit a
repositories: list and therefore grant access to all org repos; update each such
step (including the ones used by prepare-release, generate-changelog, and
update-website-docs) to include an explicit repositories: array limited to only
the repo(s) the job needs (for prepare-release and generate-changelog add the
current repo only; for update-website-docs keep cozystack/cozystack and
cozystack/website), ensuring least privilege for the tokens.
## Summary Replaces the `cozystack-bot` user account + personal access token (`GH_PAT`) with the `cozystack-ci` GitHub App across all CI/CD release workflows. ### Why - **Security**: GitHub App tokens are short-lived (1h) and scoped, vs. a long-lived PAT tied to a user account - **Auditability**: App actions are clearly attributed to `cozystack-ci[bot]` - **No 2FA issue**: The bot account lacked 2FA; GitHub Apps don't require it - **Best practice**: GitHub recommends Apps over PATs for automation ### Changes | Workflow | What changed | |---|---| | `tags.yaml` | App token in all 3 jobs: prepare-release, generate-changelog, update-website-docs | | `auto-release.yaml` | App token for daily auto-patch tagging | | `pull-requests-release.yaml` | App token for release PR finalization | All `cozystack-bot` git identity replaced with `cozystack-ci[bot]`. ### New secrets (already configured at org level) - `COZYSTACK_CI_APP_ID` — GitHub App ID - `COZYSTACK_CI_PRIVATE_KEY` — GitHub App private key ### After merge - [ ] Delete `GH_PAT` repo-level secret - [ ] Remove `cozystack-bot` from the cozystack org 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated CI/CD automation authentication mechanisms across release and tagging workflows for improved security practices. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Andrei Kvapil <[email protected]>
Replaces the `cozystack-bot` user account + personal access token (`GH_PAT`) with the `cozystack-ci` GitHub App across all CI/CD release workflows. - **Security**: GitHub App tokens are short-lived (1h) and scoped, vs. a long-lived PAT tied to a user account - **Auditability**: App actions are clearly attributed to `cozystack-ci[bot]` - **No 2FA issue**: The bot account lacked 2FA; GitHub Apps don't require it - **Best practice**: GitHub recommends Apps over PATs for automation | Workflow | What changed | |---|---| | `tags.yaml` | App token in all 3 jobs: prepare-release, generate-changelog, update-website-docs | | `auto-release.yaml` | App token for daily auto-patch tagging | | `pull-requests-release.yaml` | App token for release PR finalization | All `cozystack-bot` git identity replaced with `cozystack-ci[bot]`. - `COZYSTACK_CI_APP_ID` — GitHub App ID - `COZYSTACK_CI_PRIVATE_KEY` — GitHub App private key - [ ] Delete `GH_PAT` repo-level secret - [ ] Remove `cozystack-bot` from the cozystack org 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> * **Chores** * Updated CI/CD automation authentication mechanisms across release and tagging workflows for improved security practices. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Andrei Kvapil <[email protected]>
The GH_PAT secret tied to cozystack-bot is no longer valid after migrating release workflows to the cozystack-ci GitHub App in #2351. This broke the resolve_assets and e2e jobs for release PRs. Co-Authored-By: Claude <[email protected]> Signed-off-by: Andrei Kvapil <[email protected]>
…2383) ## Summary - Replace `GH_PAT` (cozystack-bot PAT) with `cozystack-ci` GitHub App token in `pull-requests.yaml` - This was missed in #2351 and broke the `resolve_assets` / `e2e` jobs for release PRs (draft releases not visible with invalid token) ## Test plan - [ ] Re-run the release PR pipeline for `release-1.1.6` and verify `resolve_assets` job passes <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated continuous integration authentication mechanism to use GitHub App tokens instead of personal access tokens. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
The GH_PAT secret tied to cozystack-bot is no longer valid after migrating release workflows to the cozystack-ci GitHub App in #2351. This broke the resolve_assets and e2e jobs for release PRs. Co-Authored-By: Claude <[email protected]> Signed-off-by: Andrei Kvapil <[email protected]> (cherry picked from commit 1144211)
…2383) ## Summary - Replace `GH_PAT` (cozystack-bot PAT) with `cozystack-ci` GitHub App token in `pull-requests.yaml` - This was missed in #2351 and broke the `resolve_assets` / `e2e` jobs for release PRs (draft releases not visible with invalid token) ## Test plan - [ ] Re-run the release PR pipeline for `release-1.1.6` and verify `resolve_assets` job passes <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated continuous integration authentication mechanism to use GitHub App tokens instead of personal access tokens. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Andrei Kvapil <[email protected]>
…2383) ## Summary - Replace `GH_PAT` (cozystack-bot PAT) with `cozystack-ci` GitHub App token in `pull-requests.yaml` - This was missed in #2351 and broke the `resolve_assets` / `e2e` jobs for release PRs (draft releases not visible with invalid token) ## Test plan - [ ] Re-run the release PR pipeline for `release-1.1.6` and verify `resolve_assets` job passes <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated continuous integration authentication mechanism to use GitHub App tokens instead of personal access tokens. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Andrei Kvapil <[email protected]>
Summary
Replaces the
cozystack-botuser account + personal access token (GH_PAT) with thecozystack-ciGitHub App across all CI/CD release workflows.Why
cozystack-ci[bot]Changes
tags.yamlauto-release.yamlpull-requests-release.yamlAll
cozystack-botgit identity replaced withcozystack-ci[bot].New secrets (already configured at org level)
COZYSTACK_CI_APP_ID— GitHub App IDCOZYSTACK_CI_PRIVATE_KEY— GitHub App private keyAfter merge
GH_PATrepo-level secretcozystack-botfrom the cozystack org🤖 Generated with Claude Code
Summary by CodeRabbit