You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@magpie.apache.org by "potiuk (via GitHub)" <gi...@apache.org> on 2026/09/16 15:15:02 UTC
[PR] feat: per-PR preview deployments at magpie-pr.staged.apache.org [magpie-site]
potiuk opened a new pull request, #178:
URL: https://github.com/apache/magpie-site/pull/178
Implements the design in #176. A maintainer comments `/show-preview` on a pull
request; a scheduled workflow publishes that PR's built site at
`https://magpie-pr<N>.staged.apache.org/`, keeps it tracking the PR's head
commit, and retires it when the PR closes.
**Please read #176 first** — it records what was probed against real ASF
infrastructure before any of this was written, and several decisions here only
make sense against those findings.
## How it works
**`build.yml`** (unprivileged, runs on `pull_request`, executes the PR's own
code) builds the site and uploads it as an artifact with metadata naming the PR
and its head SHA.
**`preview-publish.yml`** (privileged, `schedule` every 15 min +
`workflow_dispatch`, holds a token that can push branches and comment) never
checks out, builds or executes pull-request code. It consumes that artifact,
validates it, and force-pushes static files to `preview/pr<N>-staging`, whose
generated `.asf.yaml` makes infra serve it.
**`pull_request_target` appears nowhere.** That is the constraint the whole
shape follows from.
## Three findings that shaped the implementation
- **Deleting a preview branch does not unstage the site.** Verified directly:
the host still served HTTP 200 ten minutes after the branch was deleted. So
teardown force-pushes a tombstone *first* and deletes the branch on a later
run. "Delete the preview" is not implementable as a branch deletion.
- **A pull request can ship a `.git/` directory inside its own build artifact.**
`git init` reinitialises an existing one, preserving its hooks, and the commit
would then execute them in the job holding the ASF push token. Neither the
entry-name screen nor the symlink walk catches it — the name is clean and it
is not a symlink. Closed with three independent layers.
- **An archive entry that escapes the extraction root lands outside the tree**,
where no walk of that tree can ever see it. Screening entry names *before*
extraction is the only place that defence can live.
## Security model
- The privileged job consumes artifacts only; it never runs PR code.
- Artifact metadata is attacker-controlled, so publishing requires its
`headSha` to equal the head SHA the API reports for that PR — without it a
fork could claim another PR's number.
- `.asf.yaml` and `robots.txt` are generated by the publisher and overwrite
anything of the same name in the artifact.
- Archives containing symlink members, absolute or traversing entry names, or
an unreadable expanded size are refused outright.
- `.git`, `.gitignore` and `.gitmodules` are stripped at any depth; every git
call pins `core.hooksPath` and `core.excludesFile` to `/dev/null`; the commit
uses `--no-verify`.
- The token is redacted from every field of a child-process rejection that can
reach a log, including `spawnargs`.
- Unknown armed state never triggers a destructive action — missing data must
not be more dangerous than an explicit "no".
This PR also splits `build.yml` so the job running `npm ci` and `npm run build`
on pull-request code no longer holds `contents: write`; the publish step moves
to a separate `main`-only `deploy` job.
## Testing
83 unit tests (`npm test`, Node's built-in runner, no new dependency), now run
in CI — they weren't before. The security-carrying logic was deliberately
extracted into pure functions so it is testable: metadata validation, entry-name
screening, symlink-member detection, tree preparation, token redaction, and the
publish/tombstone/delete decision.
`git.mjs`'s push path and `artifact.mjs`'s fetch path have no integration
coverage. Both are thin shells over `git` and `fetch`, and they cannot be
exercised without a real repository and a real token — which is what the
post-merge checklist below is for.
## This cannot be tested before it merges
`schedule` and `workflow_dispatch` only ever run the default branch's copy of a
workflow, so nothing here runs from a PR branch. Post-merge acceptance:
1. Comment `/show-preview` on a throwaway PR (or dispatch **Publish PR
previews** with its number to skip the wait).
2. Confirm `preview/pr<N>-staging` appears and the URL serves within ~3 minutes.
3. Push a commit; confirm the preview follows the new SHA.
4. Close the PR; confirm the URL serves the tombstone — not a 404 — and that
the branch is deleted on the following run.
5. Confirm `https://magpie.apache.org/` is unchanged.
Heads-up: the first scheduled run after merge posts the explainer comment on
every currently-open PR at once. That is expected, and it will be visible on
`commits@`.
## Open question for the list
Tombstoning stops a PR's content being served, but the hostname keeps resolving
and keeps serving the tombstone. Whether INFRA can purge a staging profile — and
whether an accumulating set of tombstoned preview hostnames is acceptable to
them — is worth asking on `users@infra.apache.org` before this is relied on.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@magpie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat: per-PR preview deployments at magpie-pr.staged.apache.org [magpie-site]
Posted by "potiuk (via GitHub)" <gi...@apache.org>.
potiuk merged PR #178:
URL: https://github.com/apache/magpie-site/pull/178
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@magpie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org