Skip to content
Draft
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
36 changes: 36 additions & 0 deletions .github/workflows/comment-upstream-patches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Comment upstream quick patches

on:
pull_request:
branches: [main]
types: [closed]

permissions:
contents: read

jobs:
comment:
if: ${{ github.event.pull_request.merged == true }}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out
uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: oven-sh/setup-bun@v2

- name: Fetch comparison commits
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.merge_commit_sha }}
run: |
git fetch origin "$BASE_SHA" "$HEAD_SHA"

- name: Comment on upstream Capacitor PRs
env:
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.merge_commit_sha }}
run: bun run comment:upstream-patches -- --base "$BASE_SHA" --head "$HEAD_SHA"
105 changes: 105 additions & 0 deletions .github/workflows/sync-upstream-patches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Sync upstream Capacitor patches

on:
workflow_dispatch:
inputs:
require_checks:
description: Only generate patches from Capacitor+ branches with passing checks
required: true
type: boolean
default: true
refresh_existing:
description: Regenerate patch files for catalog entries that already exist
required: true
type: boolean
default: false
max_build_prs:
description: Maximum PR branches that may run compiled artifact builds in one sync
required: true
default: '3'
schedule:
- cron: '17 */6 * * *'

permissions:
contents: write
pull-requests: write

concurrency:
group: sync-upstream-capacitor-patches
cancel-in-progress: false

jobs:
sync:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Check out capacitor-patch
uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: bun i

- name: Check out Capacitor+
uses: actions/checkout@v6
with:
repository: Cap-go/capacitor-plus
ref: plus
path: .tmp/capacitor-plus
fetch-depth: 0
token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }}

- name: Fetch upstream sync branches
run: |
git -C .tmp/capacitor-plus fetch origin \
'+refs/heads/plus:refs/remotes/origin/plus' \
'+refs/heads/sync/upstream-pr-*:refs/remotes/origin/sync/upstream-pr-*'

- name: Generate patch catalog updates
env:
GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }}
REQUIRE_CHECKS: ${{ github.event.inputs.require_checks || 'true' }}
REFRESH_EXISTING: ${{ github.event.inputs.refresh_existing || 'false' }}
MAX_BUILD_PRS: ${{ github.event.inputs.max_build_prs || '3' }}
run: |
args=(
--capacitor-plus-dir .tmp/capacitor-plus
--remote origin
--base-ref origin/plus
--max-build-prs "$MAX_BUILD_PRS"
)

if [[ "$REQUIRE_CHECKS" == "true" ]]; then
args+=(--require-checks)
fi

if [[ "$REFRESH_EXISTING" == "true" ]]; then
args+=(--refresh-existing)
fi

bun run sync:patches -- "${args[@]}"

- name: Format generated catalog
run: bun run prettier -- --write patches/catalog.json

- name: Verify
run: bun run verify

- name: Open patch sync pull request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }}
branch: chore/sync-upstream-capacitor-patches
delete-branch: true
commit-message: 'chore: sync upstream Capacitor patches'
title: 'chore: sync upstream Capacitor patches'
body: |
Generated by the recurring Capacitor+ upstream patch sync.

This PR adds or refreshes patch files and catalog entries from external `ionic-team/capacitor` PRs mirrored by `Cap-go/capacitor-plus` `sync/upstream-pr-*` branches.
labels: |
automated
patches
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# node files
dist
node_modules
.tmp

# iOS files
Pods
Expand Down
44 changes: 34 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,20 +143,44 @@ The bundled catalog tracks external fix PRs mirrored by Capacitor+ auto-sync bra

Run `capgo-capacitor-patch list --all` to see the shipped catalog. Each entry includes the original upstream Capacitor PR URL, the Capacitor+ sync branch, target package, supported version range, and patch file.

## Future Automation
## Recurring Patch Automation

The long-term goal is to make this repository the fast path for Capacitor fixes that are waiting upstream.
This repository is the fast path for Capacitor fixes that are waiting upstream.

For every external PR opened against `ionic-team/capacitor`, the automation should:
The `Sync upstream Capacitor patches` workflow runs every 6 hours and can also be started manually from GitHub Actions. It:

1. Detect whether the PR is a fix that changes shipped Capacitor code.
2. Wait until the upstream PR, or the matching Capacitor+ `sync/upstream-pr-*` branch, passes its test suite.
3. Generate package-ready patch files in this repository.
4. Open or update a pull request here with the new `patches/catalog.json` entries and patch files.
5. Run this repository's tests against supported Capacitor versions.
6. Comment on the original upstream PR with the quick-patch ID and install snippet once the patch package PR is ready.
1. Checks out this repository and `Cap-go/capacitor-plus`.
2. Fetches Capacitor+ `sync/upstream-pr-*` branches.
3. Reads the matching `ionic-team/capacitor` PR metadata.
4. Skips PRs from Capacitor team members and collaborators.
5. Skips branches whose Capacitor+ checks are not passing.
6. Generates package-ready patch files and `patches/catalog.json` entries.
7. Runs this repository's verification.
8. Opens or updates a pull request with the generated changes.

The upstream PR comment should only be posted when the patch applies cleanly and this repository's checks pass. A good comment looks like:
The generator handles direct Android and iOS package source changes. It can also build package artifacts for `@capacitor/core`, `@capacitor/cli`, and native bridge asset patches when an upstream PR changes TypeScript source that users do not receive directly in `node_modules`.

Manual run:

```bash
bun run sync:patches -- \
--capacitor-plus-dir ../capacitor-plus \
--remote capgo \
--base-ref capgo/plus \
--require-checks
```

Useful options:

- `--pr <number>` only processes a specific upstream PR branch.
- `--refresh-existing` regenerates patches for entries that already exist.
- `--no-require-checks` allows local dry-runs before Capacitor+ CI finishes.
- `--max-build-prs <count>` limits expensive compiled artifact generation.
- `--dry-run` reports what would be generated without writing files.

After a generated patch PR is merged, the `Comment upstream quick patches` workflow comments on the original upstream Capacitor PR when `PERSONAL_ACCESS_TOKEN` is configured with permission to comment there.

The upstream PR comment is only posted after the patch entry lands in this repository. A good comment looks like:

````md
This fix is available as a quick patch through `@capgo/capacitor-patch`.
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"verify": "bun run verify:web",
"verify:web": "bun run build && bun run test:patch",
"test": "bun run test:patch",
"test:patch": "node --test scripts/test-capacitor-patch.mjs",
"test:patch": "node --test scripts/test-capacitor-patch.mjs scripts/test-upstream-sync.mjs",
"sync:patches": "node scripts/sync-upstream-patches.mjs",
"comment:upstream-patches": "node scripts/comment-upstream-patches.mjs",
"lint": "bun run eslint && bun run prettier -- --check",
"fmt": "bun run eslint -- --fix && bun run prettier -- --write",
"eslint": "eslint . --ext .ts",
Expand Down
Loading