From 4104b921f2d0766e068d5a417dd9c31e6138ece6 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 4 Sep 2026 03:38:49 -0400 Subject: [PATCH] Promote Decap staging branch to cms --- docs/decap-cms-gitea-setup.md | 30 ++++++++++----------- docs/decap-cms-pilot.md | 50 ++++++++++++++++++----------------- public/admin/config.yml | 6 ++--- scripts/verify-decap.mjs | 4 +-- 4 files changed, 46 insertions(+), 44 deletions(-) diff --git a/docs/decap-cms-gitea-setup.md b/docs/decap-cms-gitea-setup.md index d201e0d..398d84b 100644 --- a/docs/decap-cms-gitea-setup.md +++ b/docs/decap-cms-gitea-setup.md @@ -1,23 +1,23 @@ -# Decap CMS pilot: Gitea settings +# Decap CMS: Gitea settings -Status: applied to the preview-only pilot on 2026-09-04. The stable preview, -narrow CORS policy, public PKCE OAuth application, and both branch-protection -rules are active. Nothing has been merged to `main`. +Status: the preview-only pilot was accepted for promotion on 2026-09-04. The +repository now targets the permanent `cms` staging branch. Update the exact +CORS origin and PKCE OAuth redirect only after its first preview deployment +confirms the predicted alias. Nothing has been merged to `main`. The repository implementation targets these fixed public values: | Setting | Value | | --- | --- | | Gitea repository | `SloanOps/3031023-xyz` | -| Decap write branch | `cms-pilot` | -| Planned live write branch | `cms` | +| Decap write branch | `cms` | | Gitea base URL | `https://git.sloanops.com` | | Gitea API root | `https://git.sloanops.com/api/v1` | -| Stable CMS origin | `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev` | -| Exact OAuth redirect URI | `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev/admin/` | +| Stable CMS origin | `https://cms-3031023-xyz-www.ksloan90.workers.dev` | +| Exact OAuth redirect URI | `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` | | Required status context | `CMS content validation / cms-content-validation (pull_request)` | -The pilot and planned live branch names are intentionally slash-free. Decap +The pilot and permanent branch names are intentionally slash-free. Decap CMS 3.15.1 inserts the configured Gitea branch directly into its tree-API URL; a slash-named branch such as `cms/pilot` is split as an API path and its 404 is presented as an empty collection. The permanent `cms` branch is still a @@ -38,7 +38,7 @@ through the normal infrastructure change process: ```yaml - GITEA__cors__ENABLED=true -- GITEA__cors__ALLOW_DOMAIN=https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev +- GITEA__cors__ALLOW_DOMAIN=https://cms-3031023-xyz-www.ksloan90.workers.dev - GITEA__cors__METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS - GITEA__cors__MAX_AGE=10m - GITEA__cors__ALLOW_CREDENTIALS=false @@ -56,7 +56,7 @@ token or cookie: ```bash curl --request OPTIONS 'https://git.sloanops.com/api/v1/version' \ - --header 'Origin: https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev' \ + --header 'Origin: https://cms-3031023-xyz-www.ksloan90.workers.dev' \ --header 'Access-Control-Request-Method: GET' \ --header 'Access-Control-Request-Headers: authorization,content-type' ``` @@ -71,10 +71,10 @@ behavior before continuing. The Gitea OAuth2 application was created after the stable preview origin and CORS response were verified: -- Application name: `3031023.xyz Decap CMS pilot`. +- Application name: `3031023.xyz Decap CMS`. - Confidential client: **unchecked**. The browser client must use PKCE. - Redirect URI: exactly - `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev/admin/`, including the + `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/`, including the trailing slash. - Do not add wildcards, localhost, either production hostname, or the journal preview as additional redirects. @@ -148,7 +148,7 @@ directly into the flat uploads directory, and the local verifier rejects nested media paths. Gitea file patterns are case-insensitive; CI verifies actual file signatures and the 500 KiB limit before a preview can upload. -Everything not listed is protected from CMS/client pushes on `cms-pilot` and +Everything not listed is protected from CMS/client pushes on `cms` and on every differently named branch, including: ```text @@ -177,7 +177,7 @@ altered by choosing another branch name. ## Verification before an editor invitation 1. Run `npm ci`, `npm run verify:cms`, and both site build/verifier pairs. -2. Create `cms-pilot` from the reviewed baseline only after approval. +2. Create `cms` from the reviewed baseline only after approval. 3. Apply both protection rules together and confirm `main` is priority 1 and `*` is priority 2. 4. Use the same non-operator account intended for Decap to prove allowed diff --git a/docs/decap-cms-pilot.md b/docs/decap-cms-pilot.md index 57d29d0..d84f2ba 100644 --- a/docs/decap-cms-pilot.md +++ b/docs/decap-cms-pilot.md @@ -1,8 +1,9 @@ -# Decap CMS Gitea pilot +# Decap CMS Gitea pilot and promotion -Status: preview-only pilot infrastructure is active on `cms-pilot`: the stable -preview, narrow Gitea CORS policy, public PKCE OAuth application, and two -repository protection rules are configured. Nothing has been merged to +Status: the preview-only pilot was accepted for promotion to the permanent +`cms` staging branch on 2026-09-04. Repository configuration now targets the +predicted stable `cms` preview origin. CORS and OAuth must be changed together +only after that first preview confirms the alias. Nothing has been merged to `main`, and production remains unchanged. Updated: 2026-09-04 @@ -55,14 +56,14 @@ recorded in [Decap CMS pilot: Gitea settings](decap-cms-gitea-setup.md). ## Repository implementation -The `review/decap-cms-pilot` branch now contains the repository-side portion of -the pilot: +The reviewed pilot implementation is the baseline for the permanent `cms` +staging branch: - `public/admin/` provides a permanently no-index admin shell and one shared configuration for both site builds. - The shell pins Decap CMS `3.15.1` by exact URL and verified SHA-384 integrity value rather than a floating release range. -- The backend is fixed to `SloanOps/3031023-xyz` and `cms-pilot` and contains +- The backend is fixed to `SloanOps/3031023-xyz` and `cms` and contains only the public client ID for the non-confidential Gitea PKCE application. - All four Markdown content collections and every persisted frontmatter field are modeled with the current Astro/Zod bounds. Markdown controls remain @@ -88,8 +89,9 @@ the pilot: by its former auto-detected private address. Warm jobs reuse dependency tarballs without caching `node_modules`. -The pilot dependencies are active: the public client ID, exact-origin CORS -policy, `cms-pilot` branch, and both protection rules have been verified. +The pilot dependencies were verified on `cms-pilot`. The same protection rules +apply to `cms`; its exact-origin CORS and OAuth values are the remaining +operator-controlled promotion step. ## Proposed pilot architecture @@ -98,7 +100,7 @@ Decap at /admin/ | | Gitea PKCE OAuth and content/media commits only v -cms-pilot branch +cms branch | +--> primary no-index Worker preview | @@ -119,11 +121,11 @@ is acceptable because both sites belong to the same root-domain repository and the same owner. One Decap configuration exposes clearly labeled Primary Site and Journal collection groups. -Preview URLs for branch `cms-pilot`: +Predicted preview URLs for branch `cms`: -- `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev/admin/` -- `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev/` -- `https://cms-pilot-3031023-xyz-blog.ksloan90.workers.dev/` +- `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` +- `https://cms-3031023-xyz-www.ksloan90.workers.dev/` +- `https://cms-3031023-xyz-blog.ksloan90.workers.dev/` Confirm the aliases from the successful Gitea Actions run rather than treating these predicted URLs as authoritative. @@ -229,13 +231,13 @@ Preview deployment remains branch-specific and may use the existing Worker workflows. Cloudflare credentials stay scoped to the deployment step; content validation does not need them. -Because Decap writes to a fixed `cms-pilot` branch, SloanOps creates the pull +Because Decap writes to a fixed `cms` branch, SloanOps creates the pull request in Gitea. After an approved merge, synchronize the CMS branch with `main` before the next editing cycle. Do not automate production merge. -If the pilot is accepted, replace the disposable branch with a permanent, -slash-free `cms` branch. It remains a preview/staging branch and retains the -same pull-request, validation, and approval path into `main`. +The accepted pilot is promoted by replacing the disposable branch with the +permanent, slash-free `cms` branch. It remains a preview/staging branch and +retains the same pull-request, validation, and approval path into `main`. ## Gitea and Decap setup @@ -244,11 +246,11 @@ same pull-request, validation, and approval path into `main`. 2. Enable Gitea API CORS for the exact approved preview origin or origins. Allow only required methods and headers, including `Authorization` and `Content-Type`; do not use a wildcard origin. -3. Create `cms-pilot` from the reviewed repository baseline. +3. Create `cms` from the reviewed repository baseline. 4. Review the pinned Decap admin assets beneath `public/admin/`. Do not replace the exact version and integrity value with a floating range. 5. Configure the Gitea backend for `SloanOps/3031023-xyz` and branch - `cms-pilot`. + `cms`. 6. Model every persisted frontmatter field with limits matching `src/content.config.ts`. Do not let a CMS save silently remove an existing field. @@ -269,7 +271,7 @@ not record OAuth tokens or Cloudflare credentials. | DC-02 | Open each preview `/admin/` route | Admin shell loads and remains no-index. | | DC-03 | Authenticate with the approved operator account | PKCE login succeeds and the configured collections load. | | DC-04 | Authenticate with an account without repository write access | Repository editing is unavailable. | -| DC-05 | Edit an existing draft journal post | One content-only commit lands on `cms-pilot`; the blog preview rebuilds. | +| DC-05 | Edit an existing draft journal post | One content-only commit lands on `cms`; the blog preview rebuilds. | | DC-06 | Create, edit, and delete a disposable journal post | Filename, dates, tags, body, draft state, and cleanup remain schema-valid. | | DC-07 | Edit a primary FAQ | Only the primary preview rebuilds and displays the change. | | DC-08 | Edit service ordering and featured state | YAML types remain correct and the primary verifier passes. | @@ -279,10 +281,10 @@ not record OAuth tokens or Cloudflare credentials. | DC-12 | Attempt to modify `.gitea/workflows/**`, source, package, script, schema, Wrangler, and site-manifest paths with the CMS account | Gitea rejects every push. | | DC-13 | Attempt to create a differently named branch and change a protected file | The fallback rule rejects the push; branch naming cannot bypass protection. | | DC-14 | Attempt a direct push to `main` | Gitea rejects it. | -| DC-15 | Open a pull request from `cms-pilot` to `main` | Both sites validate; required review and merge controls are present. | +| DC-15 | Open a pull request from `cms` to `main` | Both sites validate; required review and merge controls are present. | | DC-16 | Attempt to merge without status or approval | Gitea blocks the merge. | | DC-17 | Inspect both preview responses | Expected routes work; preview metadata/headers prevent indexing; production remains unchanged. | -| DC-18 | Revert the disposable CMS changes on `cms-pilot` | Preview returns to baseline without changing either public site. | +| DC-18 | Revert disposable CMS changes on `cms` | Preview returns to baseline without changing either public site. | | DC-19 | Refresh, sign out, reauthenticate, and let a session expire | Session behavior is understandable and no unsaved-content loss is observed. | | DC-20 | Review commit attribution and audit history | Gitea identifies the editor and the exact content/media changes. | @@ -312,7 +314,7 @@ account control over executable deployment files. If the pilot is rejected or paused: -1. Revert or delete disposable changes on `cms-pilot`. +1. Revert or delete disposable changes on `cms`. 2. Delete the two disposable preview aliases/versions if desired; do not delete production Workers or retained legacy rollback resources. 3. Revoke/delete the Decap OAuth application. diff --git a/public/admin/config.yml b/public/admin/config.yml index 2c17c6f..e0c4af4 100644 --- a/public/admin/config.yml +++ b/public/admin/config.yml @@ -1,15 +1,15 @@ backend: name: gitea repo: SloanOps/3031023-xyz - branch: cms-pilot + branch: cms app_id: c07dba05-eb09-48f0-8da4-6df775a13995 api_root: https://git.sloanops.com/api/v1 base_url: https://git.sloanops.com auth_endpoint: https://git.sloanops.com/login/oauth/authorize publish_mode: simple -site_url: https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev -display_url: https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev +site_url: https://cms-3031023-xyz-www.ksloan90.workers.dev +display_url: https://cms-3031023-xyz-www.ksloan90.workers.dev show_preview_links: false media_folder: public/images/uploads diff --git a/scripts/verify-decap.mjs b/scripts/verify-decap.mjs index fe6abe2..c8f6139 100644 --- a/scripts/verify-decap.mjs +++ b/scripts/verify-decap.mjs @@ -17,11 +17,11 @@ function expectSubset(actual, expected, path) { const configPath = 'public/admin/config.yml'; const config = yaml.load(await readFile(configPath, 'utf8')); -const expectedOrigin = 'https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev'; +const expectedOrigin = 'https://cms-3031023-xyz-www.ksloan90.workers.dev'; const expectedBackend = { name: 'gitea', repo: 'SloanOps/3031023-xyz', - branch: 'cms-pilot', + branch: 'cms', api_root: 'https://git.sloanops.com/api/v1', base_url: 'https://git.sloanops.com', auth_endpoint: 'https://git.sloanops.com/login/oauth/authorize',