Promote Decap staging branch to cms

This commit is contained in:
Kevin
2026-09-04 03:38:49 -04:00
parent 09a491b16b
commit 4104b921f2
4 changed files with 46 additions and 44 deletions
+15 -15
View File
@@ -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, Status: the preview-only pilot was accepted for promotion on 2026-09-04. The
narrow CORS policy, public PKCE OAuth application, and both branch-protection repository now targets the permanent `cms` staging branch. Update the exact
rules are active. Nothing has been merged to `main`. 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: The repository implementation targets these fixed public values:
| Setting | Value | | Setting | Value |
| --- | --- | | --- | --- |
| Gitea repository | `SloanOps/3031023-xyz` | | Gitea repository | `SloanOps/3031023-xyz` |
| Decap write branch | `cms-pilot` | | Decap write branch | `cms` |
| Planned live write branch | `cms` |
| Gitea base URL | `https://git.sloanops.com` | | Gitea base URL | `https://git.sloanops.com` |
| Gitea API root | `https://git.sloanops.com/api/v1` | | Gitea API root | `https://git.sloanops.com/api/v1` |
| Stable CMS origin | `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev` | | Stable CMS origin | `https://cms-3031023-xyz-www.ksloan90.workers.dev` |
| Exact OAuth redirect URI | `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev/admin/` | | Exact OAuth redirect URI | `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` |
| Required status context | `CMS content validation / cms-content-validation (pull_request)` | | 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; 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 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 presented as an empty collection. The permanent `cms` branch is still a
@@ -38,7 +38,7 @@ through the normal infrastructure change process:
```yaml ```yaml
- GITEA__cors__ENABLED=true - 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__METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
- GITEA__cors__MAX_AGE=10m - GITEA__cors__MAX_AGE=10m
- GITEA__cors__ALLOW_CREDENTIALS=false - GITEA__cors__ALLOW_CREDENTIALS=false
@@ -56,7 +56,7 @@ token or cookie:
```bash ```bash
curl --request OPTIONS 'https://git.sloanops.com/api/v1/version' \ 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-Method: GET' \
--header 'Access-Control-Request-Headers: authorization,content-type' --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 The Gitea OAuth2 application was created after the stable preview origin and
CORS response were verified: 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. - Confidential client: **unchecked**. The browser client must use PKCE.
- Redirect URI: exactly - 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. trailing slash.
- Do not add wildcards, localhost, either production hostname, or the journal - Do not add wildcards, localhost, either production hostname, or the journal
preview as additional redirects. 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 media paths. Gitea file patterns are case-insensitive; CI verifies actual file
signatures and the 500 KiB limit before a preview can upload. 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: on every differently named branch, including:
```text ```text
@@ -177,7 +177,7 @@ altered by choosing another branch name.
## Verification before an editor invitation ## Verification before an editor invitation
1. Run `npm ci`, `npm run verify:cms`, and both site build/verifier pairs. 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 3. Apply both protection rules together and confirm `main` is priority 1 and
`*` is priority 2. `*` is priority 2.
4. Use the same non-operator account intended for Decap to prove allowed 4. Use the same non-operator account intended for Decap to prove allowed
+26 -24
View File
@@ -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 Status: the preview-only pilot was accepted for promotion to the permanent
preview, narrow Gitea CORS policy, public PKCE OAuth application, and two `cms` staging branch on 2026-09-04. Repository configuration now targets the
repository protection rules are configured. Nothing has been merged to 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. `main`, and production remains unchanged.
Updated: 2026-09-04 Updated: 2026-09-04
@@ -55,14 +56,14 @@ recorded in [Decap CMS pilot: Gitea settings](decap-cms-gitea-setup.md).
## Repository implementation ## Repository implementation
The `review/decap-cms-pilot` branch now contains the repository-side portion of The reviewed pilot implementation is the baseline for the permanent `cms`
the pilot: staging branch:
- `public/admin/` provides a permanently no-index admin shell and one shared - `public/admin/` provides a permanently no-index admin shell and one shared
configuration for both site builds. configuration for both site builds.
- The shell pins Decap CMS `3.15.1` by exact URL and verified SHA-384 integrity - The shell pins Decap CMS `3.15.1` by exact URL and verified SHA-384 integrity
value rather than a floating release range. 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. only the public client ID for the non-confidential Gitea PKCE application.
- All four Markdown content collections and every persisted frontmatter field - All four Markdown content collections and every persisted frontmatter field
are modeled with the current Astro/Zod bounds. Markdown controls remain 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 by its former auto-detected private address. Warm jobs reuse dependency
tarballs without caching `node_modules`. tarballs without caching `node_modules`.
The pilot dependencies are active: the public client ID, exact-origin CORS The pilot dependencies were verified on `cms-pilot`. The same protection rules
policy, `cms-pilot` branch, and both protection rules have been verified. apply to `cms`; its exact-origin CORS and OAuth values are the remaining
operator-controlled promotion step.
## Proposed pilot architecture ## Proposed pilot architecture
@@ -98,7 +100,7 @@ Decap at /admin/
| |
| Gitea PKCE OAuth and content/media commits only | Gitea PKCE OAuth and content/media commits only
v v
cms-pilot branch cms branch
| |
+--> primary no-index Worker preview +--> 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 the same owner. One Decap configuration exposes clearly labeled Primary Site
and Journal collection groups. 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-3031023-xyz-www.ksloan90.workers.dev/admin/`
- `https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev/` - `https://cms-3031023-xyz-www.ksloan90.workers.dev/`
- `https://cms-pilot-3031023-xyz-blog.ksloan90.workers.dev/` - `https://cms-3031023-xyz-blog.ksloan90.workers.dev/`
Confirm the aliases from the successful Gitea Actions run rather than treating Confirm the aliases from the successful Gitea Actions run rather than treating
these predicted URLs as authoritative. 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 workflows. Cloudflare credentials stay scoped to the deployment step; content
validation does not need them. 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 request in Gitea. After an approved merge, synchronize the CMS branch with
`main` before the next editing cycle. Do not automate production merge. `main` before the next editing cycle. Do not automate production merge.
If the pilot is accepted, replace the disposable branch with a permanent, The accepted pilot is promoted by replacing the disposable branch with the
slash-free `cms` branch. It remains a preview/staging branch and retains the permanent, slash-free `cms` branch. It remains a preview/staging branch and
same pull-request, validation, and approval path into `main`. retains the same pull-request, validation, and approval path into `main`.
## Gitea and Decap setup ## 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. 2. Enable Gitea API CORS for the exact approved preview origin or origins.
Allow only required methods and headers, including `Authorization` and Allow only required methods and headers, including `Authorization` and
`Content-Type`; do not use a wildcard origin. `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 4. Review the pinned Decap admin assets beneath `public/admin/`. Do not replace
the exact version and integrity value with a floating range. the exact version and integrity value with a floating range.
5. Configure the Gitea backend for `SloanOps/3031023-xyz` and branch 5. Configure the Gitea backend for `SloanOps/3031023-xyz` and branch
`cms-pilot`. `cms`.
6. Model every persisted frontmatter field with limits matching 6. Model every persisted frontmatter field with limits matching
`src/content.config.ts`. Do not let a CMS save silently remove an existing `src/content.config.ts`. Do not let a CMS save silently remove an existing
field. 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-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-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-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-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-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. | | 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-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-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-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-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-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-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. | | 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: 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 2. Delete the two disposable preview aliases/versions if desired; do not delete
production Workers or retained legacy rollback resources. production Workers or retained legacy rollback resources.
3. Revoke/delete the Decap OAuth application. 3. Revoke/delete the Decap OAuth application.
+3 -3
View File
@@ -1,15 +1,15 @@
backend: backend:
name: gitea name: gitea
repo: SloanOps/3031023-xyz repo: SloanOps/3031023-xyz
branch: cms-pilot branch: cms
app_id: c07dba05-eb09-48f0-8da4-6df775a13995 app_id: c07dba05-eb09-48f0-8da4-6df775a13995
api_root: https://git.sloanops.com/api/v1 api_root: https://git.sloanops.com/api/v1
base_url: https://git.sloanops.com base_url: https://git.sloanops.com
auth_endpoint: https://git.sloanops.com/login/oauth/authorize auth_endpoint: https://git.sloanops.com/login/oauth/authorize
publish_mode: simple publish_mode: simple
site_url: https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev site_url: https://cms-3031023-xyz-www.ksloan90.workers.dev
display_url: https://cms-pilot-3031023-xyz-www.ksloan90.workers.dev display_url: https://cms-3031023-xyz-www.ksloan90.workers.dev
show_preview_links: false show_preview_links: false
media_folder: public/images/uploads media_folder: public/images/uploads
+2 -2
View File
@@ -17,11 +17,11 @@ function expectSubset(actual, expected, path) {
const configPath = 'public/admin/config.yml'; const configPath = 'public/admin/config.yml';
const config = yaml.load(await readFile(configPath, 'utf8')); 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 = { const expectedBackend = {
name: 'gitea', name: 'gitea',
repo: 'SloanOps/3031023-xyz', repo: 'SloanOps/3031023-xyz',
branch: 'cms-pilot', branch: 'cms',
api_root: 'https://git.sloanops.com/api/v1', api_root: 'https://git.sloanops.com/api/v1',
base_url: 'https://git.sloanops.com', base_url: 'https://git.sloanops.com',
auth_endpoint: 'https://git.sloanops.com/login/oauth/authorize', auth_endpoint: 'https://git.sloanops.com/login/oauth/authorize',