Skip to content

Commit f66e960

Browse files
committed
Refresh reusable previews after PR description edits
1 parent 5e92f5e commit f66e960

2 files changed

Lines changed: 92 additions & 13 deletions

File tree

.github/workflows/preview-publish.yml

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ name: WordPress Playground Preview - Publish (reusable)
33
# Reusable workflow that publishes a built artifact bundle from
44
# `preview-build.yml` to a public release and posts the Preview button.
55
#
6-
# MUST be invoked from a `workflow_run` trigger so it can run with
6+
# Normally invoked from a `workflow_run` trigger so it can run with
77
# `contents: write` + `pull-requests: write` even when the originating PR
8-
# came from a fork. Any other trigger is rejected at runtime.
8+
# came from a fork. It may also be invoked from `pull_request_target` `edited`
9+
# to refresh the already-published preview after PR description changes.
910

1011
on:
1112
workflow_call:
@@ -74,8 +75,12 @@ jobs:
7475
WORKFLOW_RUN_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
7576
run: |
7677
set -euo pipefail
78+
if [ "$EVENT_NAME" = "pull_request_target" ]; then
79+
echo "Refreshing the existing Playground preview for a PR description edit."
80+
exit 0
81+
fi
7782
if [ "$EVENT_NAME" != "workflow_run" ]; then
78-
echo "::error::preview-publish.yml must be invoked from a workflow_run trigger." >&2
83+
echo "::error::preview-publish.yml must be invoked from workflow_run, or pull_request_target for PR description edits." >&2
7984
exit 1
8085
fi
8186
if [ "$WORKFLOW_RUN_EVENT" != "pull_request" ]; then
@@ -126,6 +131,17 @@ jobs:
126131
SOURCE_RUN_ID: ${{ github.event.workflow_run.id }}
127132
with:
128133
script: |
134+
if (context.eventName === 'pull_request_target') {
135+
const pr = context.payload.pull_request;
136+
if (!pr) {
137+
throw new Error('pull_request_target refresh requires a pull_request payload.');
138+
}
139+
core.setOutput('pr-number', String(pr.number));
140+
core.setOutput('commit-sha', pr.head.sha);
141+
core.setOutput('refresh-only', 'true');
142+
return;
143+
}
144+
129145
const runId = Number(process.env.SOURCE_RUN_ID);
130146
const list = await github.rest.actions.listWorkflowRunArtifacts({
131147
owner: context.repo.owner,
@@ -165,9 +181,10 @@ jobs:
165181
core.setOutput('artifact-name', a.name);
166182
core.setOutput('pr-number', prNumber);
167183
core.setOutput('commit-sha', commitSha);
184+
core.setOutput('refresh-only', 'false');
168185
169186
- name: Download artifact bundle
170-
if: env.SKIP != '1'
187+
if: env.SKIP != '1' && steps.meta.outputs.refresh-only != 'true'
171188
shell: bash
172189
env:
173190
ARTIFACT_ID: ${{ steps.meta.outputs.artifact-id }}
@@ -180,7 +197,7 @@ jobs:
180197
ls -R bundle
181198
182199
- name: Ensure release exists
183-
if: env.SKIP != '1'
200+
if: env.SKIP != '1' && steps.meta.outputs.refresh-only != 'true'
184201
shell: bash
185202
env:
186203
RELEASE_TAG: ${{ inputs.release-tag }}
@@ -199,7 +216,7 @@ jobs:
199216
200217
- name: Upload zips and resolve URLs
201218
id: urls
202-
if: env.SKIP != '1'
219+
if: env.SKIP != '1' && steps.meta.outputs.refresh-only != 'true'
203220
shell: bash
204221
env:
205222
RELEASE_TAG: ${{ inputs.release-tag }}
@@ -230,6 +247,65 @@ jobs:
230247
echo "first-name=$(node -e 'const j=require(process.argv[1]);console.log(Object.keys(j)[0]||"")' "$mapping_file")" >> "$GITHUB_OUTPUT"
231248
echo "count=$(node -e 'console.log(Object.keys(require(process.argv[1])).length)' "$mapping_file")" >> "$GITHUB_OUTPUT"
232249
250+
- name: Resolve existing release asset URLs
251+
id: existing-urls
252+
if: env.SKIP != '1' && steps.meta.outputs.refresh-only == 'true'
253+
shell: bash
254+
env:
255+
RELEASE_TAG: ${{ inputs.release-tag }}
256+
PR_NUMBER: ${{ steps.meta.outputs.pr-number }}
257+
COMMIT_SHA: ${{ steps.meta.outputs.commit-sha }}
258+
GH_TOKEN: ${{ github.token }}
259+
run: |
260+
set -euo pipefail
261+
mapping_file="$RUNNER_TEMP/artifact-urls.json"
262+
echo '{}' > "$mapping_file"
263+
prefix="pr-${PR_NUMBER}-${COMMIT_SHA}-"
264+
suffix=".zip"
265+
gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --json assets \
266+
--jq '.assets[].name' > release-assets.txt || true
267+
while IFS= read -r asset; do
268+
case "$asset" in
269+
"$prefix"*"$suffix") ;;
270+
*) continue ;;
271+
esac
272+
name="${asset#"$prefix"}"
273+
name="${name%"$suffix"}"
274+
url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/${asset}"
275+
node -e '
276+
const fs = require("fs");
277+
const f = process.argv[1], k = process.argv[2], v = process.argv[3];
278+
const j = JSON.parse(fs.readFileSync(f, "utf8"));
279+
j[k] = v;
280+
fs.writeFileSync(f, JSON.stringify(j));
281+
' "$mapping_file" "$name" "$url"
282+
done < release-assets.txt
283+
count="$(node -e 'console.log(Object.keys(require(process.argv[1])).length)' "$mapping_file")"
284+
if [ "$count" -eq 0 ]; then
285+
echo "No release assets found for PR #${PR_NUMBER} at ${COMMIT_SHA}; skipping refresh."
286+
echo "SKIP=1" >> "$GITHUB_ENV"
287+
fi
288+
echo "mapping-file=$mapping_file" >> "$GITHUB_OUTPUT"
289+
echo "first-name=$(node -e 'const j=require(process.argv[1]);console.log(Object.keys(j)[0]||"")' "$mapping_file")" >> "$GITHUB_OUTPUT"
290+
echo "count=$count" >> "$GITHUB_OUTPUT"
291+
292+
- name: Select artifact URL mapping
293+
id: url-meta
294+
if: env.SKIP != '1'
295+
shell: bash
296+
env:
297+
UPLOAD_MAPPING_FILE: ${{ steps.urls.outputs.mapping-file }}
298+
UPLOAD_FIRST_NAME: ${{ steps.urls.outputs.first-name }}
299+
UPLOAD_COUNT: ${{ steps.urls.outputs.count }}
300+
EXISTING_MAPPING_FILE: ${{ steps.existing-urls.outputs.mapping-file }}
301+
EXISTING_FIRST_NAME: ${{ steps.existing-urls.outputs.first-name }}
302+
EXISTING_COUNT: ${{ steps.existing-urls.outputs.count }}
303+
run: |
304+
set -euo pipefail
305+
echo "mapping-file=${UPLOAD_MAPPING_FILE:-$EXISTING_MAPPING_FILE}" >> "$GITHUB_OUTPUT"
306+
echo "first-name=${UPLOAD_FIRST_NAME:-$EXISTING_FIRST_NAME}" >> "$GITHUB_OUTPUT"
307+
echo "count=${UPLOAD_COUNT:-$EXISTING_COUNT}" >> "$GITHUB_OUTPUT"
308+
233309
- name: Render blueprint
234310
id: blueprint
235311
if: env.SKIP != '1'
@@ -238,9 +314,9 @@ jobs:
238314
BLUEPRINT_INPUT: ${{ inputs.blueprint }}
239315
KIND: ${{ inputs.kind }}
240316
FROM_ARTIFACT: ${{ inputs.blueprint-from-artifact }}
241-
MAPPING_FILE: ${{ steps.urls.outputs.mapping-file }}
242-
FIRST_NAME: ${{ steps.urls.outputs.first-name }}
243-
COUNT: ${{ steps.urls.outputs.count }}
317+
MAPPING_FILE: ${{ steps.url-meta.outputs.mapping-file }}
318+
FIRST_NAME: ${{ steps.url-meta.outputs.first-name }}
319+
COUNT: ${{ steps.url-meta.outputs.count }}
244320
run: |
245321
set -euo pipefail
246322
node <<'NODE' >> "$GITHUB_OUTPUT"
@@ -295,7 +371,7 @@ jobs:
295371
NODE
296372
297373
- name: Cleanup old artifacts
298-
if: env.SKIP != '1'
374+
if: env.SKIP != '1' && steps.meta.outputs.refresh-only != 'true'
299375
shell: bash
300376
env:
301377
RELEASE_TAG: ${{ inputs.release-tag }}

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ on:
125125
workflow_run:
126126
workflows: ["PR Preview - Build"]
127127
types: [completed]
128+
pull_request_target:
129+
types: [edited]
128130
129131
permissions:
130132
contents: write
@@ -144,7 +146,7 @@ jobs:
144146

