Skip to content

Deploy PR previews with HarperFast/deploy-action - #797

Draft
dawsontoth wants to merge 4 commits into
mainfrom
ci/preview-deploy-action
Draft

dawsontoth wants to merge 4 commits into
mainfrom
ci/preview-deploy-action

Conversation

@dawsontoth

@dawsontoth dawsontoth commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

⊙ Problem

pr-preview.yaml checks out each PR's code under pull_request_target (fork PRs too, after an approval in preview), builds it in a job that holds HARPER_PREVIEW_USERNAME and HARPER_PREVIEW_PASSWORD, and deploys a pr-<N> component that stays until the PR closes. Previews pile up on the preview host (#695).

HarperFast/deploy-action now does per-PR previews on Harper 5.4 with OIDC only, and Kris suggested running it here so it's exercised on real PRs (HarperFast/deploy-action#1).

💡 Solution

  • preview.yaml is the action's example workflow: a deploy job in the preview environment and a cleanup job in preview-cleanup, each with the fork guard. No checkout, no stored credential, and the action keeps one PR comment and a GitHub Deployment. Previews move from <preview host>/pr-<N> to https://<branch>.app.<cluster domain>/ and expire on their own (7 days by default).
  • preview-build.yaml builds the site. The action's default deploys a PR's commit by reference, but this repo has no Harper config.yaml and the cluster doesn't run a Docusaurus build. So, as the action's README and the CI guide describe for apps that need a build, an unprivileged pull_request run builds the PR's head commit and uploads pr-<N>-build. The deploy job waits for that run, downloads the artifact into site/build, writes its own site/config.yaml (the same static config production uses), and deploys site as a payload with by_ref: false.
  • scripts/preview-pr.mjs (npm run preview:pr) looks for the new build workflow's runs, and serves with base URL /.

The action is pinned to v1.0.0 by its commit, 45fa59b.

🤔 Things to look at

  • Polling. GitHub starts both workflows at once, and the trust policy matches only pull_request_target, so workflow_run can't deploy. The deploy job polls for the build of its exact commit, from a branch of this repository. It deploys nothing if the PR moves on or closes, and fails if the build fails, takes over 30 minutes, or never starts.
  • A PR with merge conflicts gets no preview. GitHub starts no pull_request run for it, so the deploy job fails after 10 minutes and says why. Assemble the v5 reference and open release notes from harper and harper-pro #788 is in that state now. The old workflow built conflicting PRs anyway.
  • Fork PRs get no preview, not one after approval. They still get the build artifact for npm run preview:pr, subject to the repository's fork-approval setting.
  • The application is docs-site, production's component name, so previews are docs-site-pr-<N>. Each gets its own database branch (the default). The site has no tables.
  • actions: read on the deploy job, beyond the example, is for reading the build's runs and downloading its artifact.

📋 Enabling previews (Dawson, @Ethan-Arrowood)

This PR changes no cluster or repository settings itself; these are done by hand, and the checked ones were done on November 5 and 6. A real preview needs this PR merged, since pull_request_target runs main's workflow and the trust policies pin main.

Before merging

Steps 3a, 3b and 4 don't depend on the cluster, so they can happen in any order.

  • 1. Harper Pro 5.4.0-beta.2 on the preview cluster, preview.harper-documentation.harperfabric.com (org-jekmjscs93exkury/clu-ifppwn095xb3s5ze, one instance). Dawson upgraded it in Fabric on November 5. The open PRs' existing previews served again within a minute. alpha.1 has no enable_previews or harper preview setup.
  • 1. The certificate renewed with the preview wildcard. The instance's certificate from Sep 20 didn't cover *.app.preview.harper-documentation.harperfabric.com. Fabric's host-manager adds that wildcard when it renews a certificate, which it does once fewer than 40 days remain (here from November 9); upgrading doesn't renew it. To renew it sooner, Dawson created letsencrypt/wildcard-owed in the instance's folder on hm-us-central1-a on November 5, and host-manager's daily run renewed it at 12:17 UTC on November 6 (until Feb 4), with DNS:*.app.preview.harper-documentation.harperfabric.com. The old /pr-<N> previews still serve. A host under *.app. gets a route only once a deployed preview publishes it, so until then a connection to one is reset. To check the certificate:
    echo | openssl s_client -connect preview.harper-documentation.harperfabric.com:443 -servername preview.harper-documentation.harperfabric.com 2>/dev/null | openssl x509 -noout -dates -ext subjectAltName
  • 2. harper preview setup app=docs-site, signed in as a super_user, from a checkout of this branch (setup reads .github/workflows/preview.yaml and checks it). login prompts for the target, https://preview.harper-documentation.harperfabric.com:9925/.
    npx -y harper@5.4.0-beta.2 login
    npx -y harper@5.4.0-beta.2 preview setup app=docs-site
    Setup creates the docs-site-ci-preview role and user and both trust policies, and enables previews for docs-site. It also sets the HARPER_PREVIEW_TARGET repository variable, which is separate from today's secret of the same name. Dawson ran it on November 5: it created the role, the user, and the github-actions-docs-site-preview and github-actions-docs-site-preview-cleanup trust policies, and set the variable to https://preview.harper-documentation.harperfabric.com:9925/.
  • 3a. preview environment: allow only main under Deployment branches and tags. Keep its developers reviewers until this merges: main's old pr-preview.yaml builds fork PRs with the stored password, and those reviewers are what gate it.
  • 3b. preview-cleanup environment: create it, with no reviewers and the same main rule.
  • 4. An Actions policy allowing pull_request_target for .github/workflows/preview.yaml: "Allow Harper previews", created November 5. The repository is public, and GitHub blocks the event from December 2, with brownouts on November 9 and 16. From the action's README:
    curl --fail --silent --show-error --location https://raw.githubusercontent.com/HarperFast/deploy-action/main/examples/preview-actions-policy.json | jq '{name, enforcement, conditions, rules}' | gh api --method POST repos/HarperFast/documentation/actions/policies --header 'X-GitHub-Api-Version: 2026-03-10' --input -

