Skip to content

feat(ci): open a docs follow-up when loft-sh/plans changes affect sidebar labels#2340

Open
Piotr1215 wants to merge 2 commits into
mainfrom
doc-1523/plans-sidebar-label-sync
Open

feat(ci): open a docs follow-up when loft-sh/plans changes affect sidebar labels#2340
Piotr1215 wants to merge 2 commits into
mainfrom
doc-1523/plans-sidebar-label-sync

Conversation

@Piotr1215

Copy link
Copy Markdown
Contributor

Content Description

Adds a weekly CI workflow that opens (or updates) a docs follow-up issue whenever loft-sh/plans changes, prompting a review of the left-nav Enterprise/Free sidebar labels and the ProAdmonition partial.

Summary

The Feature Table already syncs plan tiers into src/data/products.yaml automatically, but the sidebar labels (sidebar_class_name: pro|free, rendered as ENTERPRISE/FREE badges by src/css/sidebar.scss) and the ProAdmonition partial are maintained by hand. When loft-sh/plans moves a feature between Free and Enterprise, nothing prompts a review of those surfaces, so the docs drift. DOC-1513 (custom resources wrongly marked Enterprise) is the exact failure this leaves uncaught.

This opens a review issue rather than an auto-PR. Unlike the mechanical EOS/EOL date drift (which keeps its auto-PR path in check-eos-eol-labels), the plan-tier to page-label mapping needs human judgement: a page can document several features, a docs_url can point at a section anchor, and a ProAdmonition is placed editorially. The issue carries a best-effort suspect list plus the full label inventory, so the reviewer gets a concrete starting point instead of an empty checklist.

Key Changes

  • scripts/check-plans-sidebar-labels.js: detects recent loft-sh/plans commits, cross-checks each feature tier in products.yaml against the label on its docs page (flags a Free feature whose page still carries pro/ProAdmonition, and an Enterprise-only feature whose page carries free), builds the full label inventory, and opens or updates a single deduped tracking issue.
  • .github/workflows/check-plans-sidebar-labels.yml: weekly schedule + workflow_dispatch + repository_dispatch (plans-updated, so loft-sh/plans can trigger on push). Single script invocation, pinned action SHAs, persist-credentials: false.
  • tests/check-plans-sidebar-labels.test.js: 24 unit tests (Node built-in runner, no new dependencies) covering frontmatter parsing, the cross-check in both directions, the inventory, commit parsing, issue-body rendering, and the open-or-update logic with an injected gh exec.
  • package.json: a check-plans-sidebar-labels script for local dry-runs.

Validated locally against real repo data: the cross-check surfaces a genuine suspect (external-database-kerberos is Free in plans but its page carries sidebar_class_name: pro + a ProAdmonition).

Preview Link

No documentation pages changed (CI tooling, script, and tests only), so there is no deploy preview for this PR.

Dependencies

None. Reuses the existing GH_ACCESS_TOKEN secret, the same token the Feature Table sync uses to read the private loft-sh/plans repo.

TODO

  • Optional follow-up: add a repository_dispatch sender in loft-sh/plans to trigger the review on push. The workflow already runs weekly without it.

Internal Reference

Closes DOC-1523
References DOC-1513

AI review: mention @claude in a comment to request a review or changes. See CONTRIBUTING.md for available commands.

FORK LIMITATION: @claude does not work on pull requests opened from forks. GitHub Actions cannot access the required secrets for fork-originated PRs. To use AI review, push your branch directly to this repository.

@netlify /docs

…ebar labels

The Feature Table already syncs plan tiers automatically, but the left-nav
Enterprise/Free sidebar labels (sidebar_class_name: pro|free) and the
ProAdmonition partial are maintained by hand. When loft-sh/plans moves a
feature between Free and Enterprise, nothing prompts a review of those labels,
so the docs silently drift. DOC-1513 (custom resources wrongly marked
Enterprise) is the exact failure this leaves uncaught.

