Skip to content

feat(site): add versioned docs infrastructure#1314

Merged
decepulis merged 3 commits intomainfrom
claude/versioned-docs-issue-goQLQ
Apr 14, 2026
Merged

feat(site): add versioned docs infrastructure#1314
decepulis merged 3 commits intomainfrom
claude/versioned-docs-issue-goQLQ

Conversation

@decepulis
Copy link
Copy Markdown
Collaborator

@decepulis decepulis commented Apr 12, 2026

Summary

Implements the infrastructure for versioned docs (#645). After this PR merges, the docs deploy pipeline works like this:

How it works

Two sites, two branches:

Branch Deploys to Content
site/v10 (production) videojs.org Stable docs matching the latest release
main (branch deploy) next.videojs.org Pre-release docs (may include unreleased APIs)

Release flow:

Commit merges to main
  → release-please detects release-worthy commits
  → creates release PR on release-please--branches--main
  → release PR merges to main
  → cd.yml fires:
      1. Publishes packages to npm
      2. Force-pushes site/v10 to main's HEAD  ← NEW
  → Netlify rebuilds videojs.org from site/v10

Between releases, main keeps advancing with new features and docs. site/v10 stays pinned to the last release point. Netlify builds main on every push and deploys it to next.videojs.org, so unreleased docs are always accessible without polluting the stable site.

Docs hotfixes between releases:

If a typo or content fix lands on main and needs to go live before the next release, cherry-pick it from main to site/v10:

git checkout site/v10
git cherry-pick <sha-from-main>
git push origin site/v10
# → Netlify auto-deploys to videojs.org

All fixes must land on main first. The site/v10 branch should have branch protection restricting direct pushes to the CD bot (see manual steps below). On the next release, the force-push resets site/v10 to main's HEAD, which already includes the cherry-picked commit.

Changes

.github/workflows/cd.yml

  • Added a step after npm publish that force-pushes HEAD to site/v10. This ensures the docs branch only advances after packages are successfully published. Uses contents: write permission already present on the job.

site/astro.config.mjs

  • Updated SITE_URL to resolve per Netlify deploy context using the CONTEXT and BRANCH env vars that Netlify provides automatically:
    • CONTEXT=productionhttps://videojs.org (stable docs from site/v10)
    • BRANCH=mainhttps://next.videojs.org (pre-release docs)
    • Deploy previews → DEPLOY_PRIME_URL (Netlify subdomain, for PR previews)
  • PRODUCTION_URL in consts.ts is unchanged — canonical URLs and JSON-LD always point to videojs.org regardless of deploy context.

site/netlify.toml

  • Added TURBO_SCM_BASE = "HEAD~1" scoped to context.production and context.branch-deploy. Without this, turbo query affected computes the merge-base of the current branch with main. On site/v10 (which is a point on main's history) and on main itself, that merge-base is HEAD — meaning zero changes, meaning the build gets skipped. HEAD~1 compares against the previous commit instead. This is scoped to production and branch-deploy only so PR previews keep the default merge-base behavior (which correctly detects all changes across a PR's commits).
  • Added X-Robots-Tag: noindex header for branch deploys. This prevents search engines from indexing next.videojs.org, so pre-release docs don't compete with stable docs in search results. The production context (videojs.org) is not affected.

site/CLAUDE.md and site/README.md

  • Added Deployment section documenting the two-branch model, the cherry-pick hotfix workflow, and the branch protection requirement.

Manual steps required after merge

These are Netlify UI settings that can't be configured via netlify.toml:

  1. Change production branch from main to site/v10 in Netlify site settings → Build & deploy → Continuous Deployment → Branches and deploy contexts
  2. Enable branch deploys for main in the same settings panel
  3. Set up next.videojs.org for the main branch deploy (exact approach TBD — branch subdomain vs. separate domain alias)
  4. Add branch protection on site/v10 — restrict direct pushes to the CD workflow's token only. Human contributors cherry-pick via PR targeting site/v10.

Future versioning

When v11 ships someday, the pattern extends naturally:

  • site/v11 becomes the new production branch → videojs.org
  • site/v10 gets pointed at v10.videojs.org (the existing redirect would be updated)
  • main continues serving next.videojs.org

Related

Test plan

  • Verify cd.yml change: the Update site/v10 branch step has the correct if guard and runs after publish
  • Verify astro.config.mjs: local dev still works (CONTEXT and BRANCH are undefined → falls back to https://videojs.org)
  • After merge: change Netlify production branch to site/v10, enable branch deploys for main, set up next.videojs.org domain
  • After merge: add branch protection on site/v10 (restrict pushes to CD bot)
  • After merge: verify videojs.org still deploys correctly
  • After merge: verify next.videojs.org deploys from main
  • After merge: verify X-Robots-Tag: noindex header is present on next.videojs.org responses
  • After next release: verify site/v10 gets force-pushed to the release commit

Note

Medium Risk
Introduces a force-push-based release step and deploy-context URL/SEO behavior changes, which can affect production docs publishing and indexing if misconfigured.

Overview
Implements a two-branch docs deployment flow: releases now publish to npm and then force-push the release commit to site/v10 so Netlify production can track stable docs, while main remains a pre-release branch deploy.

Updates the Astro/Netlify build and SEO behavior for this split: SITE_URL now resolves based on Netlify CONTEXT/BRANCH, Turbo build skipping is avoided via TURBO_SCM_BASE=HEAD~1 on production/branch-deploy, and non-production origins emit robots: noindex.

Adds documentation in site/CLAUDE.md and site/README.md describing the release sync, hotfix cherry-pick workflow, and branch protection expectations.

Reviewed by Cursor Bugbot for commit 75222bf. Bugbot is set up for automated code reviews on this repo. Configure here.

Wire up the `site/v10` branch as the production deploy target for
videojs.org, with `main` serving next.videojs.org for pre-release docs.

- CD workflow force-pushes `site/v10` to main's HEAD after npm publish
- Astro site URL resolves per Netlify context (production / branch / preview)
- Turbo SCM base set to HEAD~1 so deploy-ignore works on all branches
- Branch deploys get X-Robots-Tag: noindex to prevent search indexing

Closes #645

https://claude.ai/code/session_01W6Sb7pTX66ZMbekoJKrnU7
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 12, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 75222bf
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69dd47270c19c7000889ba29
😎 Deploy Preview https://deploy-preview-1314--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Apr 13, 2026 7:43pm

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 12, 2026

📦 Bundle Size Report

🎨 @videojs/html — no changes
Presets (7)
Entry Size
/video (default) 27.59 kB
/video (default + hls) 159.84 kB
/video (minimal) 25.12 kB
/video (minimal + hls) 157.47 kB
/audio (default) 25.59 kB
/audio (minimal) 23.22 kB
/background 4.16 kB
Media (8)
Entry Size
/media/background-video 1.04 kB
/media/container 1.73 kB
/media/dash-video 236.51 kB
/media/hls-video 133.61 kB
/media/mux-audio 156.15 kB
/media/mux-video 156.10 kB
/media/native-hls-video 3.62 kB
/media/simple-hls-video 15.87 kB
Players (3)
Entry Size
/video/player 6.49 kB
/audio/player 4.90 kB
/background/player 3.86 kB
Skins (17)
Entry Type Size
/video/minimal-skin.css css 3.47 kB
/video/skin.css css 3.49 kB
/video/minimal-skin js 25.12 kB
/video/minimal-skin.tailwind js 25.33 kB
/video/skin js 27.56 kB
/video/skin.tailwind js 27.71 kB
/audio/minimal-skin.css css 2.53 kB
/audio/skin.css css 2.50 kB
/audio/minimal-skin js 23.27 kB
/audio/minimal-skin.tailwind js 23.37 kB
/audio/skin js 25.57 kB
/audio/skin.tailwind js 25.71 kB
/background/skin.css css 117 B
/background/skin js 1.15 kB
/base.css css 157 B
/shared.css css 88 B
/skin-element js 1.35 kB
UI Components (24)
Entry Size
/ui/alert-dialog 1015 B
/ui/alert-dialog-close 470 B
/ui/alert-dialog-description 371 B
/ui/alert-dialog-title 389 B
/ui/buffering-indicator 2.02 kB
/ui/captions-button 2.21 kB
/ui/compounds 2.56 kB
/ui/controls 1.44 kB
/ui/error-dialog 2.48 kB
/ui/fullscreen-button 2.30 kB
/ui/hotkey 2.28 kB
/ui/mute-button 2.28 kB
/ui/pip-button 2.23 kB
/ui/play-button 2.27 kB
/ui/playback-rate-button 2.07 kB
/ui/popover 1.84 kB
/ui/poster 1.82 kB
/ui/seek-button 2.27 kB
/ui/slider 1.53 kB
/ui/thumbnail 2.48 kB
/ui/time 1.54 kB
/ui/time-slider 2.62 kB
/ui/tooltip 2.02 kB
/ui/volume-slider 2.23 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react — no changes
Presets (7)
Entry Size
/video (default) 22.33 kB
/video (default + hls) 154.76 kB
/video (minimal) 20.02 kB
/video (minimal + hls) 152.56 kB
/audio (default) 18.33 kB
/audio (minimal) 16.83 kB
/background 754 B
Media (7)
Entry Size
/media/background-video 575 B
/media/dash-video 236.42 kB
/media/hls-video 133.55 kB
/media/mux-audio 156.30 kB
/media/mux-video 156.16 kB
/media/native-hls-video 3.65 kB
/media/simple-hls-video 15.92 kB
Skins (14)
Entry Type Size
/video/minimal-skin.css css 3.40 kB
/video/skin.css css 3.42 kB
/video/minimal-skin js 19.96 kB
/video/minimal-skin.tailwind js 23.42 kB
/video/skin js 22.26 kB
/video/skin.tailwind js 23.48 kB
/audio/minimal-skin.css css 2.43 kB
/audio/skin.css css 2.39 kB
/audio/minimal-skin js 16.74 kB
/audio/minimal-skin.tailwind js 19.24 kB
/audio/skin js 18.23 kB
/audio/skin.tailwind js 19.23 kB
/background/skin.css css 90 B
/background/skin js 272 B
UI Components (19)
Entry Size
/ui/alert-dialog 1.13 kB
/ui/buffering-indicator 1.71 kB
/ui/captions-button 2.23 kB
/ui/controls 1.69 kB
/ui/error-dialog 1.62 kB
/ui/fullscreen-button 2.21 kB
/ui/mute-button 2.22 kB
/ui/pip-button 2.26 kB
/ui/play-button 2.24 kB
/ui/playback-rate-button 1.90 kB
/ui/popover 1.89 kB
/ui/poster 1.53 kB
/ui/seek-button 2.25 kB
/ui/slider 3.03 kB
/ui/thumbnail 2.04 kB
/ui/time 1.83 kB
/ui/time-slider 2.77 kB
/ui/tooltip 2.28 kB
/ui/volume-slider 2.75 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (8)
Entry Size
. 5.29 kB
/dom 11.38 kB
/dom/media/custom-media-element 1.91 kB
/dom/media/dash 235.94 kB
/dom/media/hls 132.95 kB
/dom/media/mux 155.73 kB
/dom/media/native-hls 2.97 kB
/dom/media/simple-hls 15.31 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 999 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.38 kB
/html 696 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 1.80 kB
/events 319 B
/function 327 B
/object 247 B
/predicate 265 B
/string 148 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (3)
Entry Size
. 40 B
/dom 13.27 kB
/playback-engine 13.11 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Context-scoped headers not supported in netlify.toml
    • Replaced unsupported context-scoped headers with a branch-deploy build command that writes a publish-directory _headers file containing X-Robots-Tag: noindex.
  • ✅ Fixed: BRANCH env var missing from turbo globalEnv cache key
    • Added BRANCH to globalEnv in turbo.json so Turbo cache invalidates when branch-dependent site URL logic changes.

Create PR

Or push these changes by commenting:

@cursor push 9424c4f07d
Preview (9424c4f07d)
diff --git a/site/netlify.toml b/site/netlify.toml
--- a/site/netlify.toml
+++ b/site/netlify.toml
@@ -11,15 +11,11 @@
   TURBO_SCM_BASE = "HEAD~1"
 
 # Prevent search engines from indexing branch deploys (next.videojs.org).
-# Stable docs at videojs.org (production context) are not affected.
-[context.branch-deploy.environment]
-  X_ROBOTS_TAG = "noindex"
+# Netlify cannot scope headers by context, so branch deploys generate a
+# context-specific _headers file in the publish directory during build.
+[context.branch-deploy]
+  command = "pnpm build:site && echo '/*' > site/dist/_headers && echo '  X-Robots-Tag: noindex' >> site/dist/_headers"
 
-[[context.branch-deploy.headers]]
-  for = "/*"
-  [context.branch-deploy.headers.values]
-    X-Robots-Tag = "noindex"
-
 # PostHog reverse proxy (ad-blocker bypass)
 [[redirects]]
   from = "/ph/static/*"

diff --git a/turbo.json b/turbo.json
--- a/turbo.json
+++ b/turbo.json
@@ -2,7 +2,7 @@
   "$schema": "https://turbo.build/schema.json",
   "ui": "stream",
   "concurrency": "20",
-  "globalEnv": ["CONTEXT", "DEPLOY_PRIME_URL"],
+  "globalEnv": ["CONTEXT", "DEPLOY_PRIME_URL", "BRANCH"],
   "tasks": {
     "build": {
       "dependsOn": ["^build", "^build:cdn"],

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Comment thread site/netlify.toml Outdated
Comment thread site/astro.config.mjs
@decepulis
Copy link
Copy Markdown
Collaborator Author

Review note: branch protection needed on site/v10

The cherry-pick hotfix workflow depends on the rule that all fixes land on main first. Without enforcement, someone could push directly to site/v10 and have their work silently wiped by the next release's force-push.

After merge, add a branch protection rule for site/v10:

  • Restrict pushes to only the CD workflow's token (the V10_WORKFLOW_TRIGGER_WORKFLOW_GH_TOKEN PAT or equivalent)
  • Human contributors cherry-pick via PR targeting site/v10, which adds a review checkpoint

This keeps the force-push safe by design rather than by team policy.

- Replace invalid [[context.branch-deploy.headers]] in netlify.toml
  with a <meta name="robots" content="noindex"> tag in Base.astro.
  Netlify headers can't be scoped per deploy context, but comparing
  Astro.site against PRODUCTION_URL achieves the same result and works
  with any host.

- Add BRANCH to turbo.json globalEnv so the build cache invalidates
  when the Netlify branch changes (SITE_URL now depends on it).

https://claude.ai/code/session_01W6Sb7pTX66ZMbekoJKrnU7
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 75222bf. Configure here.

Comment thread .github/workflows/cd.yml

- name: Update site/v10 branch
if: ${{ steps.release.outputs.releases_created == 'true' }}
run: git push origin HEAD:refs/heads/site/v10 --force
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Force-push uses GITHUB_TOKEN, incompatible with planned branch protection

High Severity

The git push --force to site/v10 uses the default GITHUB_TOKEN credentials (from actions/checkout at line 35, which doesn't specify a token). The GITHUB_TOKEN cannot bypass branch protection rules. Once the planned branch protection is added to site/v10 (restricting pushes to the CD bot's PAT), this force-push will fail, breaking the entire release-to-production docs pipeline. The actions/checkout step needs to use token: ${{ secrets.V10_WORKFLOW_TRIGGER_WORKFLOW_GH_TOKEN }} (the PAT already available in this workflow) so the push inherits credentials that can bypass branch protection.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 75222bf. Configure here.

@decepulis decepulis merged commit c4dfa3e into main Apr 14, 2026
23 checks passed
@decepulis decepulis deleted the claude/versioned-docs-issue-goQLQ branch April 14, 2026 12:50
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.

Feature: Versioned Docs

2 participants