After merging

  • 3c. preview environment: remove the developers reviewers, or every docs PR's preview waits for an approval.
  • A same-repository PR that changes the docs gets a preview that serves, and closing it drops the preview. Then update Verification below.
  • Delete the HARPER_PREVIEW_USERNAME, HARPER_PREVIEW_PASSWORD and HARPER_PREVIEW_TARGET secrets, and the PREVIEW_CLEANUP_APP_ID variable and PREVIEW_CLEANUP_APP_PRIVATE_KEY secret if they're set. Nothing reads them now.
  • Delete the 37 pr-<N> environments.
  • Drop the old pr-<N> components from the preview cluster.

Outside this PR

  • An Actions policy for companion-check.yaml, allowing pull_request_target, schedule and workflow_dispatch. It also runs on pull_request_target, so without a policy it fails during the brownouts and stops on December 2.

✅ Verification

  • Harper's own check: checkPreviewWorkflowFile (what harper preview setup runs, the same in main and v5.4.0-beta.2) finds no problems, nothing unverified, and nothing unsafe in preview.yaml. Adding a PR-head checkout, or dropping the fork guard, gets refused.
  • The wait step, run against this repository with validate.yaml in place of the build workflow:
  • npm run preview:pr's lookup, run with validate.yaml in place of the build workflow: finds the newest successful run of a branch, and {} for a branch with none.
  • After merging main: Harper's check again finds nothing in preview.yaml, and its paths match deploy-fabric.yaml's.
  • Prettier, oxlint and npm run test:workflows pass. actionlint isn't installed here and wasn't run.
  • The preview cluster, on November 6: Harper Pro 5.4.0-beta.2, a certificate covering *.app.preview.harper-documentation.harperfabric.com, and the open PRs' old /pr-<N> previews serving 200.
  • Not verified: a real deploy or cleanup. Everything before merging is done, so this needs this PR merged and step 3c.

Relates to #695.

🤖 Generated with Claude Code

Replace the hand-written pr-preview.yaml with preview.yaml, the action's
two-job preview workflow (deploy in `preview`, cleanup in
`preview-cleanup`). It authenticates by OIDC only, so the stored
username and password are no longer read, and it never checks out PR
code; fork PRs get no preview.

The site needs a Docusaurus build, which must not run with the preview
identity. preview-build.yaml builds each PR's head commit in an
unprivileged pull_request run and uploads it; the deploy job waits for
that run, downloads the artifact under a config.yaml of its own, and
deploys it as a payload preview at the root of its own host. The local
preview script follows the build workflow's name and base URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request updates scripts/preview-pr.mjs to query the "Build PR Preview" workflow instead of "Deploy PR Preview" and sets the DOCUSAURUS_BASE_URL to / instead of a PR-specific path. Feedback points out that using the --paginate flag with gh api and --jq can result in multiple JSON documents being returned, which will cause a SyntaxError when parsed. It is recommended to remove the --paginate flag.

Comment thread scripts/preview-pr.mjs Resolved
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dawsontoth
dawsontoth marked this pull request as ready for review November 5, 2026 19:17
@dawsontoth
dawsontoth requested a review from a team as a code owner November 5, 2026 19:17
@dawsontoth
dawsontoth marked this pull request as draft November 5, 2026 19:20
dawsontoth and others added 2 commits November 5, 2026 14:22
main dropped reference/** from the preview paths and added
scripts/assemble-content.mjs, and gave the build a token for the
assembler's GitHub calls. Both now apply to preview-build.yaml and
preview.yaml in place of the deleted pr-preview.yaml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`--paginate` with `--jq` printed one document per page, which
JSON.parse refuses once a branch has more than one page of runs. The
workflow's runs endpoint, filtered to successful runs of the branch,
returns the newest one first, and `{}` when there is none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant