Skip to content

Commit 33c57d7

Browse files
committed
ci: 👷 Add workflow to automatically open upstream PRs on merge
1 parent 10d7953 commit 33c57d7

1 file changed

Lines changed: 141 additions & 0 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Automatically opens a PR to the upstream repository material-components/material-web
2+
# when a PR is merged into main-community in this fork.
3+
# Requirements: (recommended) secret UPSTREAM_TOKEN with public_repo (or repo) scope.
4+
# The triggering PR's head branch MUST be based off 'main' (not another feature branch) or it will be skipped.
5+
6+
name: Mirror merged PR upstream
7+
8+
on:
9+
pull_request:
10+
types: [closed]
11+
12+
concurrency:
13+
group: mirror-upstream-${{ github.event.pull_request.number }}
14+
cancel-in-progress: false
15+
16+
permissions:
17+
contents: write
18+
pull-requests: write
19+
20+
jobs:
21+
create-upstream-pr:
22+
if: >-
23+
${{ github.event.pull_request.merged == true &&
24+
github.event.pull_request.base.ref == 'main-community' &&
25+
github.event.pull_request.head.repo.full_name == github.repository &&
26+
github.repository != 'material-components/material-web' }}
27+
runs-on: ubuntu-latest
28+
env:
29+
UPSTREAM_REPO: material-components/material-web
30+
SKIP_LABEL: no-upstream
31+
BASE_TARGET: main
32+
REQUIRED_ANCESTOR: main
33+
# Fallback: if UPSTREAM_TOKEN is set and not empty use it, otherwise default to GITHUB_TOKEN (may lack upstream permissions)
34+
TOKEN: ${{ secrets.UPSTREAM_TOKEN != '' && secrets.UPSTREAM_TOKEN || secrets.GITHUB_TOKEN }}
35+
steps:
36+
- name: Check skip label
37+
id: check_label
38+
run: |
39+
labels='${{ toJson(github.event.pull_request.labels) }}'
40+
echo "Labels: $labels"
41+
if echo "$labels" | grep -q '"name":"'"$SKIP_LABEL"'"'; then
42+
echo "skip=true" >> $GITHUB_OUTPUT
43+
else
44+
echo "skip=false" >> $GITHUB_OUTPUT
45+
fi
46+
- name: Stop if skip label present
47+
if: steps.check_label.outputs.skip == 'true'
48+
run: echo "Skip label present, abort upstream mirroring" && exit 0
49+
50+
- name: Checkout repository
51+
uses: actions/checkout@v4
52+
with:
53+
fetch-depth: 0
54+
55+
- name: Set branch variables
56+
id: vars
57+
run: |
58+
echo "branch=${{ github.event.pull_request.head.ref }}" >> $GITHUB_OUTPUT
59+
echo "head_sha=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
60+
echo "author_login=${{ github.event.pull_request.user.login }}" >> $GITHUB_OUTPUT
61+
echo "repo_full=${{ github.repository }}" >> $GITHUB_OUTPUT
62+
63+
- name: Verify branch is based on 'main'
64+
id: verify_base
65+
run: |
66+
BRANCH='${{ steps.vars.outputs.branch }}'
67+
echo "Verifying branch $BRANCH is based on REQUIRED_ANCESTOR=${REQUIRED_ANCESTOR}"
68+
git fetch origin ${REQUIRED_ANCESTOR} --quiet
69+
git fetch origin $BRANCH --quiet || true
70+
# Ensure required ancestor is an ancestor of head SHA
71+
if git merge-base --is-ancestor origin/${REQUIRED_ANCESTOR} ${{ steps.vars.outputs.head_sha }}; then
72+
echo "Base verification passed"
73+
echo "ok=true" >> $GITHUB_OUTPUT
74+
else
75+
echo "Branch is NOT based on origin/${REQUIRED_ANCESTOR}; skipping upstream PR";
76+
echo "ok=false" >> $GITHUB_OUTPUT
77+
fi
78+
- name: Stop if not based on main
79+
if: steps.verify_base.outputs.ok != 'true'
80+
run: echo "Branch not based on 'main'. Aborting." && exit 0
81+
82+
- name: Create (or skip) upstream PR
83+
id: create_upstream_pr
84+
env:
85+
BRANCH: ${{ steps.vars.outputs.branch }}
86+
PR_TITLE: ${{ github.event.pull_request.title }}
87+
PR_BODY: ${{ github.event.pull_request.body }}
88+
PR_NUMBER: ${{ github.event.pull_request.number }}
89+
REPO_OWNER: ${{ github.repository_owner }}
90+
REPO_FULL: ${{ steps.vars.outputs.repo_full }}
91+
AUTHOR: ${{ steps.vars.outputs.author_login }}
92+
UPSTREAM_REPO: ${{ env.UPSTREAM_REPO }}
93+
BASE_TARGET: ${{ env.BASE_TARGET }}
94+
TOKEN: ${{ env.TOKEN }}
95+
run: |
96+
set -euo pipefail
97+
echo "Checking for existing open PR to $UPSTREAM_REPO with head $REPO_OWNER:$BRANCH"
98+
99+
export GH_TOKEN="$TOKEN"
100+
existing=$(gh pr list -R "$UPSTREAM_REPO" --state open --head "$REPO_OWNER:$BRANCH" --json number --jq '.[0].number')
101+
if [ -n "$existing" ]; then
102+
echo "An upstream PR already exists (#$existing). Exiting."; exit 0; fi
103+
104+
body_content=${PR_BODY:-"(No description provided in the source PR)"}
105+
body_content+="\n\n(Automated upstream mirror of source PR ${REPO_FULL}#${PR_NUMBER} by @${AUTHOR})"
106+
printf '%s\n' "$body_content" > body.md
107+
108+
echo "Creating PR on $UPSTREAM_REPO: base=$BASE_TARGET head=$REPO_OWNER:$BRANCH"
109+
gh pr create -R "$UPSTREAM_REPO" \
110+
--title "$PR_TITLE" \
111+
--body-file body.md \
112+
--base "$BASE_TARGET" \
113+
--head "$REPO_OWNER:$BRANCH"
114+
115+
new_number=$(gh pr list -R "$UPSTREAM_REPO" --state open --head "$REPO_OWNER:$BRANCH" --json number --jq '.[0].number') || true
116+
if [ -n "${new_number}" ]; then
117+
upstream_url=$(gh pr view -R "$UPSTREAM_REPO" "${new_number}" --json url --jq '.url') || upstream_url=""
118+
echo "Upstream PR created successfully (#${new_number}).";
119+
echo "new_number=${new_number}" >> $GITHUB_OUTPUT
120+
if [ -n "$upstream_url" ]; then
121+
echo "upstream_url=${upstream_url}" >> $GITHUB_OUTPUT
122+
fi
123+
else
124+
echo "Upstream PR creation command finished but PR number could not be determined.";
125+
fi
126+
127+
- name: Comment back on source PR with upstream link
128+
if: ${{ steps.create_upstream_pr.outputs.new_number != '' }}
129+
env:
130+
GH_TOKEN: ${{ env.TOKEN }}
131+
PR_NUMBER: ${{ github.event.pull_request.number }}
132+
UPSTREAM_REPO: ${{ env.UPSTREAM_REPO }}
133+
UPSTREAM_PR_NUMBER: ${{ steps.create_upstream_pr.outputs.new_number }}
134+
UPSTREAM_URL: ${{ steps.create_upstream_pr.outputs.upstream_url }}
135+
run: |
136+
set -euo pipefail
137+
url_display=${UPSTREAM_URL:-"https://github.com/${UPSTREAM_REPO}/pull/${UPSTREAM_PR_NUMBER}"}
138+
comment="Upstream PR opened: ${UPSTREAM_REPO}#${UPSTREAM_PR_NUMBER}\n${url_display}\n\n(This comment was added automatically)"
139+
echo "Posting comment to source PR #${PR_NUMBER}:"; echo "$comment"
140+
gh pr comment "$PR_NUMBER" --body "$comment"
141+

0 commit comments

Comments
 (0)