Guard content PRs: fail validate-content when CMS-managed paths change outside Decap - #150
Merged
Merged
Conversation
…e Decap Content edits that bypass /admin skip the editorial workflow's draft/review status, stable per-entry preview alias, and publish/schedule semantics, and can collide with Decap's on-repo state — and nothing enforced the contract (label-non-decap-prs.yml only tags offenders). Ride the already-required `editorial / validate-content` context instead of adding a new required check: a new pure-logic scripts/content-pr-guard.js (unit-testable, sourcing the cms/ branch convention from e2e/cms-fixture-pr.js so the guard, labeller, and fixture harness can't drift) fails the job with an explanatory PR comment when a non-Decap PR touches base-collection folders, uploaded media, or the site seam's collection folders. Escape hatch: a maintainer applies the override label (name sourced from the module, deliberately not re-typed in the workflow) and the labeled event re-runs the check. Comment/label API work is isolated so an API hiccup can never flip the verdict; the step runs before Ruby setup to fail fast; validate-content keeps NO concurrency block (#1815). Callers should pin the new platform_ref input to their uses: pin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7rqKwvKrn92eNE5LgCvBq
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Consumer sites manage content through Decap's editorial workflow (one entry per PR on a
cms/<collection>/<slug>branch), but nothing enforced that —label-non-decap-prs.ymlonly tags offenders, so a hand-made PR editing_posts/merges as easily as a CMS one (context: today's Adam-S-Daniel/adamdaniel.ai#2549 → #2553 rework).This adds a content PR conformance guard that fails the already-required
editorial / validate-contentcontext when a PR touches CMS-managed content paths without being Decap-created — no new required check, no ruleset change, nocms-automerge-nudgerequired_contextschurn; consumers adopt it through an ordinary platform version bump.scripts/content-pr-guard.js(new) — pure decision logic, no deps/network/fs. Content paths = base collections' folders +media_folder(fromtheme/admin/config.base.yml) + the site seam's (admin/collections.site.yml)folder:values (so jodidaniel.com's per-section collections are covered) + optional extras. "Decap-shaped" = the same triadlabel-non-decap-prs.ymluses (branch prefix sourced frome2e/cms-fixture-pr.js'sFIXTURE_BRANCH_PREFIX, body marker,decap-cms/*label) so the guard, labeller, and fixture harness cannot drift.cms-editorial-workflow.yml— new optionalplatform_repo/platform_refinputs (same pin-to-uses convention aslabel-non-decap-prs.yml);validate-contentgains a platform-module checkout + a fail-fast guard step (before Ruby setup). On violation it upserts a marker PR comment explaining which files are CMS-managed, why the restriction exists, how to redo the change via the site's/admin/(URL derived from the site's_config.yml, never hardcoded), and the escape hatch. Comment/label API work is isolated in try/catch so an API hiccup can never flip the verdict. The job keeps noconcurrencyblock (#1815).content-guard/overridelabel (auto-created; name sourced from the module and deliberately absent from the workflow text, lint-enforced) for deliberate maintenance (fixture repair, bulk migration); thelabeledevent re-runs the check, which then passes.e2e/content-pr-guard.test.js(new, pure-fs lane) — 18 tests: the decision matrix (incl. seam-derived dirs, media uploads, file-list capping) + wiring lints (inputs/defaults, platform checkout, pin byte-equality with the rest of the file, the #1815 no-concurrency invariant, example-caller pin parity). Registered inPLATFORM_META_SPECSper the feat(admin): ship admin/ via the theme gem (Option 1A, issue #5) #16 recurrence guard.examples/sitecaller — passesplatform_refmatching itsuses:pin.Sanctioned flows unaffected
Decap entry/delete-recovery PRs (
cms/*branches), e2e fixture PRs (cms/e2e-fixture/*), dependabot/platform-bump/skills-sync/tool-sync PRs (no content paths) all pass. One observation, deliberately not addressed here:publish-scheduled-posts.ymlpushes content commits directly tomainwith the default token, while the checked-in adamdaniel.ai ruleset shows apull_requestrule and emptybypass_actors— those two mechanisms look mutually inconsistent regardless of this PR (worth a look).Test plan
--project=chromium-light): 861 passed, incl. the 18 new testsactionlint-relevant style checks ride the existing lints (run-name, pin comments); YAML parses cleanfield-library-ref-render.test.js(RubyUS-ASCIIlocale issue in this container; reproduces identically on unmodifiedmainviagit stash— untouched here)Rollout
gh workflow run release.yml -f version=v0.1.59+).with: platform_ref: <same tag>to the editorial caller (defaults tomainotherwise — works, but can version-skew).🤖 Generated with Claude Code
https://claude.ai/code/session_01D7rqKwvKrn92eNE5LgCvBq