Skip to content
Merged
Show file tree
Hide file tree
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
7 changes: 0 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# Changelog

## [0.1.1](https://github.com/masterpointio/github-action-trunk-upgrade/compare/v0.1.0...v0.1.1) (2025-09-01)


### Bug Fixes

* don't require GH PAT ([#5](https://github.com/masterpointio/github-action-trunk-upgrade/issues/5)) ([0b927a3](https://github.com/masterpointio/github-action-trunk-upgrade/commit/0b927a33380e33d406f8d68d4f746fa6447d1a01))

## 0.1.0 (2025-08-18)


Expand Down
34 changes: 21 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

A reusable GitHub Action for automated [Trunk](https://trunk.io) upgrades with status check handling and auto-merge.

This action automates the process of keeping your Trunk configuration up-to-date by creating pull requests for upgrades and automatically merging them after status checks pass. It follows security best practices with dual-token authentication and only waits for required status checks, avoiding unnecessary delays from optional checks.
This action automates the process of keeping your Trunk configuration up-to-date by creating pull requests for upgrades and automatically merging them after status checks pass. It follows security best practices with a two-token setup and only waits for required status checks, avoiding unnecessary delays from optional checks.

## Usage

### Prerequisites

- GitHub repository with Trunk configuration
- Personal Access Token (required)
- Personal Access Token from a code owner or team member (required)
- GitHub App credentials (recommended for enhanced performance and security)
- Repository permissions: `contents: write` and `pull-requests: write`

Expand All @@ -26,7 +26,7 @@ This action automates the process of keeping your Trunk configuration up-to-date
1. **Set up authentication secrets** in your repository:
- `BOT_APP_ID` - GitHub App ID
- `BOT_APP_PRIVATE_KEY` - GitHub App private key
- `ORG_PAT` - Personal Access Token with admin permissions
- `CODE_OWNER_PAT` - Personal Access Token from a code owner or team member
2. **Create workflow file** `.github/workflows/trunk-upgrade.yml`:

```yaml
Expand All @@ -50,7 +50,7 @@ jobs:
with:
app-id: ${{ secrets.BOT_APP_ID }}
app-private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }}
github-token: ${{ secrets.ORG_PAT }}
github-token: ${{ secrets.CODE_OWNER_PAT }}
reviewers: "@org/engineering"
```

Expand Down Expand Up @@ -84,33 +84,41 @@ with:
github-token: ${{ secrets.GITHUB_TOKEN }}
```

**Dual-Token (Recommended):**
**Two-Token Setup (Recommended):**

```yaml
with:
app-id: ${{ secrets.BOT_APP_ID }}
app-private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }}
github-token: ${{ secrets.TEAM_PAT }}
github-token: ${{ secrets.CODE_OWNER_PAT }}
```

**How the dual-token setup works:**
**How the two-token setup works:**

1. **GitHub App Token (Primary)**: Generated from `app-id` + `app-private-key`
- Used for PR creation via trunk-io action
- Higher rate limits (5,000/hr vs 1,000/hr)
- Clean bot attribution in commits
- Scoped permissions (only what the app needs)

2. **Personal Access Token (Fallback/Admin)**: `github-token` input
- Used for merge operations with `--admin` flag
- Bypasses branch protection rules reliably
- Required for repositories with strict protection settings
2. **Personal Access Token (Code Reviewer)**: `github-token` input
- Used for merge operations to satisfy code owner requirements
- Should be from a user who is a code owner or team member
- Required for repositories with code owner review requirements
- Bypasses the "can't approve own PR" limitation
- Falls back if no App credentials provided

**Why Two-Token Setup is Recommended:**

Many repositories have branch protection rules requiring code owner reviews. When a GitHub App creates a PR, it cannot approve its own PR due to GitHub's security model. The two-token approach solves this by:

- **App creates the PR** → Clean bot attribution
- **Code owner PAT approves/merges** → Satisfies repository protection rules

**Token Selection Logic:**

- If App credentials provided → Use App token for PR creation, PAT for merge
- If no App credentials → Use PAT for both operations
- If App credentials provided → Use App token for PR creation, PAT for approval/merge
- If no App credentials → Use PAT for both operations (**Note**: This won't work if the repository has rulesets or branch protection rules requiring code owner reviews, since the same user/token cannot create and approve their own PR)

## Built By

Expand Down
22 changes: 12 additions & 10 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ author: hello@masterpoint.io

inputs:
github-token:
description: GitHub token for creating PRs and performing operations (required if app credentials not provided)
description: GitHub token for creating PRs and performing operations
required: true

app-id:
description: GitHub App ID for bot authentication
Expand Down Expand Up @@ -51,9 +52,8 @@ runs:
- name: Validate inputs
shell: bash
run: |
# Ensure either github-token OR app credentials are provided
if [[ -z "${{ inputs.github-token }}" && ( -z "${{ inputs.app-id }}" || -z "${{ inputs.app-private-key }}" ) ]]; then
echo "::error::Either github-token must be provided, or both app-id and app-private-key must be provided"
if [[ -z "${{ inputs.github-token }}" ]]; then
echo "::error::github-token is required"
exit 1
fi

Expand Down Expand Up @@ -93,7 +93,7 @@ runs:
id: auto-merge
shell: bash
env:
GH_TOKEN: ${{ steps.github-token.outputs.token }}
GH_TOKEN: ${{ inputs.github-token }}
PR_NUMBER: ${{ steps.trunk-upgrade.outputs.pull-request-number }}
REPO_URL: https://github.com/${{ github.repository }}
MERGE_METHOD: ${{ inputs.merge-method }}
Expand Down Expand Up @@ -124,8 +124,10 @@ runs:
echo "$1" | jq '[.[] | select(.state!="SUCCESS" or .bucket!="pass")] | length'
}

merge_pr() {
echo "🤖 Auto-merging PR $REPO_URL/pull/$PR_NUMBER..."
approve_and_merge_pr() {
local approval_message="$1"
echo "🤖 Auto-approving and merging PR $REPO_URL/pull/$PR_NUMBER..."
gh pr review "$PR_NUMBER" --approve --body "$approval_message"

# Retry merge up to 3 times to handle base branch updates
local max_retries=3
Expand Down Expand Up @@ -167,8 +169,8 @@ runs:
# Handle case with no required checks - can merge immediately
if [ "$REQUIRED_COUNT" -eq 0 ]; then
echo "✅ No required status checks configured. PR is ready to merge."
echo "Proceeding with auto-merge..."
if merge_pr; then
echo "Proceeding with auto-approval and merge..."
if approve_and_merge_pr "Auto-approved by trunk upgrade action (no required status checks)"; then
exit 0
else
echo "❌ Failed to merge PR. Exiting with error."
Expand Down Expand Up @@ -206,7 +208,7 @@ runs:
# Check if all required checks have passed
PENDING_COUNT=$(count_pending_checks "$CURRENT_CHECKS")
if [ "$PENDING_COUNT" -eq 0 ]; then
if merge_pr; then
if approve_and_merge_pr "Auto-approved by trunk upgrade action (all required checks passed)"; then
break
else
echo "❌ Failed to merge PR after all checks passed. Exiting with error."
Expand Down