Add a weekly workflow (plus workflow_dispatch and a repository_dispatch hook
for loft-sh/plans) that detects plans activity, cross-checks each feature tier
in products.yaml against the label on its docs page, and opens or updates a
single review issue carrying the suspected mismatches and the full label
inventory. An issue rather than an auto-PR because the plan-tier to page-label
mapping needs human judgement (multi-feature pages, anchored docs_urls,
editorial ProAdmonition placement); mechanical drift keeps its auto-PR path
(check-eos-eol-labels). Logic lives in a unit-tested Node script so the
workflow stays a single invocation.

Closes DOC-1523
@Piotr1215 Piotr1215 requested a review from a team as a code owner June 29, 2026 13:12
@netlify

netlify Bot commented Jun 29, 2026

Copy link
Copy Markdown

Deploy Preview for vcluster-docs-site ready!

Name Link
🔨 Latest commit 5a21b47
🔍 Latest deploy log https://app.netlify.com/projects/vcluster-docs-site/deploys/6a4623826ff20f0008a7b2d2
😎 Deploy Preview https://deploy-preview-2340--vcluster-docs-site.netlify.app/docs
📱 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.

@Piotr1215 Piotr1215 requested a review from djwfyi June 29, 2026 13:13
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@djwfyi djwfyi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A few suggested fixes:

scripts/check-plans-sidebar-labels.js -- silent failure in fetchPlansCommits

If the gh api call throws (e.g. token lacks read access to loft-sh/plans), commits = [] and on a non-forced run the script outputs drift=false -- indistinguishable from a genuine no-activity week. Track the failure separately:

let commits = [];
let fetchFailed = false;
try {
  commits = fetchPlansCommits({ sinceDays: opts.sinceDays });
} catch (e) {
  console.error(`Could not read ${PLANS_REPO} commits: ${e.message}`);
  fetchFailed = true;
}

Then append plans_fetch_failed=true to GITHUB_OUTPUT so a failed fetch is visible to the caller and doesn't look like a clean no-op.


.github/workflows/check-plans-sidebar-labels.yml -- npm installnpm ci

npm ci is the right call in CI: faster, uses exact lock-file versions, and fails loudly if package-lock.json is out of sync.


scripts/check-plans-sidebar-labels.js -- PRODUCTS_PATH naming

PRODUCTS_YAML and PRODUCTS_PATH serve different purposes (absolute read path vs. prose display string) but are easy to conflate. PRODUCTS_DISPLAY_PATH would make the intent clear.

When the gh api read of loft-sh/plans throws (for example the token loses
read access to the private repo), the sidebar-labels check swallowed the
error and reported drift=false with zero commits, indistinguishable from a
genuine quiet week. A broken token would then silently stop the review from
ever firing. Track the failure separately and emit plans_fetch_failed=true
so the caller can tell a failed fetch from real inactivity.

Also from PR review: use npm ci in the workflow (exact lockfile versions,
fails loudly on lockfile skew) and rename PRODUCTS_PATH to
PRODUCTS_DISPLAY_PATH to distinguish the prose display string from the
absolute read path PRODUCTS_YAML.
@Piotr1215

Copy link
Copy Markdown
Contributor Author

Thanks for the review. All three addressed in 5a21b47.

1. Silent failure in fetchPlansCommits

Tracked the failure separately and surfaced it. main() now sets a fetchFailed flag in the catch and appends plans_fetch_failed=true to GITHUB_OUTPUT on every output path (no-op, open-issue, report-only). A failed read of loft-sh/plans (for example a token without access) now reports drift=false + plans_fetch_failed=true instead of being indistinguishable from a genuine quiet week. The new output is documented in the header comment.

Verified with a failing gh shim plus --force: the error logs and GITHUB_OUTPUT reads drift=true / plans_fetch_failed=true.

2. npm install to npm ci

Done. package-lock.json is tracked, and the workflow already assumed a lockfile via cache: 'npm', so this is safe and fails loudly on lockfile skew.

3. PRODUCTS_PATH naming

Renamed to PRODUCTS_DISPLAY_PATH (both occurrences), with a comment marking it as the prose display string versus the absolute read path PRODUCTS_YAML.

Test suite still green: 24/24 via node --test tests/check-plans-sidebar-labels.test.js.

@Piotr1215 Piotr1215 requested a review from djwfyi July 2, 2026 08:53
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