145147
You do not need to create `secrets.GITHUB_TOKEN`; GitHub provides it automatically to each workflow run.
146148

147-
Open a pull request. The build workflow runs `npm ci && npm run build:plugin-zip`. After that succeeds, the publish workflow uploads the resulting ZIP to a public release URL and posts the Preview button. When someone clicks it, Playground installs and activates the built plugin.
149+
Open a pull request. The build workflow runs `npm ci && npm run build:plugin-zip`. After that succeeds, the publish workflow uploads the resulting ZIP to a public release URL and posts the Preview button. When someone clicks it, Playground installs and activates the built plugin. If the publish workflow also listens to `pull_request_target: edited`, editing the PR description refreshes the existing preview button/comment against the latest published artifact for the current PR head.
148150

149151
Expected result:
150152

@@ -579,8 +581,9 @@ splits the work at the artifact boundary:
579581
The publish workflow has a runtime guard that **fails loudly** if invoked from
580582
any trigger other than `workflow_run`. Non-PR source runs and failed build runs
581583
skip intentionally because there is no successful PR preview to publish.
582-
Misconfigured callers (for example someone reaches for `pull_request_target`)
583-
get a red failure instead of a silent skip.
584+
Misconfigured callers get a red failure instead of a silent skip. The one
585+
exception is `pull_request_target` for PR description edits, which may refresh
586+
an already-published preview without rebuilding or reading PR code.
584587

585588
Because the publish workflow is privileged, its third-party action references
586589
are pinned to commit SHAs. This avoids granting write permissions to a moved

0 commit comments

Comments
 (0)