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
25 changes: 23 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,18 @@ Supported sources:

CLI flags and environment variables override config values for one run. Legacy `UPSTREAM_OWNER`, `UPSTREAM_REPO`, `UPSTREAM_REF`, `RELEASE_SELECTOR`, `BASE_BRANCH`, `SYNC_BRANCH`, and `PATCH_REFS` variables remain supported for migration.

GitHub issue notifications are optional. Patchlane keeps one issue per failure event, updates it on repeated failures, assigns configured users individually, and closes it after a successful run when `closeOnRecovery` is enabled. The generated workflows request `issues: write` only when they handle an enabled GitHub issue event. Notification API errors are warnings and do not replace the sync, CI, or promotion result.
GitHub issue notifications are optional. Patchlane keeps one issue per failure event, updates it on repeated failures, assigns configured users individually, and closes it after a successful run when `closeOnRecovery` is enabled. The generated App tokens request `issues: write` only when they handle an enabled GitHub issue event. Notification API errors are warnings and do not replace the sync, CI, or promotion result.

## GitHub App authentication

The generated workflows require:

- repository variable `PATCHLANE_APP_CLIENT_ID`
- repository secret `PATCHLANE_APP_PRIVATE_KEY`

The App installation must grant Contents read/write and Workflows write. Enable Issues read/write when GitHub issue notifications are configured. Patchlane requests these permissions explicitly when creating each short-lived token, passes the token to checkout and `gh` as `GH_TOKEN`, and leaves the built-in `GITHUB_TOKEN` read-only.

A GitHub App or user token is required for pushes that must start another workflow. GitHub deliberately suppresses most workflow events caused by the built-in `GITHUB_TOKEN`; increasing its workflow permissions does not change that behavior. See [Manual setup](manual-setup.md) for App creation and repository configuration.

## Commands

Expand All @@ -72,7 +83,17 @@ npx patchlane doctor
npx patchlane doctor --json
```

Doctor checks source resolution, remote patch refs, patch bases, composed workflow configuration and policy, CI triggers, permissions, and bootstrap state without changing repository state.
Doctor checks source resolution, remote patch refs, patch bases, composed workflow configuration and policy, CI triggers, App-token wiring, permissions, and bootstrap state without changing repository state. For GitHub origins it also attempts to inspect Actions enablement and the names—not values—of the expected repository variable and secret. Insufficient metadata access is reported as a warning.

### Verify workflow authentication

After the generated workflows are present on the base branch, run:

```bash
npx patchlane verify-auth
```

This dispatches `sync-upstream.yml` with `no_push=true`, finds the newly created run, and waits for it to finish. A successful run validates the uploaded App key, installation, requested permissions, API access, authenticated checkout, and Patchlane rebuild without changing a branch. Use `--timeout <seconds>` and `--poll-interval <seconds>` to override the 60-second discovery timeout and 2-second polling interval. The equivalent environment variables are `PATCHLANE_AUTH_TIMEOUT_SECONDS` and `PATCHLANE_AUTH_POLL_INTERVAL_SECONDS`.

### Validate or publish a sync

Expand Down
40 changes: 33 additions & 7 deletions docs/manual-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,27 @@ This walkthrough configures an existing GitHub fork. You need Node.js 22+, `git`

Already using an earlier Patchlane version? Follow the [migration guide](migrations.md) instead.

## 1. Choose the upstream source
## 1. Configure GitHub App authentication

Patchlane must push as a GitHub App so pushes to `sync/integration` and the promoted base branch trigger their configured workflows. GitHub suppresses most workflow events caused by the built-in `GITHUB_TOKEN`, so granting that token `contents: write` is not sufficient.

Create or reuse a GitHub App, install it on the fork, and grant these repository permissions:

- **Contents: read and write**
- **Workflows: write**
- **Issues: read and write** only when GitHub issue notifications are enabled

The App does not need Actions write permission. Disable webhooks unless the App has another use that requires them. Record its Client ID and generate a private key, then configure the repository without printing the private key:

```bash
FORK=OWNER/REPOSITORY
gh variable set PATCHLANE_APP_CLIENT_ID --repo "$FORK" --body "YOUR_APP_CLIENT_ID"
gh secret set PATCHLANE_APP_PRIVATE_KEY --repo "$FORK" < /path/to/app-private-key.pem
```

The generated workflows use `actions/create-github-app-token` to request only the permissions needed by each job. Token creation fails if the App is not installed on the fork or lacks a requested permission.

## 2. Choose the upstream source

Add the upstream remote if needed:

Expand Down Expand Up @@ -32,7 +52,7 @@ git fetch upstream main

Keep `SOURCE_REF` unchanged for the remaining steps. Every patch branch must start independently from this commit.

## 2. Create the Patchlane patch
## 3. Create the Patchlane patch

Find the exact `name:` of the existing CI workflow, then create `patch/sync`:

Expand All @@ -52,7 +72,7 @@ git push -u origin patch/sync

Replace `CI` with the existing workflow's exact name. Do not rename the workflow just for Patchlane.

## 3. Make CI test generated syncs
## 4. Make CI test generated syncs

Create `patch/ci` from the same source—not from `patch/sync`:

Expand All @@ -79,7 +99,7 @@ git commit -m "Run fork CI on Patchlane syncs"
git push -u origin patch/ci
```

