Skip to content

ci: deploy website in its own workflow, only on docs output change#9650

Merged
Boshen merged 14 commits into
mainfrom
ci/deploy-website-workflow
Jun 5, 2026
Merged

ci: deploy website in its own workflow, only on docs output change#9650
Boshen merged 14 commits into
mainfrom
ci/deploy-website-workflow

Conversation

@Boshen

@Boshen Boshen commented Jun 4, 2026

Copy link
Copy Markdown
Member

What

Moves the docs build + Void deploy out of ci.yml into a dedicated .github/workflows/deploy-website.yml, and deploys only when the docs content actually changes.

Why

Previously ci.yml deployed on every push to main that touched packages/rolldown/src/** — even when the rendered docs were unchanged. Docs should be validated on every PR, but a deploy should only happen when the output actually changes.

How

Two jobs, split so the deploy secret is isolated from build steps:

docs-validation — runs on every PR / merge-queue / main run. Builds the whole site (the PR validation build) and, on main, decides whether to deploy. It has no environment and no access to VOID_TOKEN.

  • Generates the TypeDoc API reference, and (off-main) builds the site.
  • On main, computes a deterministic fingerprint of the deployable content — git-tracked docs/** sources plus the generated docs/reference/** — and uses it as a GitHub Actions cache key to identify an already-deployed state. A packages/rolldown/src change that doesn't alter the public API yields the same reference → same fingerprint → already cached → no deploy.

deploy-voidenvironment: void, runs only on main and only when the fingerprint is new (cache miss) or on workflow_dispatch. Deploys via vp run docs:void and records the fingerprint in the cache. The only place VOID_TOKEN is in scope, and it never runs on a PR.

The built dist is never hashed — it isn't reproducible (some vitepress plugins emit unstable ordering). TypeDoc output and the git-tracked sources are deterministic, which is what makes the cache fingerprint reliable.

ci.yml: drops the docs-changes filter/output and the old docs-validation / deploy-void jobs.

🤖 Generated with Claude Code

Move the docs build and Void deploy out of ci.yml into a dedicated
deploy-website.yml workflow.

Docs are validated on every PR/merge-queue run. On main the site is
deployed only when the rendered output actually changes: a content
fingerprint of the git-tracked docs sources plus the generated TypeDoc
reference (docs/reference) is compared against an actions/cache marker
keyed by that hash, so a packages/rolldown/src change that does not
affect the public API produces the same reference and is skipped.
workflow_dispatch force-deploys.

Drops the docs-changes detection and the docs-validation/deploy-void
jobs from ci.yml.
@netlify

netlify Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit ef10cb8
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/6a2285f2b8c612000820cc40

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 74104eba61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/deploy-website.yml
Boshen added 2 commits June 5, 2026 00:03
…d steps

The merged job carried environment: void while running build/validation
steps on PRs, so a malicious PR could materialize VOID_TOKEN in those
steps. Split back into two jobs:

- docs-validation: builds/validates and computes the deploy decision
  (content fingerprint + cache marker lookup). No environment, no secret.
- deploy-void: environment: void; runs only on main and only when
  docs-validation says a deploy is needed. Does nothing but checkout +
  setup + deploy, so VOID_TOKEN is in scope only for the deploy step and
  unchanged content never enters the void environment.
…ommit

Replace the actions/cache fingerprint marker with a direct comparison of
the generated content between HEAD and HEAD^.

docs-validation regenerates the TypeDoc reference for both commits and
hashes the git-tracked docs sources plus docs/reference; the deploy runs
only when they differ (or on workflow_dispatch). The built dist itself is
not compared because it is not reproducible (vitepress-plugin-group-icons
and vitepress-plugin-llms emit unstable ordering); TypeDoc output and the
tracked sources are deterministic, which makes this comparison reliable.

No GitHub Actions cache is used. Any failure regenerating the previous
commit falls through to deploying.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ed5155e8ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/deploy-website.yml Outdated
# Regenerate the reference from the previous commit and fingerprint that. Any failure
# here (e.g. a dependency change between commits) falls through to deploying.
prev_fp=""
if git checkout HEAD^ -- docs packages/rolldown && vp run --filter rolldown-docs generate; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Compare against the last deployed fingerprint

On a push to main that contains multiple commits, GitHub runs this workflow once for the final SHA, but this line compares the rendered docs only to HEAD^. If commit A changes the docs output and commit B does not, the final run sees head_fp == prev_fp and skips deployment even though the live site still predates commit A. Compare against the push's previous SHA or a persisted last-deployed fingerprint/cache marker instead of only the immediately preceding commit.

Useful? React with 👍 / 👎.

Build the full site for HEAD and HEAD^ and diff the dist; deploy only when
it changed (or on workflow_dispatch). This compares the actual rendered
output instead of the generated reference, so source changes that don't
affect the site are skipped.

The dist diff requires reproducible builds. vitepress-plugin-group-icons
emitted the [data-title] selectors within each CSS rule in unstable order
(file-transform order), which changed the CSS hash and cascaded to every
HTML page; patch it to sort the selectors. With that, two builds of the
same commit are byte-identical except the generated llms*.txt aggregations,
which the diff excludes (real content changes still show up in the HTML).

No GitHub Actions cache is used. A failure building the previous commit
falls through to deploying.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86e4fc951e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/deploy-website.yml Outdated
&& vp run --filter rolldown-docs generate \
&& vp run --filter rolldown-docs build; then
mv docs/.vitepress/dist /tmp/dist-prev
if diff -rq -x 'llms.txt' -x 'llms-full.txt' /tmp/dist-head /tmp/dist-prev >/dev/null 2>&1; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include LLM artifacts in the deploy gate

When a main push changes only LLM-specific output, such as the llmstxt description, details, or ignoreFiles settings in docs/.vitepress/config.ts, the workflow is triggered by docs/** but this diff ignores both generated LLM files. Because those settings do not need to affect any HTML, changed can stay 0 and deploy=false, leaving the live /llms.txt or /llms-full.txt stale even though tracked docs inputs changed.

Useful? React with 👍 / 👎.

Boshen added 3 commits June 5, 2026 01:29
Pick the diff baseline by context: on main compare the built site against
the previous commit; on a PR compare against the target branch (main, fetched
shallowly). The comparison now also runs on PRs as an informational signal
(written to the job summary); deploy stays main-only.
… dist

The vitepress-plugin-group-icons patch broke installs on pnpm 10 (Node 20)
with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH, because the patch was committed with
pnpm 11 whose lockfile encoding of patchedDependencies differs.

Remove the patch entirely. Without it the built dist isn't reproducible, so
the deploy gate compares the deterministic generated content (git-tracked
docs sources + the TypeDoc reference) against the baseline instead — keeping
the contextual baseline (previous commit on main, target branch on a PR).
…script

zizmor flagged interpolating github.ref_name / github.base_ref directly into
the run block (a branch name could inject shell). Pass them through env and
reference shell variables instead.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fed698de54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/deploy-website.yml Outdated
Boshen added 2 commits June 5, 2026 09:53
Build the full site for the current ref and the baseline (previous commit on
main, target branch on a PR) and diff the dist. The build isn't byte-reproducible
(vitepress-plugin-group-icons emits unstable CSS selector/rule ordering, which
cascades into the asset hash; vitepress-plugin-llms reorders llms*.txt), so both
dists are normalized before diffing via scripts/misc/normalize-docs-dist.mjs:
canonicalize the CSS, de-hash the CSS filename/refs, and drop llms*.txt. The
deploy still ships the real build.

Verified: two builds of the same commit normalize to an identical tree, and a
real content change is still detected. No dependency patch, no Actions cache.
The shallow checkout (fetch-depth 2) made vitepress lastUpdated fall back to
file mtime, so the per-page data chunks (and their hashes, and sitemap lastmod)
differed between the two builds and the normalized diff always reported a change.
Fetch full history so lastUpdated resolves from git and is identical across both
builds. Also log the differing files when a change is detected.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a23f7a3fb5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/misc/normalize-docs-dist.mjs Outdated
.join(',');
return `${selectors}{${chunk.slice(i + 1)}`;
});
chunks.sort();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve CSS rule order when normalizing

When a docs change intentionally reorders equal-specificity CSS rules, such as in docs/.vitepress/theme/styles.css, the shipped CSS changes because cascade order is meaningful, but this normalization sorts every CSS rule chunk before diffing. In that scenario the normalized head and baseline trees can compare equal after the hash/reference rewrite, so a main push that only changes CSS ordering skips the deploy even though the live site would render differently.

Useful? React with 👍 / 👎.

Boshen added 4 commits June 5, 2026 12:02
After the full-clone fix, the only residual diff was assets/style.css. The
canonicalizer is order-invariant (proven: 4 builds canonicalize to one hash),
so a residual diff means the group-icons CSS content itself differs between CI
builds (the label collection races). Drop the [data-title] rules from the CSS
comparison entirely; the rest of the CSS is deterministic. Verified locally:
same-commit builds normalize identical, real content change still detected.
Drop the built-output diff (and scripts/misc/normalize-docs-dist.mjs) — the
built site isn't reproducible. Instead, on main, fingerprint the deterministic
deployable content (git-tracked docs sources + the TypeDoc reference) and use it
as an actions/cache key: deploy only when that fingerprint hasn't been deployed
before (or on workflow_dispatch). Deploys remain main-only; PRs only validate.
On main, fingerprint the deterministic deployable content (git-tracked docs
sources + the TypeDoc reference) and use it as an actions/cache key: deploy only
when that fingerprint hasn't been deployed before (or on workflow_dispatch). The
built site is never hashed (not reproducible). Deploys stay main-only.
- The deploy decision is a pure function of the cache lookup + event, so express
  it directly in the job output and remove the separate gate step.
- The deploy marker's contents are never read (cache key is the fingerprint), so
  touch it instead of writing the hash through an env var.
@Boshen Boshen merged commit de761ba into main Jun 5, 2026
32 checks passed
@Boshen Boshen deleted the ci/deploy-website-workflow branch June 5, 2026 08:34
@shulaoda shulaoda mentioned this pull request Jun 11, 2026
@rolldown-guard rolldown-guard Bot mentioned this pull request Jun 11, 2026
shulaoda pushed a commit that referenced this pull request Jun 11, 2026
## [1.1.1] - 2026-06-11

### 🚀 Features

- ecmascript_utils: introduce AstFactory for AST construction (#9682) by @hyf0
- drop no-op init calls for empty wrapped-ESM modules (#9678) by @IWANABETHATGUY

### 🐛 Bug Fixes

- resolver: honor package.json#type for .jsx/.tsx extensions (#9690) (#9699) by @IWANABETHATGUY
- hmr: report the full-reload reason for the invalidate-loop case (#9708) by @hyf0
- explicit `moduleSideEffects` from a hook must take priority over the `package.json#sideEffects` (#9688) by @sapphi-red
- keep the `rolldown-runtime` name for the standalone runtime chunk (#9685) by @shulaoda
- lazy-barrel: request all exports for entry barrels on first encounter (#9672) by @shulaoda
- finalizer: skip init_*() for tree-shaken wrapped ESM owners (#9669) by @IWANABETHATGUY
- order `chunk.imports` by execution order (#9654) by @chuganzy
- vite-resolve: preserve slash separators for Sass partial exports (#9546) by @cjc0013
- cross-chunk CJS wrapper shadowed by author-local binding (#9648) by @IWANABETHATGUY

### 🚜 Refactor

- precompute wrapped-ESM init metadata in generate stage (#9712) by @IWANABETHATGUY
- ecmascript_utils: fold construction ext traits onto AstFactory and delete AstSnippet (#9702) by @hyf0
- finalizer: finish ScopeHoistingFinalizer migration to AstFactory (#9701) by @hyf0
- finalizer: migrate module_finalizers/mod.rs to AstFactory (#9700) by @hyf0
- hmr: migrate hmr finalizer to AstFactory (#9695) by @hyf0
- plugin: migrate vite_build_import_analysis to AstFactory (#9693) by @hyf0
- scanner: migrate tweak_ast_for_scanning to AstFactory (#9683) by @hyf0
- always split runtime module first (#9419) by @IWANABETHATGUY

### 📚 Documentation

- tsconfig: correct auto-discovery resolution to match TypeScript (#9714) by @shulaoda
- design: plan to unify all internal AST construction (#9673) by @hyf0
- tsconfig: align reference resolution docs with TypeScript behavior (#9641) by @shulaoda

### ⚡ Performance

- avoid per-module join Strings in scope-hoisting concatenation (#9645) by @Boshen
- avoid intermediate Strings in the ESM export clause (#9644) by @Boshen
- reuse a scratch buffer for facade namespace names in the scanner (#9642) by @Boshen
- reuse the import-matching tracker stack across named imports (#9643) by @Boshen
- avoid cloning the per-chunk export-items map in render_chunk_exports (#9639) by @Boshen
- avoid CompactStr allocation in sorted-exports membership check (#9640) by @Boshen

### 🧪 Testing

- add more `moduleSideEffects` precedence tests (#9689) by @sapphi-red
- 9651: drop shimMissingExports from regression fixture (#9674) by @IWANABETHATGUY

### ⚙️ Miscellaneous Tasks

- update react repo links (#9711) by @iiio2
- remove outdated pnpm configurations (#9666) by @btea
- deps: update github actions to v2.81.5 (#9665) by @renovate[bot]
- testing: migrate test harness off deprecated inlineDynamicImports (#9710) by @IWANABETHATGUY
- hmr: delete commented-out dead code left from old register-module codegen (#9707) by @hyf0
- deps: update napi-rs toolchain (#9706) by @shulaoda
- deps: update rollup submodule for tests to v4.61.1 (#9676) by @rolldown-guard[bot]
- deps: update test262 submodule for tests (#9677) by @rolldown-guard[bot]
- deps: update oxc to 0.135.0 (#9670) by @Boshen
- pass ALGOLIA_APP_ID and ALGOLIA_API_KEY to void deploy (#9667) by @Boshen
- deps: update github actions (#9662) by @renovate[bot]
- deps: update rust crates (#9663) by @renovate[bot]
- deps: update rolldown-plugin-dts to v0.25.2 (#9661) by @renovate[bot]
- deps: update typos to v1.47.2 (#9660) by @renovate[bot]
- deps: update docs dependencies (#9657) by @bddjr
- deps: update typos to v1.47.1 (#9655) by @renovate[bot]
- deploy website in its own workflow, only on docs output change (#9650) by @Boshen
- publish to pkg.pr.new add pm and `commentWithDev` option (#9638) by @btea

### ❤️ New Contributors

* @chuganzy made their first contribution in [#9654](#9654)
* @cjc0013 made their first contribution in [#9546](#9546)
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.

2 participants