Add preview-only Decap CMS pilot
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
# Decap CMS pilot: unapplied Gitea settings
|
||||
|
||||
Status: repository-side review specification only. Nothing in this document
|
||||
authorizes a Gitea restart, OAuth application creation, branch-protection
|
||||
change, branch push, preview deployment, or merge.
|
||||
|
||||
The repository implementation targets these fixed public values:
|
||||
|
||||
| Setting | Value |
|
||||
| --- | --- |
|
||||
| Gitea repository | `SloanOps/3031023-xyz` |
|
||||
| Decap write branch | `cms/pilot` |
|
||||
| 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/` |
|
||||
| Required status context | `CMS content validation / cms-content-validation (pull_request)` |
|
||||
|
||||
The predicted Workers alias must be confirmed after the first explicitly
|
||||
approved preview deployment. If Cloudflare assigns a different stable origin,
|
||||
update the repository configuration, CORS origin, and OAuth redirect URI
|
||||
together through review before enabling login.
|
||||
|
||||
## Unapplied CORS change
|
||||
|
||||
The tracked Gitea service in
|
||||
`/home/sloan/gitRepos/SloanOps/infrastructure/docker-compose.yml` currently has
|
||||
no `[cors]` environment overrides. After separate approval, add these entries
|
||||
to the existing `gitea.environment` list and redeploy only 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__METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
|
||||
- GITEA__cors__MAX_AGE=10m
|
||||
- GITEA__cors__ALLOW_CREDENTIALS=false
|
||||
- GITEA__cors__HEADERS=Authorization,Content-Type
|
||||
```
|
||||
|
||||
This is one exact origin, no wildcard, no cookie-bearing cross-origin request,
|
||||
and only the API methods and non-safelisted request headers used by the Gitea
|
||||
backend. Do not add the production domains, the journal preview, arbitrary
|
||||
Workers preview hosts, or localhost. Gitea's same-origin login page continues
|
||||
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
|
||||
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 'Access-Control-Request-Method: GET' \
|
||||
--header 'Access-Control-Request-Headers: authorization,content-type'
|
||||
```
|
||||
|
||||
The response must allow only the configured origin, requested method, and
|
||||
headers. Repeat with an unrelated origin and confirm that Gitea does not grant
|
||||
it cross-origin access. Also verify normal Git, web UI, API, and Actions
|
||||
behavior before continuing.
|
||||
|
||||
## OAuth application to create later
|
||||
|
||||
Create one Gitea OAuth2 application only after the stable preview origin and
|
||||
CORS response have been reviewed:
|
||||
|
||||
- Application name: `3031023.xyz Decap CMS pilot`.
|
||||
- Confidential client: **unchecked**. The browser client must use PKCE.
|
||||
- Redirect URI: exactly
|
||||
`https://cms-pilot-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.
|
||||
- Copy only the public client ID into `backend.app_id` in
|
||||
`public/admin/config.yml` through a reviewed commit.
|
||||
- Do not store or use a client secret in the repository, Actions, Cloudflare,
|
||||
or Decap configuration.
|
||||
|
||||
The CMS account still needs repository write access; the branch rules below
|
||||
are the security boundary that reduces that write access to pilot content and
|
||||
media paths.
|
||||
|
||||
## Branch protection rules to apply together
|
||||
|
||||
Create both rules before granting a CMS/client account write access or testing
|
||||
login. Gitea uses only the first matching rule, so the order is material.
|
||||
|
||||
### Priority 1: production `main`
|
||||
|
||||
| Gitea control | Required value |
|
||||
| --- | --- |
|
||||
| Protected branch pattern | `main` |
|
||||
| Priority | `1` (first/highest) |
|
||||
| Push | Disabled for everyone; no push allowlist |
|
||||
| Force push | Disabled |
|
||||
| Required approvals | `1` |
|
||||
| Approval allowlist | Enabled; only the named SloanOps operator user/team |
|
||||
| Dismiss stale approvals | Enabled |
|
||||
| Ignore stale approvals | Disabled |
|
||||
| Block rejected reviews | Enabled |
|
||||
| Block outstanding official review requests | Enabled |
|
||||
| Block outdated pull requests | Enabled |
|
||||
| Status checks | Enabled |
|
||||
| Status check pattern | `CMS content validation / cms-content-validation (pull_request)` |
|
||||
| Merge allowlist | Enabled; only the named SloanOps operator user/team |
|
||||
| Administrators must follow protection | Enabled |
|
||||
| Protection bypass allowlist | Disabled |
|
||||
|
||||
Leave both file-pattern fields empty on this rule. Direct pushes are already
|
||||
disabled, and executable or configuration changes must remain possible only in
|
||||
a separate operator-authored, reviewed pull request. The fallback rule prevents
|
||||
CMS users from placing those files on any source branch.
|
||||
|
||||
This rule means every `main` change arrives through a current pull request,
|
||||
passes the one secret-free job that validates the CMS contract and builds and
|
||||
verifies both sites, receives a current SloanOps approval, and is merged by a
|
||||
SloanOps operator. A CMS/client account must not be added to the approval,
|
||||
merge, administrator, or bypass allowlists.
|
||||
|
||||
### Priority 2: all-branch fallback
|
||||
|
||||
| Gitea control | Required value |
|
||||
| --- | --- |
|
||||
| Protected branch pattern | `*` |
|
||||
| Priority | `2` (after `main`) |
|
||||
| Push | Disabled, except the unprotected file patterns below |
|
||||
| Force push | Disabled |
|
||||
| Unprotected file patterns | Exact semicolon-separated value below |
|
||||
| Protected file patterns | Empty; every path omitted from the unprotected list remains protected |
|
||||
| Protection bypass allowlist | Enabled only for the named SloanOps operator user/team |
|
||||
| CMS/client account | Write access, but absent from every allowlist |
|
||||
|
||||
Enter this exact single value in **Unprotected file patterns**:
|
||||
|
||||
```text
|
||||
sites/www/content/**;sites/blog/content/**;public/images/uploads/*.jpg;public/images/uploads/*.jpeg;public/images/uploads/*.png;public/images/uploads/*.webp;public/images/uploads/*.avif
|
||||
```
|
||||
|
||||
Do not add a nested upload glob. The current Decap configuration writes media
|
||||
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
|
||||
on every differently named branch, including:
|
||||
|
||||
```text
|
||||
.gitea/**
|
||||
astro.config.mjs
|
||||
package.json
|
||||
package-lock.json
|
||||
schemas/**
|
||||
scripts/**
|
||||
src/**
|
||||
wrangler/**
|
||||
sites.json
|
||||
sites/*/site.json
|
||||
public/admin/**
|
||||
public/_headers
|
||||
public/images/*
|
||||
```
|
||||
|
||||
The final `public/images/*` line describes the effect of omission, not an
|
||||
additional Gitea field value: existing approved images stay protected while
|
||||
the explicitly listed `public/images/uploads/*.<raster>` paths remain writable.
|
||||
Workflows, source, scripts, package files, schemas, manifests, Wrangler
|
||||
configuration, `site.json` files, and the CMS application therefore cannot be
|
||||
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.
|
||||
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
|
||||
Markdown edits succeed and every protected-path/direct-`main` attempt fails.
|
||||
5. Open, but do not merge, a pull request into `main`. Confirm Gitea reports
|
||||
the exact required status context above and blocks merge without both its
|
||||
successful result and a current SloanOps approval.
|
||||
6. Confirm both branch previews remain no-index and production is unchanged.
|
||||
|
||||
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
|
||||
content/media integrity boundary.
|
||||
+39
-5
@@ -1,9 +1,10 @@
|
||||
# Decap CMS Gitea pilot
|
||||
|
||||
Status: planned; no CMS, OAuth application, Gitea CORS change, branch
|
||||
protection, or deployment has been enabled by this document.
|
||||
Status: repository implementation prepared for review; no OAuth application,
|
||||
Gitea CORS change, branch protection, branch push, preview deployment, or
|
||||
production merge has been performed.
|
||||
|
||||
Updated: 2026-09-03
|
||||
Updated: 2026-09-04
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -49,6 +50,39 @@ References:
|
||||
- <https://docs.gitea.com/usage/access-control/protected-branches/>
|
||||
- <https://github.com/decaporg/decap-cms/issues/7115>
|
||||
|
||||
The exact, unapplied Gitea CORS, OAuth, and two-rule branch-protection settings
|
||||
are recorded in [Decap CMS pilot: unapplied Gitea settings](decap-cms-gitea-setup.md).
|
||||
|
||||
## Repository implementation under review
|
||||
|
||||
The `review/decap-cms-pilot` branch now contains the repository-side portion of
|
||||
the pilot:
|
||||
|
||||
- `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`. Its public
|
||||
OAuth client ID remains an explicit placeholder until the separately
|
||||
approved non-confidential Gitea application exists.
|
||||
- All four Markdown collections and every persisted frontmatter field are
|
||||
modeled with the current Astro/Zod bounds. Markdown controls are text-first;
|
||||
image embedding and general file widgets are absent.
|
||||
- The team image field accepts an existing approved raster path or a bounded
|
||||
upload. URL insertion and SVG paths are rejected. Builds additionally verify
|
||||
the extension, raster signature, flat upload path, and 500 KiB limit.
|
||||
- `.gitea/workflows/cms-content-validation.yml` runs on every pull request into
|
||||
`main`, uses no secrets, and reports
|
||||
`CMS content validation / cms-content-validation (pull_request)` after
|
||||
checking the Decap contract and building and verifying both sites.
|
||||
- The existing branch preview workflows remain in place. Local output checks
|
||||
now prove preview robots rules and page metadata stay no-index and that the
|
||||
admin response has a no-index header rule.
|
||||
|
||||
This preparation does not make the CMS usable yet: the placeholder public
|
||||
client ID, unapplied CORS setting, absent `cms/pilot` remote branch, and absent
|
||||
branch rules are intentional review gates.
|
||||
|
||||
## Proposed pilot architecture
|
||||
|
||||
```text
|
||||
@@ -192,8 +226,8 @@ request in Gitea. After an approved merge, synchronize the CMS branch with
|
||||
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.
|
||||
4. Add pinned Decap admin assets beneath `public/admin/`. Do not load a floating
|
||||
major-version range from a CDN.
|
||||
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`.
|
||||
6. Model every persisted frontmatter field with limits matching
|
||||
|
||||
Reference in New Issue
Block a user