## 4. Validate
## 5. Validate

Return to the branch containing `.patchlane.yml`:

Expand All @@ -89,9 +109,9 @@ npx patchlane doctor
npx patchlane sync --dry-run
```

`doctor` should have no errors. A bootstrap warning is expected because the workflows are not on the generated base branch yet.
`doctor` should have no errors. It checks the generated App-token wiring and, when your local `gh` credentials permit it, confirms that Actions is enabled and the expected repository variable and secret exist. GitHub does not expose secret values, so this cannot prove that the uploaded private key is valid. A bootstrap warning is expected because the workflows are not on the generated base branch yet.

## 5. Bootstrap the first promotion
## 6. Bootstrap the first promotion

Validate once more without publishing:

Expand All @@ -105,7 +125,13 @@ Then publish, wait for CI, and promote the exact successful SHA:
npx patchlane bootstrap --wait
```

After this succeeds, scheduled syncs and automatic promotions are active.
After this succeeds, run the post-bootstrap authentication check:

```bash
npx patchlane verify-auth
```

This dispatches the sync workflow with `no_push=true`, waits for it, and fails if the App credentials, installation, requested permissions, API access, checkout, or rebuild are invalid. It does not change any branches. After it succeeds, scheduled syncs and automatic promotions are active.

## Adding product patches

Expand Down
30 changes: 23 additions & 7 deletions docs/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Follow the section for the version you are adopting. Migration notes are listed

## vNext

vNext requires every version-1 `.patchlane.yml` file to define `allowedWorkflows`. Patchlane implicitly includes its generated `sync-upstream.yml` and `promote-tested-sync.yml` workflows, so list only repository-specific workflows.
vNext requires every version-1 `.patchlane.yml` file to define `allowedWorkflows` and updates generated workflows to authenticate with a GitHub App. Patchlane implicitly includes its generated `sync-upstream.yml` and `promote-tested-sync.yml` workflows, so list only repository-specific workflows.

### 1. Inventory the composed workflow tree

Expand Down Expand Up @@ -43,7 +43,19 @@ allowedWorkflows:

Use `allowedWorkflows: []` when the composed tree should contain only Patchlane's generated workflows. Add patches that delete unwanted upstream workflows rather than allowing them merely because they currently exist.

### 3. Validate before rollout
### 3. Configure GitHub App authentication

Create or reuse a GitHub App installed on the fork with Contents read/write and Workflows write. Add Issues read/write when issue notifications are enabled. Configure its credentials using the standard names:

```bash
FORK=OWNER/REPOSITORY
gh variable set PATCHLANE_APP_CLIENT_ID --repo "$FORK" --body "YOUR_APP_CLIENT_ID"
gh secret set PATCHLANE_APP_PRIVATE_KEY --repo "$FORK" < /path/to/app-private-key.pem
```

Replace both generated workflow files with the vNext templates. Do not merely pass `github.token` as `GH_TOKEN`: that fixes API authentication, but pushes made by the built-in `GITHUB_TOKEN` do not start the required downstream workflow runs.

### 4. Validate before rollout

After pushing the updated patch refs, validate with the vNext package version selected for the migration:

Expand All @@ -54,17 +66,21 @@ npx patchlane@VERSION sync --dry-run

Doctor should identify every unexpected or missing workflow by filename. The dry run validates the actual output after all configured patches are replayed without changing the local or remote sync branch.

### 4. Roll out through a tested promotion
### 5. Roll out through a tested promotion

Update the pinned Patchlane version in the sync and promotion workflows as part of the same configuration patch. From that patch branch, use the new client for the first policy-enforced rebuild and promotion:

```bash
npx patchlane@VERSION bootstrap --wait
```

This validates the allowlist before publishing `sync/integration`, waits for CI on the exact published SHA, revalidates that SHA, and then promotes it. Confirm afterward that the generated base contains the intended workflow set and that scheduled syncs use the new Patchlane version.
This validates the allowlist before publishing `sync/integration`, waits for CI on the exact published SHA, revalidates that SHA, and then promotes it. Confirm afterward that the generated base contains the intended workflow set and that scheduled syncs use the new Patchlane version. Then validate the uploaded App credentials with a no-push workflow run:

