Record completed internal CMS rollout
This commit is contained in:
@@ -1,24 +1,66 @@
|
|||||||
# Happy Tails Astro pilot
|
# Happy Tails Astro sites and internal CMS
|
||||||
|
|
||||||
This repository is a static Astro pilot for `3031023.xyz`. The existing
|
This repository backs the production Happy Tails sites at `3031023.xyz` and
|
||||||
Cloudflare Pages site remains the rollback target while this branch is tested.
|
`blog.3031023.xyz`. Both are static Astro builds deployed as separate
|
||||||
|
Cloudflare Workers. The retained Cloudflare Pages project remains the primary
|
||||||
|
site rollback target.
|
||||||
|
|
||||||
The planned preview-only editing experiment is documented in the [Decap CMS
|
The Decap CMS workflow is operational for internal SloanOps use. It is not a
|
||||||
Gitea pilot](docs/decap-cms-pilot.md). It does not authorize CMS activation, a
|
public feature or a client-editing offer. The approved internal editor is
|
||||||
production merge, or a domain change.
|
`sloanops.admin`; do not invite additional editors without a separate access,
|
||||||
|
support, and security review.
|
||||||
|
|
||||||
## Repository layout
|
## Repository layout
|
||||||
|
|
||||||
- `sites/www/` is the primary-site content location; it serves `3031023.xyz`.
|
- `sites/www/` contains primary-site Markdown and site data.
|
||||||
- `sites/blog/` is the separate `blog.3031023.xyz` sample site.
|
- `sites/blog/` contains the Journal Markdown and site data.
|
||||||
- `sites.json` is the operator-controlled manifest. It is not public intake.
|
- `public/admin/` contains the shared, no-index Decap admin shell and config.
|
||||||
|
- `public/images/uploads/` is the only CMS-writable media directory.
|
||||||
|
- `sites.json` is the operator-controlled build manifest, not public intake.
|
||||||
- `src/` contains shared Astro presentation code.
|
- `src/` contains shared Astro presentation code.
|
||||||
- `schemas/` and `fixtures/` provide the Phase 1 JSON contract proof.
|
- `schemas/`, `scripts/`, and `fixtures/` enforce the content and deployment
|
||||||
|
contracts.
|
||||||
|
|
||||||
Blog posts and primary-site FAQs are now static Markdown collections with
|
## Internal CMS workflow
|
||||||
validated frontmatter and draft filtering. Production CMS publishing, public
|
|
||||||
sign-up changes, n8n changes, and onboarding-worker changes remain out of
|
Use `https://3031023.xyz/admin/` for normal internal editing. Decap commits
|
||||||
scope; the linked Decap plan is a preview-only editing experiment.
|
content and approved raster media directly to the protected `cms` staging
|
||||||
|
branch; it never writes to `main`.
|
||||||
|
|
||||||
|
1. Make and review content changes on `cms`.
|
||||||
|
2. Confirm the `cms` Worker version previews and content validation results.
|
||||||
|
3. Open a pull request from `cms` to `main`.
|
||||||
|
4. Merge only after the required CMS validation, both site builds, and SloanOps
|
||||||
|
review pass.
|
||||||
|
5. Relevant `main` changes deploy the production primary and/or blog Worker.
|
||||||
|
6. Synchronize the resulting `main` merge commit back into `cms` through a
|
||||||
|
protected-branch pull request before the next editing cycle.
|
||||||
|
|
||||||
|
Operator-authored CMS application or configuration work also starts from the
|
||||||
|
current `cms` state. Use a short branch and a pull request into protected
|
||||||
|
`cms`, then promote the reviewed `cms` state to `main`. Direct protected-file
|
||||||
|
pushes to `cms` and all direct pushes to `main` are intentionally rejected.
|
||||||
|
|
||||||
|
Only the stable CMS and production admin origins are registered with Gitea.
|
||||||
|
Logging in from a branch-specific Worker preview alias therefore produces
|
||||||
|
`Unregistered Redirect URI`; this is expected. Never solve it with a wildcard
|
||||||
|
OAuth redirect or wildcard CORS origin. Use branch previews to inspect rendered
|
||||||
|
site output and an approved admin origin to test editing.
|
||||||
|
|
||||||
|
See the [CMS operating record](docs/decap-cms-pilot.md) and
|
||||||
|
[Gitea settings](docs/decap-cms-gitea-setup.md) for the complete boundaries.
|
||||||
|
|
||||||
|
## Content and media behavior
|
||||||
|
|
||||||
|
The CMS exposes primary FAQs, services, team members, fixed primary page
|
||||||
|
images, and Journal posts. Draft content is excluded from pages and sitemaps.
|
||||||
|
Journal publish and optional updated dates use Decap's supported `datetime`
|
||||||
|
widget in date-only UTC mode; new posts default the publish date to the current
|
||||||
|
UTC date.
|
||||||
|
|
||||||
|
CMS image sources must be approved JPG, PNG, WebP, or AVIF files in the flat
|
||||||
|
uploads directory and may be at most 25 MiB. Every build re-encodes public
|
||||||
|
copies to at most 1920 pixels and 500 KiB and strips embedded metadata.
|
||||||
|
|
||||||
## Local checks
|
## Local checks
|
||||||
|
|
||||||
@@ -37,25 +79,18 @@ Each selected site builds to `dist/<site-key>/`. The Worker configurations use
|
|||||||
only static assets and a custom 404 page. They contain no custom domains,
|
only static assets and a custom 404 page. They contain no custom domains,
|
||||||
routes, account IDs, or secrets.
|
routes, account IDs, or secrets.
|
||||||
|
|
||||||
`npm run verify:cms` checks the fixed Gitea branch/backend, the four Markdown
|
`npm run verify:cms` checks the fixed Gitea backend and `cms` branch, all CMS
|
||||||
content contracts, the fixed page-image record, the bounded raster-media boundary, the pinned admin
|
content contracts, media boundaries, the pinned admin shell, supported
|
||||||
shell, and the stable secret-free pull-request status context. The live steps
|
widgets, and the stable secret-free pull-request status context.
|
||||||
remain unapplied; see the [Gitea pilot settings](docs/decap-cms-gitea-setup.md)
|
|
||||||
for the exact review checklist.
|
|
||||||
|
|
||||||
## Preview deployment
|
## Preview deployment
|
||||||
|
|
||||||
The Gitea workflows upload scoped non-production preview versions of the
|
Non-`main` pushes upload no-index Cloudflare Worker versions with readable
|
||||||
production Workers from every non-`main` branch. Cloudflare assigns each branch
|
branch aliases; they do not move production traffic. Relevant `main` pushes
|
||||||
a readable `workers.dev` preview alias without changing production traffic.
|
deploy `3031023-xyz-www` and/or `3031023-xyz-blog` automatically after building
|
||||||
Relevant primary-site pushes to `main` deploy `3031023-xyz-www`, and relevant
|
and verifying the selected site. Routine workflows contain no DNS, custom
|
||||||
blog pushes to `main` deploy `3031023-xyz-blog`, automatically. Both production
|
domain, route, account ID, or token configuration.
|
||||||
workflows deploy static assets only.
|
|
||||||
It uses the existing `CF_API_TOKEN` and `CF_ACCOUNT_ID`. The token retains the
|
|
||||||
Pages permissions needed for the `main` rollback target and now also permits
|
|
||||||
the non-production Worker preview deployment.
|
|
||||||
Routine jobs do not configure DNS or custom domains.
|
|
||||||
|
|
||||||
Production hostname attachment and the `www` redirect are manual,
|
The production domain bindings, `www` redirect, retained Pages rollback, and
|
||||||
operator-controlled cutover steps. See `docs/pilot-baseline.md` for the
|
recovery steps are recorded in the [primary cutover runbook](docs/cutover-runbook.md)
|
||||||
rollback record.
|
and [blog production runbook](docs/blog-production-runbook.md).
|
||||||
|
|||||||
+15
-7
@@ -19,13 +19,21 @@ Phase 6 adds two validated Markdown collections:
|
|||||||
homepage.
|
homepage.
|
||||||
|
|
||||||
Both collections require frontmatter and support `draft: true`. Draft entries
|
Both collections require frontmatter and support `draft: true`. Draft entries
|
||||||
are excluded from the rendered pages and sitemap. This is a Git-backed pilot;
|
are excluded from the rendered pages and sitemap. The internal Decap CMS now
|
||||||
it does not add a CMS or client-editing access.
|
edits these collections through the protected `cms` staging branch; client and
|
||||||
|
public editing remain disabled.
|
||||||
|
|
||||||
Images must live under `public/images/`, be referenced with descriptive alt
|
Journal `pubDate` and optional `updatedDate` fields use Decap's supported
|
||||||
text where rendered, and remain at or below the 500 KiB pilot limit. The build
|
`datetime` control in date-only UTC mode. New posts default `pubDate` to the
|
||||||
will reject malformed site JSON and oversize source images.
|
current UTC date. Repository verification rejects the unsupported `date`
|
||||||
|
widget so the missing-control failure cannot silently return.
|
||||||
|
|
||||||
|
CMS images must live in the flat `public/images/uploads/` directory and be
|
||||||
|
referenced with descriptive alt text where rendered. Approved source rasters
|
||||||
|
may be at most 25 MiB; each build produces a metadata-free public copy no
|
||||||
|
larger than 1920 pixels or 500 KiB. The build rejects malformed site JSON,
|
||||||
|
disallowed image signatures, nested upload paths, and oversized sources.
|
||||||
|
|
||||||
Each selected build also generates `sitemap.xml` and `robots.txt`. Preview
|
Each selected build also generates `sitemap.xml` and `robots.txt`. Preview
|
||||||
builds disallow indexing; a future approved production build must set
|
builds disallow indexing; production workflows set `DEPLOY_ENV=production` to
|
||||||
`DEPLOY_ENV=production` to publish crawlable robots rules and the sitemap URL.
|
publish crawlable robots rules and the sitemap URL.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Decap CMS: Gitea settings
|
# Decap CMS: Gitea settings
|
||||||
|
|
||||||
Status: the preview-only pilot was accepted for promotion on 2026-09-04. The
|
Status: applied and verified for internal use on 2026-09-04. The repository
|
||||||
repository now targets the permanent `cms` staging branch. Update the exact
|
targets the permanent `cms` staging branch, the CMS application is deployed
|
||||||
CORS origin and PKCE OAuth redirect only after its first preview deployment
|
from `main`, and `sloanops.admin` is the approved internal editor. Client or
|
||||||
confirms the predicted alias. Nothing has been merged to `main`.
|
public access remains out of scope.
|
||||||
|
|
||||||
The repository implementation targets these fixed public values:
|
The repository implementation targets these fixed public values:
|
||||||
|
|
||||||
@@ -14,7 +14,8 @@ The repository implementation targets these fixed public values:
|
|||||||
| 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-3031023-xyz-www.ksloan90.workers.dev` |
|
| Stable CMS origin | `https://cms-3031023-xyz-www.ksloan90.workers.dev` |
|
||||||
| Exact OAuth redirect URI | `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` |
|
| Production admin origin | `https://3031023.xyz` |
|
||||||
|
| Exact OAuth redirects | `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` and `https://3031023.xyz/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 permanent branch names are intentionally slash-free. Decap
|
The pilot and permanent branch names are intentionally slash-free. Decap
|
||||||
@@ -24,10 +25,10 @@ presented as an empty collection. The permanent `cms` branch is still a
|
|||||||
non-production staging branch: changes reach `main` only through the protected
|
non-production staging branch: changes reach `main` only through the protected
|
||||||
pull-request flow.
|
pull-request flow.
|
||||||
|
|
||||||
The Workers alias was confirmed after the first approved preview deployment.
|
The stable Worker alias and production admin route were confirmed. If either
|
||||||
If Cloudflare later assigns a different stable origin,
|
origin changes, update repository configuration, CORS, and OAuth together
|
||||||
update the repository configuration, CORS origin, and OAuth redirect URI
|
through review. Branch-specific preview aliases are intentionally not
|
||||||
together through review before enabling login.
|
registered and will return `Unregistered Redirect URI` during login.
|
||||||
|
|
||||||
## Runner cache settings
|
## Runner cache settings
|
||||||
|
|
||||||
@@ -53,24 +54,23 @@ action branches on every job; npm packages remain keyed separately by
|
|||||||
## Applied CORS policy
|
## Applied CORS policy
|
||||||
|
|
||||||
The tracked Gitea service in
|
The tracked Gitea service in
|
||||||
`/home/sloan/gitRepos/SloanOps/infrastructure/docker-compose.yml` contains
|
`/home/sloan/gitRepos/SloanOps/infrastructure/docker-compose.yml` was deployed
|
||||||
these entries in the existing `gitea.environment` list and was deployed
|
with exact origins for the internal CMS applications. The two origins relevant
|
||||||
through the normal infrastructure change process:
|
to this repository are represented by these settings:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- GITEA__cors__ENABLED=true
|
- GITEA__cors__ENABLED=true
|
||||||
- GITEA__cors__ALLOW_DOMAIN=https://cms-3031023-xyz-www.ksloan90.workers.dev
|
- GITEA__cors__ALLOW_DOMAIN=https://cms-3031023-xyz-www.ksloan90.workers.dev,https://3031023.xyz
|
||||||
- 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
|
||||||
- GITEA__cors__HEADERS=Authorization,Content-Type
|
- GITEA__cors__HEADERS=Authorization,Content-Type
|
||||||
```
|
```
|
||||||
|
|
||||||
This is one exact origin, no wildcard, no cookie-bearing cross-origin request,
|
These are exact origins, not wildcards, and API requests do not carry cookies.
|
||||||
and only the API methods and non-safelisted request headers used by the Gitea
|
Do not add arbitrary Worker preview hosts or localhost. Gitea's same-origin
|
||||||
backend. Do not add the production domains, the journal preview, arbitrary
|
login page continues to use its own session cookie; Decap API calls use the
|
||||||
Workers preview hosts, or localhost. Gitea's same-origin login page continues
|
OAuth bearer token.
|
||||||
to use its own session cookie; Decap API calls use the OAuth bearer token.
|
|
||||||
|
|
||||||
After an approved Gitea deployment, verify a preflight without including a
|
After an approved Gitea deployment, verify a preflight without including a
|
||||||
token or cookie:
|
token or cookie:
|
||||||
@@ -94,11 +94,11 @@ CORS response were verified:
|
|||||||
|
|
||||||
- Application name: `3031023.xyz Decap CMS`.
|
- 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 URIs: exactly
|
||||||
`https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/`, including the
|
`https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` and
|
||||||
trailing slash.
|
`https://3031023.xyz/admin/`, including trailing slashes.
|
||||||
- Do not add wildcards, localhost, either production hostname, or the journal
|
- Do not add wildcards, localhost, the journal hostname, or branch-specific
|
||||||
preview as additional redirects.
|
Worker preview aliases as additional redirects.
|
||||||
- Copy only the public client ID into `backend.app_id` in
|
- Copy only the public client ID into `backend.app_id` in
|
||||||
`public/admin/config.yml` through a reviewed commit.
|
`public/admin/config.yml` through a reviewed commit.
|
||||||
- Do not store or use a client secret in the repository, Actions, Cloudflare,
|
- Do not store or use a client secret in the repository, Actions, Cloudflare,
|
||||||
@@ -158,6 +158,11 @@ merge, administrator, or bypass allowlists.
|
|||||||
| Protection bypass allowlist | Enabled only for the named SloanOps operator user/team |
|
| Protection bypass allowlist | Enabled only for the named SloanOps operator user/team |
|
||||||
| CMS/client account | Write access, but absent from every allowlist |
|
| CMS/client account | Write access, but absent from every allowlist |
|
||||||
|
|
||||||
|
Do not assume a local Git identity can use the operator bypass. A direct
|
||||||
|
protected-file push was rejected during the final Journal repair sync, and the
|
||||||
|
successful path was a short branch followed by PR #11 into `cms`. Prefer that
|
||||||
|
reviewable path for all operator-authored CMS code and configuration changes.
|
||||||
|
|
||||||
Enter this exact single value in **Unprotected file patterns**:
|
Enter this exact single value in **Unprotected file patterns**:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -197,18 +202,22 @@ Workflows, source, scripts, package files, schemas, manifests, Wrangler
|
|||||||
configuration, `site.json` files, and the CMS application therefore cannot be
|
configuration, `site.json` files, and the CMS application therefore cannot be
|
||||||
altered by choosing another branch name.
|
altered by choosing another branch name.
|
||||||
|
|
||||||
## Verification before an editor invitation
|
## Routine verification and promotion
|
||||||
|
|
||||||
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` from the reviewed baseline only after approval.
|
2. For CMS application/configuration work, branch from current `cms`, open a
|
||||||
3. Apply both protection rules together and confirm `main` is priority 1 and
|
pull request back into `cms`, and review the no-index version previews.
|
||||||
`*` is priority 2.
|
The required CMS status is scoped to pull requests into `main`, so the local
|
||||||
4. Use the same non-operator account intended for Decap to prove allowed
|
full check is mandatory before this staging merge.
|
||||||
Markdown edits succeed and every protected-path/direct-`main` attempt fails.
|
3. Open `cms` to `main` and confirm Gitea reports the exact required status
|
||||||
5. Open, but do not merge, a pull request into `main`. Confirm Gitea reports
|
context above and blocks merge without its successful result and current
|
||||||
the exact required status context above and blocks merge without both its
|
SloanOps approval.
|
||||||
successful result and a current SloanOps approval.
|
4. Merge deliberately, then confirm each affected production Worker workflow
|
||||||
6. Confirm both branch previews remain no-index and production is unchanged.
|
succeeds and smoke-test the public routes.
|
||||||
|
5. Merge the resulting `main` commit back into `cms` through another
|
||||||
|
protected-branch pull request if the histories diverge.
|
||||||
|
6. Confirm previews remain no-index and never expand OAuth/CORS for a temporary
|
||||||
|
branch alias.
|
||||||
|
|
||||||
Do not treat a Decap UI restriction as authorization. Gitea's first-match
|
Do not treat a Decap UI restriction as authorization. Gitea's first-match
|
||||||
branch rules are the enforcement boundary, and the pull-request build is the
|
branch rules are the enforcement boundary, and the pull-request build is the
|
||||||
|
|||||||
+84
-50
@@ -1,19 +1,21 @@
|
|||||||
# Decap CMS Gitea pilot and promotion
|
# Decap CMS Gitea pilot and promotion
|
||||||
|
|
||||||
Status: the preview-only pilot was accepted for promotion to the permanent
|
Status: completed and operational for internal SloanOps use as of 2026-09-04.
|
||||||
`cms` staging branch on 2026-09-04. Repository configuration now targets the
|
The reviewed CMS application is deployed from `main`, Decap writes only to the
|
||||||
predicted stable `cms` preview origin. CORS and OAuth must be changed together
|
protected `cms` staging branch, and the final Journal date-control repair is
|
||||||
only after that first preview confirms the alias. Nothing has been merged to
|
present on both branches. Production deployments from the approved merge
|
||||||
`main`, and production remains unchanged.
|
completed successfully.
|
||||||
|
|
||||||
|
This remains an internal capability for `sloanops.admin`, not a public feature
|
||||||
|
or a client-editing offer. No client account has been invited.
|
||||||
|
|
||||||
Updated: 2026-09-04
|
Updated: 2026-09-04
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
Use the `3031023.xyz` demonstration repository to determine whether
|
Record and operate the Decap CMS editing interface for both sites in the
|
||||||
Decap CMS can provide a useful editing interface for all sites owned under one
|
`3031023.xyz` demonstration repository without allowing an editor to publish
|
||||||
root domain without allowing an editor to publish directly to production or
|
directly to production or modify deployment code.
|
||||||
modify deployment code.
|
|
||||||
|
|
||||||
The pilot covers both the primary Happy Tails site and its journal subdomain.
|
The pilot covers both the primary Happy Tails site and its journal subdomain.
|
||||||
That matches the SloanOps repository boundary: one private repository per root
|
That matches the SloanOps repository boundary: one private repository per root
|
||||||
@@ -21,8 +23,9 @@ domain, with independently built sites beneath `sites/`. A future client who
|
|||||||
owns the root-domain site also owns the hosted subdomains represented in that
|
owns the root-domain site also owns the hosted subdomains represented in that
|
||||||
repository.
|
repository.
|
||||||
|
|
||||||
This plan does not authorize a production merge, domain change, contact-form
|
The completed internal rollout does not authorize public discovery, a client
|
||||||
activation, repository invitation, or removal of a rollback resource.
|
repository invitation, contact-form changes, a broader OAuth grant, or removal
|
||||||
|
of a rollback resource.
|
||||||
|
|
||||||
## Current evidence
|
## Current evidence
|
||||||
|
|
||||||
@@ -41,6 +44,13 @@ activation, repository invitation, or removal of a rollback resource.
|
|||||||
writes directly to the configured branch.
|
writes directly to the configured branch.
|
||||||
- The Gitea backend does not support Git LFS. Pilot media must remain small,
|
- The Gitea backend does not support Git LFS. Pilot media must remain small,
|
||||||
ordinary repository assets.
|
ordinary repository assets.
|
||||||
|
- The approved `sloanops.admin` account can authenticate at the production
|
||||||
|
admin route, load all collections, and write through the `cms` branch.
|
||||||
|
- Gitea rejects protected-file pushes to `cms` and direct pushes to `main`;
|
||||||
|
operator code/configuration changes therefore use pull requests as well.
|
||||||
|
- Arbitrary branch-preview hostnames are deliberately absent from the OAuth
|
||||||
|
redirect and CORS allowlists. Login from one returns `Unregistered Redirect
|
||||||
|
URI`; rendered branch previews remain usable without expanding OAuth.
|
||||||
|
|
||||||
References:
|
References:
|
||||||
|
|
||||||
@@ -56,8 +66,8 @@ recorded in [Decap CMS pilot: Gitea settings](decap-cms-gitea-setup.md).
|
|||||||
|
|
||||||
## Repository implementation
|
## Repository implementation
|
||||||
|
|
||||||
The reviewed pilot implementation is the baseline for the permanent `cms`
|
The reviewed production implementation and permanent `cms` staging branch
|
||||||
staging branch:
|
provide the following controls:
|
||||||
|
|
||||||
- `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.
|
||||||
@@ -68,6 +78,10 @@ staging branch:
|
|||||||
- 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
|
||||||
text-first; embedded images and general file widgets are absent.
|
text-first; embedded images and general file widgets are absent.
|
||||||
|
- Journal publish and optional updated dates use the supported Decap
|
||||||
|
`datetime` widget in date-only UTC mode. New posts default to the current UTC
|
||||||
|
date, and the verifier rejects the unsupported `date` widget that caused the
|
||||||
|
original create/update failures.
|
||||||
- A fixed image-only Markdown record controls the primary home, About,
|
- A fixed image-only Markdown record controls the primary home, About,
|
||||||
Services, and Contact page imagery without exposing either `site.json`.
|
Services, and Contact page imagery without exposing either `site.json`.
|
||||||
- Team and page-image fields accept optional bounded uploads. Editors can add,
|
- Team and page-image fields accept optional bounded uploads. Editors can add,
|
||||||
@@ -94,11 +108,11 @@ staging branch:
|
|||||||
repository cache without trusting a mutable tag or refreshing GitHub on every
|
repository cache without trusting a mutable tag or refreshing GitHub on every
|
||||||
job.
|
job.
|
||||||
|
|
||||||
The pilot dependencies were verified on `cms-pilot`. The same protection rules
|
The pilot dependencies were verified on `cms-pilot`, promoted to the permanent
|
||||||
apply to `cms`; its exact-origin CORS and OAuth values are the remaining
|
`cms` branch, and deployed to `main`. Exact-origin CORS, PKCE OAuth, branch
|
||||||
operator-controlled promotion step.
|
protection, previews, validation, and production deployment are all applied.
|
||||||
|
|
||||||
## Proposed pilot architecture
|
## Operating architecture
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Decap at /admin/
|
Decap at /admin/
|
||||||
@@ -126,16 +140,17 @@ 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.
|
||||||
|
|
||||||
Predicted preview URLs for branch `cms`:
|
Confirmed stable preview URLs for branch `cms`:
|
||||||
|
|
||||||
- `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/`
|
- `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/`
|
||||||
- `https://cms-3031023-xyz-www.ksloan90.workers.dev/`
|
- `https://cms-3031023-xyz-www.ksloan90.workers.dev/`
|
||||||
- `https://cms-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
|
The production admin entry is `https://3031023.xyz/admin/`. Use the stable
|
||||||
these predicted URLs as authoritative.
|
`cms` admin origin or the production admin entry for interactive CMS testing;
|
||||||
|
do not use arbitrary branch aliases for OAuth login.
|
||||||
|
|
||||||
## Initial Decap scope
|
## Internal Decap scope
|
||||||
|
|
||||||
Allow editing and creation only within these collections:
|
Allow editing and creation only within these collections:
|
||||||
|
|
||||||
@@ -248,34 +263,30 @@ 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` branch, SloanOps creates the pull
|
Because Decap writes to a fixed `cms` branch, SloanOps creates the pull request
|
||||||
request in Gitea. After an approved merge, synchronize the CMS branch with
|
from `cms` to `main` in Gitea. Internal CMS application/configuration changes
|
||||||
`main` before the next editing cycle. Do not automate production merge.
|
start from the current `cms` state, enter protected `cms` through an
|
||||||
|
operator-reviewed pull request, and are then promoted with the staged content.
|
||||||
|
After an approved `main` merge, synchronize its merge commit back into `cms`
|
||||||
|
through a protected-branch pull request before the next editing cycle. Do not
|
||||||
|
automate production merge.
|
||||||
|
|
||||||
The accepted pilot is promoted by replacing the disposable branch with the
|
The accepted pilot is promoted by replacing the disposable branch with the
|
||||||
permanent, slash-free `cms` branch. It remains a preview/staging branch and
|
permanent, slash-free `cms` branch. It remains a preview/staging branch and
|
||||||
retains the same pull-request, validation, and approval path into `main`.
|
retains the same pull-request, validation, and approval path into `main`.
|
||||||
|
|
||||||
## Gitea and Decap setup
|
## Routine operating sequence
|
||||||
|
|
||||||
1. Add a non-confidential Gitea OAuth application for the exact preview
|
1. Edit through an approved admin origin and confirm the commit lands only on
|
||||||
`/admin/` redirect URI or URIs. Store only the public client ID in Decap.
|
`cms` in an allowed content or media path.
|
||||||
2. Enable Gitea API CORS for the exact approved preview origin or origins.
|
2. Confirm the affected no-index Worker version preview succeeds.
|
||||||
Allow only required methods and headers, including `Authorization` and
|
3. Run the local CMS verifier and both site build/verifier pairs before any
|
||||||
`Content-Type`; do not use a wildcard origin.
|
operator-authored CMS application or configuration PR into `cms`.
|
||||||
3. Create `cms` from the reviewed repository baseline.
|
4. Review the complete staged diff and open `cms` to `main`.
|
||||||
4. Review the pinned Decap admin assets beneath `public/admin/`. Do not replace
|
5. Require the stable secret-free validation status and SloanOps approval.
|
||||||
the exact version and integrity value with a floating range.
|
6. Merge deliberately and confirm the affected production Worker jobs pass.
|
||||||
5. Configure the Gitea backend for `SloanOps/3031023-xyz` and branch
|
7. Synchronize the resulting `main` merge commit back into protected `cms`
|
||||||
`cms`.
|
before another editing cycle.
|
||||||
6. Model every persisted frontmatter field with limits matching
|
|
||||||
`src/content.config.ts`. Do not let a CMS save silently remove an existing
|
|
||||||
field.
|
|
||||||
7. Apply and verify the `main` and fallback branch protections before inviting
|
|
||||||
or testing a non-operator editor.
|
|
||||||
8. Add the secret-free pull-request validation workflow.
|
|
||||||
9. Push only the review branch and confirm both no-index preview builds before
|
|
||||||
any CMS login test.
|
|
||||||
|
|
||||||
## Test matrix
|
## Test matrix
|
||||||
|
|
||||||
@@ -305,13 +316,36 @@ not record OAuth tokens or Cloudflare credentials.
|
|||||||
| 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. |
|
||||||
|
|
||||||
Do not merge DC-15 during the initial infrastructure and editing test. A later
|
The initial no-merge restriction applied only while the pilot was being
|
||||||
harmless demo-content merge requires explicit approval and repeats both public
|
evaluated. Promotion is now allowed through the protected workflow above; each
|
||||||
site checks after deployment.
|
release still requires explicit review and repeats both public site checks.
|
||||||
|
|
||||||
## Acceptance criteria
|
## Completion record — 2026-09-04
|
||||||
|
|
||||||
Accept the pilot only when:
|
- PR #1 launched the no-index Decap shell and Gitea-backed collections.
|
||||||
|
- PRs #2 and #3 tightened image filename validation and added bounded,
|
||||||
|
metadata-stripping build optimization.
|
||||||
|
- PRs #4 and #5 synchronized the reviewed implementation into `cms` and then
|
||||||
|
promoted it to `main`.
|
||||||
|
- Disposable Journal, page-image, and FAQ edits exposed workflow and schema
|
||||||
|
behavior; the test content was reverted and the temporary FAQ was removed.
|
||||||
|
- PR #9 consolidated the rollback history and resynchronized the branches.
|
||||||
|
- Failed Journal content runs were traced to Decap's unsupported `date` widget.
|
||||||
|
PR #10 replaced it with date-only `datetime` controls, defaulted new publish
|
||||||
|
dates, and added a verifier regression guard.
|
||||||
|
- PR #11 synchronized the final repair into protected `cms` after the direct
|
||||||
|
protected-file push was correctly rejected.
|
||||||
|
- CMS validation and both site builds passed locally. The PR #10 required
|
||||||
|
validation passed, and production blog run 342 plus primary run 343 both
|
||||||
|
completed successfully.
|
||||||
|
- The deployed Journal editor was opened at `3031023.xyz/admin/`; Publish date
|
||||||
|
displayed a populated UTC date and Updated date displayed an optional date
|
||||||
|
control. No post was saved during this final smoke test.
|
||||||
|
|
||||||
|
## Operating acceptance boundary
|
||||||
|
|
||||||
|
The internal pilot was accepted against these controls. Re-evaluate them before
|
||||||
|
any client rollout:
|
||||||
|
|
||||||
- both site families are understandable in one CMS;
|
- both site families are understandable in one CMS;
|
||||||
- content edits preserve every modeled field and pass Astro validation;
|
- content edits preserve every modeled field and pass Astro validation;
|
||||||
@@ -329,7 +363,7 @@ account control over executable deployment files.
|
|||||||
|
|
||||||
## Rollback and cleanup
|
## Rollback and cleanup
|
||||||
|
|
||||||
If the pilot is rejected or paused:
|
If the internal CMS is paused:
|
||||||
|
|
||||||
1. Revert or delete disposable changes on `cms`.
|
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
|
||||||
@@ -341,7 +375,7 @@ If the pilot is rejected or paused:
|
|||||||
6. Remove or retain branch protections deliberately; never weaken `main` as
|
6. Remove or retain branch protections deliberately; never weaken `main` as
|
||||||
cleanup.
|
cleanup.
|
||||||
|
|
||||||
## Implementation estimate
|
## Historical implementation estimate
|
||||||
|
|
||||||
Expected hands-on implementation time: **6–9 hours**, normally one focused
|
Expected hands-on implementation time: **6–9 hours**, normally one focused
|
||||||
working day. Budget up to two elapsed days if OAuth/CORS behavior or Gitea
|
working day. Budget up to two elapsed days if OAuth/CORS behavior or Gitea
|
||||||
|
|||||||
+26
-20
@@ -1,24 +1,30 @@
|
|||||||
# Preview deployment and later cutover controls
|
# Worker version previews
|
||||||
|
|
||||||
The preview workflow deploys static output only. It has two deliberately
|
Non-`main` pushes build and verify the affected site, then upload a
|
||||||
separate Worker names:
|
non-production version of its production Worker with a readable branch alias.
|
||||||
|
They do not deploy the version to production traffic.
|
||||||
|
|
||||||
| Site key | Preview Worker | Future hostname |
|
| Site key | Versioned Worker | Stable `cms` alias | Production hostname |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `www` | `3031023-xyz-www-preview` | `3031023.xyz` |
|
| `www` | `3031023-xyz-www` | `cms-3031023-xyz-www.ksloan90.workers.dev` | `3031023.xyz` |
|
||||||
| `blog` | `3031023-xyz-blog-preview` | `blog.3031023.xyz` |
|
| `blog` | `3031023-xyz-blog` | `cms-3031023-xyz-blog.ksloan90.workers.dev` | `blog.3031023.xyz` |
|
||||||
|
|
||||||
`workers.dev` is the only preview destination. Neither Wrangler configuration
|
The workflows derive the preview alias from the branch name, build with
|
||||||
contains a route, custom domain, zone ID, account ID, or token. Cloudflare
|
preview robots rules, run output verification, and call `wrangler versions
|
||||||
credentials remain Gitea repository secrets. The preview workflow uses the
|
upload`. They use the existing `CF_API_TOKEN` and `CF_ACCOUNT_ID` repository
|
||||||
existing `CF_API_TOKEN`, which has Account Workers Scripts edit/write for this
|
secrets but contain no DNS, routes, custom-domain mutations, account IDs, or
|
||||||
pilot plus the retained Pages rollback permissions. It uses `CF_ACCOUNT_ID` as
|
tokens in source.
|
||||||
the account selector. Do not grant DNS edit permissions for routine preview
|
|
||||||
deployment.
|
|
||||||
|
|
||||||
The primary Worker must pass its public preview test matrix before an operator
|
Only the stable CMS and production admin origins are registered with Gitea
|
||||||
records the exact Pages deployment/DNS state and requests production-cutover
|
OAuth/CORS. A CMS login attempt from a temporary branch alias is expected to
|
||||||
approval. A production deployment should use a separately named approved
|
fail with `Unregistered Redirect URI`; do not add a wildcard. Review rendered
|
||||||
Worker or an explicitly promoted Worker version; it must not be an ordinary
|
site output on the branch alias and use `https://3031023.xyz/admin/` or the
|
||||||
push job. Attaching `3031023.xyz`, configuring the `www.3031023.xyz` redirect,
|
stable `cms` admin origin for editing tests.
|
||||||
and attaching `blog.3031023.xyz` each remain manual approval-boundary actions.
|
|
||||||
|
Relevant pushes to `main` use separate production workflows and `wrangler
|
||||||
|
deploy` for the affected Worker after a production build and verifier pass.
|
||||||
|
The existing domain bindings do not change during routine deployments.
|
||||||
|
|
||||||
|
The older `*-preview` Wrangler configurations and Workers are retained as
|
||||||
|
rollback artifacts. Current branch workflows use Worker version previews of
|
||||||
|
the production-named Workers instead.
|
||||||
|
|||||||
Reference in New Issue
Block a user