@@ -86,13 +86,12 @@ jobs:
8686 path : ${{ runner.temp }}/generated-docs.patch
8787 if-no-files-found : error
8888
89- push :
89+ create-sync-pr :
9090 needs : generate
91+ if : needs.generate.outputs.has_changes == 'true'
9192 runs-on : ubuntu-latest
9293 permissions :
93- contents : write
94- outputs :
95- final_sha : ${{ steps.record_sha.outputs.final_sha }}
94+ contents : read
9695 steps :
9796 - uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
9897 with :
@@ -105,15 +104,21 @@ jobs:
105104 with :
106105 name : generated-docs-patch
107106 path : ${{ runner.temp }}
108- # Use GITHUB_TOKEN intentionally so this sync commit does not fan out into
109- # another push-triggered workflow run. Docs deployment is chained below.
110- - name : Commit generated docs
111- if : needs.generate.outputs.has_changes == 'true'
107+ # Use a narrowly-scoped GitHub App token so branch/PR events run normal
108+ # checks without granting the workflow permission to bypass main rules.
109+ - name : Create GitHub App token
110+ id : app-token
111+ uses : actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
112+ with :
113+ app-id : ${{ secrets.GENERATED_DOCS_APP_ID }}
114+ private-key : ${{ secrets.GENERATED_DOCS_APP_PRIVATE_KEY }}
115+ - name : Create or update generated docs PR
112116 env :
113- GH_TOKEN : ${{ github .token }}
117+ GH_TOKEN : ${{ steps.app-token.outputs .token }}
114118 TARGET_REPO : ${{ github.repository }}
115119 TARGET_REF : ${{ github.ref_name }}
116120 PATCH_PATH : ${{ runner.temp }}/generated-docs.patch
121+ SYNC_BRANCH : docs/generated-docs-sync
117122 run : |
118123 set -euo pipefail
119124 git apply --index --whitespace=nowarn "$PATCH_PATH"
@@ -123,25 +128,55 @@ jobs:
123128 git config user.name "github-actions[bot]"
124129 git config user.email "github-actions[bot]@users.noreply.github.com"
125130 git commit -m "docs: sync generated docs"
126- git fetch origin "${TARGET_REF}"
127- git rebase "origin/${TARGET_REF}"
128- git push "https://x-access-token:${GH_TOKEN}@github.com/${TARGET_REPO}.git" "HEAD:${TARGET_REF}"
129- - name : Record final revision
130- id : record_sha
131- env :
132- DEFAULT_SHA : ${{ github.sha }}
133- run : |
134- if [ "${{ needs.generate.outputs.has_changes }}" = "true" ]; then
135- echo "final_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
131+
132+ remote_url="https://x-access-token:${GH_TOKEN}@github.com/${TARGET_REPO}.git"
133+ remote_sha="$(git ls-remote --heads "$remote_url" "$SYNC_BRANCH" | awk '{print $1}')"
134+ if [ -n "$remote_sha" ]; then
135+ git push --force-with-lease="refs/heads/${SYNC_BRANCH}:${remote_sha}" \
136+ "$remote_url" \
137+ "HEAD:refs/heads/${SYNC_BRANCH}"
138+ else
139+ git push "$remote_url" "HEAD:refs/heads/${SYNC_BRANCH}"
140+ fi
141+
142+ pr_body="$(mktemp)"
143+ cat > "$pr_body" <<EOF
144+ Generated docs are out of sync with main.
145+
146+ This PR was created automatically after a push to ${TARGET_REF} at ${GITHUB_SHA}.
147+ EOF
148+
149+ target_owner="${TARGET_REPO%%/*}"
150+ existing_pr="$(
151+ gh pr list \
152+ --repo "$TARGET_REPO" \
153+ --base "$TARGET_REF" \
154+ --head "${target_owner}:${SYNC_BRANCH}" \
155+ --state open \
156+ --json number \
157+ --jq '.[0].number // empty'
158+ )"
159+
160+ if [ -n "$existing_pr" ]; then
161+ gh pr edit "$existing_pr" \
162+ --repo "$TARGET_REPO" \
163+ --title "Sync generated docs" \
164+ --body-file "$pr_body"
136165 else
137- echo "final_sha=${DEFAULT_SHA}" >> "$GITHUB_OUTPUT"
166+ gh pr create \
167+ --repo "$TARGET_REPO" \
168+ --base "$TARGET_REF" \
169+ --head "${target_owner}:${SYNC_BRANCH}" \
170+ --title "Sync generated docs" \
171+ --body-file "$pr_body"
138172 fi
139173
140174 deploy-docs :
141- needs : push
175+ needs : [generate, create-sync-pr]
176+ if : always() && needs.generate.result == 'success' && needs.generate.outputs.has_changes == 'false'
142177 uses : ./.github/workflows/docs-deploy.yaml
143178 with :
144- checkout_ref : ${{ needs.push.outputs.final_sha }}
179+ checkout_ref : ${{ github.sha }}
145180 deploy_branch : dev
146181 secrets :
147182 CLOUDFLARE_API_TOKEN : ${{ secrets.CLOUDFLARE_API_TOKEN }}
0 commit comments