```bash
npx patchlane@VERSION verify-auth
```

### 5. Optionally enable maintainer notifications
### 6. Optionally enable maintainer notifications

Failure notifications are opt-in. Existing configurations that omit `notifications` keep their current behavior and require no notification-specific migration.

Expand All @@ -87,8 +103,8 @@ notifications:

Update both generated workflow files from the vNext templates in the same patch:

- `sync-upstream.yml` must grant `issues: write`, report `sync-failed` after a failed sync, and optionally report recovery after success.
- `promote-tested-sync.yml` must grant `issues: write`, report unsuccessful CI workflow runs, report failed promotions, and optionally report each recovery.
- `sync-upstream.yml` must request `permission-issues: write` for its App token, report `sync-failed` after a failed sync, and optionally report recovery after success.
- `promote-tested-sync.yml` must request `permission-issues: write` in jobs that report issues, report unsuccessful CI workflow runs, report failed promotions, and optionally report each recovery.

Do not add only the configuration block: existing workflow files do not invoke `patchlane notify`, and `patchlane doctor` reports missing `issues: write` permissions. Keep notification steps on `continue-on-error` so a GitHub API or assignment failure cannot replace the original automation result.

Expand Down
13 changes: 11 additions & 2 deletions examples/promote-tested-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
types: [completed]

permissions:
contents: write
contents: read

jobs:
promote:
Expand All @@ -15,10 +15,18 @@ jobs:
github.event.workflow_run.head_branch == 'sync/integration'
runs-on: ubuntu-latest
steps:
- name: Create Patchlane token
id: patchlane-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.PATCHLANE_APP_CLIENT_ID }}
private-key: ${{ secrets.PATCHLANE_APP_PRIVATE_KEY }}
permission-contents: write
permission-workflows: write
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ steps.patchlane-token.outputs.token }}

- uses: actions/setup-node@v4
with:
Expand All @@ -27,4 +35,5 @@ jobs:
- name: Run patchlane promote
run: npx patchlane@0.4.1 promote
env:
GH_TOKEN: ${{ steps.patchlane-token.outputs.token }}
EXPECTED_SYNC_SHA: ${{ github.event.workflow_run.head_sha }}
19 changes: 17 additions & 2 deletions examples/sync-upstream.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Sync Upstream Integration
run-name: ${{ inputs.verification_id && format('Verify Patchlane authentication ({0})', inputs.verification_id) || 'Sync Upstream Integration' }}

on:
schedule:
Expand All @@ -20,18 +21,31 @@ on:
type: string
required: false
default: ''
verification_id:
description: Correlate a Patchlane authentication check run.
type: string
required: false
default: ''

permissions:
contents: write
contents: read

jobs:
fork-sync:
runs-on: ubuntu-latest
steps:
- name: Create Patchlane token
id: patchlane-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.PATCHLANE_APP_CLIENT_ID }}
private-key: ${{ secrets.PATCHLANE_APP_PRIVATE_KEY }}
permission-contents: write
permission-workflows: write
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ steps.patchlane-token.outputs.token }}

- uses: actions/setup-node@v4
with:
Expand All @@ -40,6 +54,7 @@ jobs:
- name: Run patchlane sync
run: npx patchlane@0.4.1 sync
env:
GH_TOKEN: ${{ steps.patchlane-token.outputs.token }}
UPSTREAM_SOURCE: ${{ inputs.source }}
PATCH_REFS: ${{ inputs.patch_refs }}
NO_PUSH: ${{ inputs.no_push || false }}
26 changes: 23 additions & 3 deletions skills/patchlane-fork-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,24 @@ Ask the user which upstream source to track. Do not infer this from version file
- `release:<regex>` for matching release tags
- `branch:<ref>` for an upstream development branch

Resolve and show the source tag or branch and commit SHA. Before pushing or rewriting branches, show the complete plan and get confirmation. Include the source, base branch, sync branch, ordered patch refs, existing CI workflow name, and any force-pushes required.
Resolve and show the source tag or branch and commit SHA. Before pushing or rewriting branches, show the complete plan and get confirmation. Include the source, base branch, sync branch, ordered patch refs, existing CI workflow name, GitHub authentication approach, and any force-pushes required.

## Configure GitHub authentication

Treat working GitHub App authentication as a setup prerequisite. The built-in `GITHUB_TOKEN` is not sufficient: GitHub suppresses most workflow events caused by it, so its pushes do not reliably start integration CI or the promotion chain.

