Skip to content

Remove pull_request_target by consolidating stale preview cleanup - #2876

Open
gcl-sekoia wants to merge 1 commit into
mainfrom
consolidate-stale-preview-cleanup
Open

Remove pull_request_target by consolidating stale preview cleanup#2876
gcl-sekoia wants to merge 1 commit into
mainfrom
consolidate-stale-preview-cleanup

Conversation

@gcl-sekoia

@gcl-sekoia gcl-sekoia commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Delete delete-preview-for-stale-prs.yaml which used pull_request_target to react to the stale label being added
  • Extend mark-as-stale.yaml to handle preview deletion directly: the staled-issues-prs output from actions/stale feeds a matrix job that calls the existing delete-preview composite action for each newly staled PR

pull_request_target now appears in zero workflows in this repo.

This is not the drop-in replacement it looks like

An earlier version of this description claimed the change was behaviour-preserving. That was wrong, and the correction matters more than the change itself.

The old workflow could never react to the stale bot. The label is applied with secrets.GITHUB_TOKEN, and token-generated events do not start new workflow runs. Across all 175 historical runs of that workflow, github-actions[bot] accounts for zero:

actor runs conclusions
dependabot[bot] 106 all skipped
otetard 31 28 success, 3 failure
squioc / clement-burtscher-sekoia / others 38 all skipped
github-actions[bot] 0

All 28 successes were a single manual bulk-labelling session on 2026-01-21. Every run since 2026-02-01 was skipped, while the stale bot went on labelling PRs throughout.

So scheduled stale-preview cleanup has never actually worked. This PR does not preserve that behaviour — it makes it work for the first time. The new path is therefore untested in production and will fire for real on the first 0 3 * * * run after merge; that run is worth watching.

Two consequences to decide on separately:

  • Manual labelling no longer deletes a preview. Hand-applying stale was the only path that ever worked (28 times). If it's worth keeping, a workflow_dispatch input taking a PR number is the cheap route that doesn't reintroduce pull_request_target.
  • 36 PRs are already labelled stale. They won't be re-staled, so _markStale never fires for them and their previews leak indefinitely. Needs a one-off swift delete sweep, out of scope here.

Other changes

  • fail-fast: false on the matrix: a PR is only ever staled once, so a failed deletion is never retried — one failure must not cancel the other previews
  • The extracted PR list defaults to [], so an unparsable staled-issues-prs logs a warning instead of failing a run that cannot be retried. Note jq on empty input exits 0 with no output, so the real hazard was fromJson('') raising an expression error in the job-level if:, not a jq crash
  • persist-credentials: false and a .github/actions/ sparse checkout, matching delete-preview.yaml
  • The blob is passed via env: and quoted rather than interpolated into the run body — it embeds attacker-controlled PR titles, and this job holds the OVH Swift credentials
  • Job/workflow names updated to reflect that this workflow now does two things

Known limitations (accepted, not fixed here)

  • The "preview published" comment is not replaced, only followed. delete-preview's find-comment step matches on body-includes: Newest code from ${{ github.actor }}, but the original comment is written by upload-preview.yaml where github.actor is the PR author, whereas in a schedule run it is always one fixed account. The match fails, create-or-update-comment falls through to its create branch, and the original comment survives still linking a deleted prefix. Pre-existing and shared with the merge path, but newly visible now that this path runs.
  • staled-issues-prs is far larger than it looks. TypeScript private is compile-time only, so each element embeds the whole ~60-key options object (including repoToken, masked by the runner). At roughly 2–3 KB per item, a single env var hits MAX_ARG_STRLEN somewhere around 50 staled PRs. Not reachable in steady state; would be reachable if the stale label were bulk-removed from the current 36. Never add a step that echoes $STALED or sets set -x.

Test plan

  • Watch the first scheduled run that labels at least one PR: delete-stale-previews must expand to exactly those PR numbers, and the preview URL must 404 afterwards
  • Verify preview deletion still works on the close/merge path (preview-cleanup.yamldelete-preview.yaml, unchanged)

workflow_dispatch with debug-only: true is not a safe dry run: _markStale populates staled-issues-prs before the debugOnly check, so the matrix job would still delete real previews.

@gcl-sekoia
gcl-sekoia marked this pull request as ready for review March 19, 2026 12:28
@github-actions

github-actions Bot commented Mar 19, 2026

Copy link
Copy Markdown

Newest code from gcl-sekoia has been published to preview environment

🚀 Latest deployment was built on 2026-08-07 19:00:21 (4502140383512c379a9d3b354a4267b2474f3cab).

@github-actions

Copy link
Copy Markdown

This PR was marked as stale because it has been open for 30 days with no activity.

@gcl-sekoia
gcl-sekoia force-pushed the consolidate-stale-preview-cleanup branch from b4af4b6 to e634f09 Compare August 7, 2026 11:41
@gcl-sekoia
gcl-sekoia requested a lite review from Copilot August 7, 2026 11:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the pull_request_target-based stale preview cleanup workflow by consolidating stale-preview deletion into the scheduled mark-as-stale workflow, reducing the pull_request_target attack surface while keeping equivalent behavior.

Changes:

  • Deleted .github/workflows/delete-preview-for-stale-prs.yaml (previously triggered on pull_request_target + stale label).
  • Extended .github/workflows/mark-as-stale.yaml to extract newly-staled PR numbers from actions/stale and run a matrix job to delete previews via the existing ./.github/actions/delete-preview composite action.
  • Configured the matrix to be non-fail-fast and adjusted checkout options for the deletion job.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
.github/workflows/mark-as-stale.yaml Adds extraction of newly-staled PRs and a matrix-driven delete-stale-previews job to delete previews without pull_request_target.
.github/workflows/delete-preview-for-stale-prs.yaml Removes the old pull_request_target workflow that deleted previews when the stale label was applied.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/mark-as-stale.yaml Outdated
Comment on lines +39 to +41
run: |
prs=$(echo "$STALED" | jq -c '[.[] | select(.pull_request != null) | .number]')
echo "prs=$prs" >> "$GITHUB_OUTPUT"
…anup

Replace the dedicated delete-preview-for-stale-prs workflow (which used
pull_request_target) by extending mark-as-stale to delete previews for
newly staled PRs directly, using the staled-issues-prs output from
actions/stale.

This is not the drop-in replacement it looks like. The old workflow
could never react to the stale bot: the label was applied with
secrets.GITHUB_TOKEN, and token-generated events do not start new
workflow runs. Across 175 historical runs it only ever fired for humans
labelling by hand -- github-actions[bot] accounts for zero. Scheduled
preview cleanup has therefore never worked. Driving it from the action's
own output makes it work for the first time, and removes the
pull_request_target attack surface.

Two consequences worth recording: hand-applying the stale label no
longer deletes a preview, and the previews of PRs already labelled stale
need a one-off sweep since they will not be re-staled.

Also set fail-fast: false so one failed deletion does not cancel the
others, and default the extracted list to [] so an unparsable
staled-issues-prs warns instead of failing a run that is never retried.
@gcl-sekoia
gcl-sekoia force-pushed the consolidate-stale-preview-cleanup branch from e634f09 to d0818eb Compare August 7, 2026 18:52
@github-actions github-actions Bot removed the stale label Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants