Skip to content

Commit 8a2f38c

Browse files
committed
ci: add upstream sync workflow
1 parent e59e20a commit 8a2f38c

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
# Opens a PR that brings changes from the upstream metaregistrar/php-epp-client
3+
# into this fork, keeping the local Exonet patches. Merge the PR manually with
4+
# "Squash and merge" to land the upstream changes as a single *Verified* commit
5+
# (GitHub web-flow signature) on master.
6+
name: Upstream sync
7+
8+
on:
9+
schedule:
10+
- cron: '0 6 * * 1' # Mondays 06:00 UTC
11+
workflow_dispatch:
12+
13+
permissions:
14+
contents: read
15+
16+
env:
17+
UPSTREAM: metaregistrar/php-epp-client
18+
UPSTREAM_BRANCH: master
19+
20+
jobs:
21+
upstream-sync:
22+
name: Open upstream sync PR
23+
runs-on: ubuntu-latest
24+
timeout-minutes: 10
25+
26+
steps:
27+
# App token so the branch/PR are created as the app (and the PR triggers
28+
# CI). Needs the app installed on this repo with contents: write +
29+
# pull-requests: write.
30+
- name: Generate app token
31+
id: app-token
32+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
33+
with:
34+
client-id: ${{ secrets.RELEASE_APP_CLIENT_ID }}
35+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
36+
permission-contents: write
37+
permission-pull-requests: write
38+
39+
- name: Checkout fork
40+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
41+
with:
42+
token: ${{ steps.app-token.outputs.token }}
43+
fetch-depth: 0
44+
45+
- name: Open an upstream sync PR
46+
env:
47+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
48+
run: |
49+
set -euo pipefail
50+
51+
git remote add upstream "https://github.com/${UPSTREAM}.git"
52+
git fetch --no-tags --quiet upstream "${UPSTREAM_BRANCH}"
53+
54+
upstream_sha="$(git rev-parse --short "upstream/${UPSTREAM_BRANCH}")"
55+
branch="upstream-sync/${upstream_sha}"
56+
57+
# Dedupe on the upstream SHA: a squash-merge doesn't make upstream an
58+
# ancestor of master, so we can't detect "already synced" via ancestry.
59+
# Skip if a PR for this exact upstream commit already exists (any state).
60+
# -R pins THIS fork, since gh resolves fork context to the upstream parent.
61+
if [ -n "$(gh pr list -R "${GITHUB_REPOSITORY}" --head "${branch}" --state all --json number --jq '.[].number' | head -n1)" ]; then
62+
echo "Already synced/attempted ${UPSTREAM}@${upstream_sha}; nothing to do."
63+
exit 0
64+
fi
65+
66+
# Mirror upstream onto a feature branch. Its commits stay unsigned there
67+
# (feature branches have no signing requirement); a "Squash and merge"
68+
# produces the single signed commit that lands on master.
69+
git push --force origin "upstream/${UPSTREAM_BRANCH}:refs/heads/${branch}"
70+
71+
body="Automated sync of upstream [\`${UPSTREAM}\`](https://github.com/${UPSTREAM}) into this fork. Merge with **Squash and merge** to land the upstream changes as a single Verified commit on \`master\`; the local Exonet patches are preserved. Resolve any conflicts with the patches first. Upstream: ${UPSTREAM}@${upstream_sha}."
72+
73+
# Create the PR in THIS fork. `gh pr create` defaults the base repo to the
74+
# upstream parent for forks (which the app token can't touch -> "Resource
75+
# not accessible by integration"), so hit the fork's pulls API directly.
76+
url="$(gh api --method POST "repos/${GITHUB_REPOSITORY}/pulls" \
77+
-f "title=Sync with upstream ${UPSTREAM}@${upstream_sha}" \
78+
-f "head=${branch}" \
79+
-f "base=master" \
80+
-f "body=${body}" \
81+
--jq '.html_url')"
82+
echo "Opened ${url} — review and merge it manually with \"Squash and merge\"."

0 commit comments

Comments
 (0)