1. Inspect existing workflows and repository secret/variable names for an established GitHub App pattern. Ask whether the user wants to reuse that App or create one; do not silently select an identity.
2. Require the App installation to grant Contents read/write and Workflows write. Require Issues read/write only when GitHub issue notifications are enabled. Actions write is not required.
3. Use the standard repository variable `PATCHLANE_APP_CLIENT_ID` and repository secret `PATCHLANE_APP_PRIVATE_KEY`. An existing App identity can be reused through these names even when other workflows wrap it in a custom action.
4. App creation, permission approval, installation, and private-key generation require the user. Never ask them to paste a private key into chat or print it. After explicit approval, the agent may configure a variable and secret from a local key file with `gh variable set` and `gh secret set`.
5. Check deterministically where possible:
- `gh auth status`
- `gh api repos/OWNER/REPO/actions/permissions`
- `gh variable get PATCHLANE_APP_CLIENT_ID --repo OWNER/REPO`
- `gh secret list --repo OWNER/REPO --json name,updatedAt`
6. Explain that secret metadata proves only that a secret exists. The generated `actions/create-github-app-token` step verifies the installation and requested permissions at runtime.

Include all external repository changes in the plan and obtain confirmation before setting variables, secrets, or dispatching workflows.

## Configure the fork

Expand All @@ -27,7 +44,7 @@ Resolve and show the source tag or branch and commit SHA. Before pushing or rewr
3. Prefer the order `patch/sync`, `patch/ci`, then product-specific patches. Foundational changes must precede patches that depend on them.
4. Put `.patchlane.yml`, Patchlane workflows, and installed `.agents/skills` on `patch/sync`.
5. Put only the existing CI trigger adjustment on `patch/ci`. Preserve the existing workflow's `name`; configure `ciWorkflow` and the promotion workflow to reference that exact name. Add the CI filename and every other intentionally retained repository workflow to `allowedWorkflows`; Patchlane adds its generated sync and promotion workflows implicitly.
6. Use `npx patchlane init` to generate `.patchlane.yml` and pinned workflow files when practical, then adapt rather than replace existing repository conventions.
6. Use `npx patchlane init` to generate `.patchlane.yml` and pinned workflow files when practical, then adapt rather than replace existing repository conventions. Preserve the generated GitHub App token creation, explicit permission requests, authenticated checkout, and `GH_TOKEN` wiring.
7. Ensure fork CI covers normal pull requests plus pushes to both the generated base and sync branches.

Use the bundled assets as invariants when adapting workflows:
Expand Down Expand Up @@ -63,7 +80,9 @@ The workflows do not exist on the default branch before the first promotion. Boo
1. Run `npx patchlane bootstrap` to validate without publishing.
2. After user approval, run `npx patchlane bootstrap --publish` and wait for the configured CI workflow.
3. Promote the exact successful SHA printed by bootstrap, or use `npx patchlane bootstrap --wait` to wait and promote automatically.
4. Confirm the generated base is rooted at the selected source and that future workflows are active.
4. Confirm the generated base is rooted at the selected source.
5. After the workflows are present on the base branch, obtain approval and run `npx patchlane verify-auth`. This dispatches a no-push sync and waits for it, validating the uploaded App key, installation, permissions, API access, checkout, and rebuild without changing branches.
6. On the first App-authenticated published sync, confirm CI ran as a `push` for the exact integration SHA and promotion moved the base branch to that SHA.

After bootstrap, a remote no-push test can be dispatched safely from the default branch.

Expand All @@ -77,3 +96,4 @@ Summarize:
- files and workflows added or updated
- doctor and dry-run results
- bootstrap CI and promotion results
- GitHub App metadata and post-bootstrap authentication verification results
13 changes: 11 additions & 2 deletions skills/patchlane-fork-setup/assets/promote-tested-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
types: [completed]

permissions:
contents: write
contents: read

jobs:
promote:
Expand All @@ -15,10 +15,18 @@ jobs:
github.event.workflow_run.head_branch == 'sync/integration'
runs-on: ubuntu-latest
steps:
- name: Create Patchlane token
id: patchlane-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.PATCHLANE_APP_CLIENT_ID }}
private-key: ${{ secrets.PATCHLANE_APP_PRIVATE_KEY }}
permission-contents: write
permission-workflows: write
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ steps.patchlane-token.outputs.token }}

- uses: actions/setup-node@v4
with:
Expand All @@ -27,4 +35,5 @@ jobs:
- name: Run patchlane promote
run: npx patchlane@0.4.1 promote
env:
GH_TOKEN: ${{ steps.patchlane-token.outputs.token }}
EXPECTED_SYNC_SHA: ${{ github.event.workflow_run.head_sha }}
Loading