From 7be2992413f65993c79fa2927963cfc57e8b95e8 Mon Sep 17 00:00:00 2001 From: NewtTheWolf Date: Mon, 2 Nov 2026 13:26:29 +0100 Subject: [PATCH] Prepare plugin catalogs and Blacksmith source qualification --- .github/workflows/cms.yml | 82 +- .tabularium | 1 + README.md | 58 +- assets/README.md | 14 + assets/icon.svg | 1 + extensions/managed-cms/src/manifest.ts | 2 +- extensions/managed-cms/ui/cms.tsx | 4 +- packages/cms-ui/src/client.tsx | 6 +- packages/cms-ui/src/styles.css | 16 +- packages/i18n/compiled/de.json | 112 +- packages/i18n/locales/de/cms.po | 4 +- .../i18n/locales/de/customer-build-release.po | 62 + .../i18n/locales/de/customer-usage-release.po | 47 + packages/i18n/locales/de/waitlist-release.po | 32 + packages/i18n/locales/webdock.pot | 12280 ++++++++-------- screenshots/cms-draft-editor.png | Bin 0 -> 73534 bytes screenshots/cms-draft-mobile.png | Bin 0 -> 40690 bytes scripts/plugin-release/build_oci.py | 134 +- 18 files changed, 7064 insertions(+), 5791 deletions(-) create mode 100644 .tabularium create mode 100644 assets/README.md create mode 100644 assets/icon.svg create mode 100644 packages/i18n/locales/de/customer-build-release.po create mode 100644 packages/i18n/locales/de/customer-usage-release.po create mode 100644 packages/i18n/locales/de/waitlist-release.po create mode 100644 screenshots/cms-draft-editor.png create mode 100644 screenshots/cms-draft-mobile.png diff --git a/.github/workflows/cms.yml b/.github/workflows/cms.yml index 5c4bcdc..1a36b2f 100644 --- a/.github/workflows/cms.yml +++ b/.github/workflows/cms.yml @@ -1,3 +1,5 @@ +# Execution stays disabled until the operator records Blacksmith qualification. +# See webdock/tools/plugin-ci/BLACKSMITH.md; runner labels alone do not select a region. name: Independent CMS on: push: @@ -10,34 +12,94 @@ concurrency: cancel-in-progress: false jobs: check: - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository - runs-on: ${{ fromJSON(vars.WEBDOCK_PLUGIN_CI_RUNNER) }} + if: vars.WEBDOCK_BLACKSMITH_READY == 'true' && vars.WEBDOCK_BLACKSMITH_REGION != '' && vars.WEBDOCK_BLACKSMITH_BUILDER_READY == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + runs-on: blacksmith-4vcpu-ubuntu-2404 timeout-minutes: 35 + env: + WEBDOCK_OCI_BACKEND: blacksmith-buildx + WEBDOCK_BLACKSMITH_REGION: ${{ vars.WEBDOCK_BLACKSMITH_REGION }} steps: + - name: Require reviewed Blacksmith execution + shell: bash + env: + QUALIFIED_BLACKSMITH_REGION: ${{ vars.WEBDOCK_BLACKSMITH_REGION }} + run: | + set -euo pipefail + case "${BLACKSMITH_REGION:-}" in us-east|us-west|eu-central|eu-west) ;; *) echo 'Recorded Blacksmith region required' >&2; exit 1 ;; esac + test "$BLACKSMITH_REGION" = "$QUALIFIED_BLACKSMITH_REGION" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: persist-credentials: false + - name: Reject unexpected existing BuildKit before provider setup + shell: bash + run: | + set -euo pipefail + test ! -e buildkitd.toml + python3 scripts/plugin-release/build_oci.py --before-blacksmith-setup + - name: Set up the managed Blacksmith builder + uses: useblacksmith/setup-docker-builder@19215110ab936351210feebdfa5b440b4493e184 # reviewed v2 + with: + cache-key: Dockerfile + nofallback: true + buildx-version: v0.23.0 + platforms: linux/amd64 + - name: Verify provider builder and keep generated configuration outside source + shell: bash + run: | + set -euo pipefail + python3 scripts/plugin-release/build_oci.py --assert-blacksmith-builder + mv -- buildkitd.toml "$RUNNER_TEMP/blacksmith-buildkitd.toml" - name: Resolve source image identity shell: bash run: printf 'IMAGE_REPOSITORY=ghcr.io/%s\n' "${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" - - name: Check, unit test and build both components on the qualified EU builder - env: - WEBDOCK_BUILDKIT_ADDR: ${{ vars.WEBDOCK_BUILDKIT_ADDR }} - WEBDOCK_BUILDCTL_SHA256: ${{ vars.WEBDOCK_BUILDCTL_SHA256 }} + - name: Check, unit test and build both components on the qualified Blacksmith builder run: python3 scripts/plugin-release/build_oci.py --check < /dev/null release: needs: check - if: startsWith(github.ref, 'refs/tags/v') && github.event_name == 'push' - runs-on: ${{ fromJSON(vars.WEBDOCK_PLUGIN_CI_RUNNER) }} + if: vars.WEBDOCK_BLACKSMITH_READY == 'true' && vars.WEBDOCK_BLACKSMITH_REGION != '' && vars.WEBDOCK_BLACKSMITH_BUILDER_READY == 'true' && (startsWith(github.ref, 'refs/tags/v') && github.event_name == 'push') + runs-on: blacksmith-4vcpu-ubuntu-2404 timeout-minutes: 45 environment: plugin-release permissions: contents: write packages: write + env: + WEBDOCK_OCI_BACKEND: blacksmith-buildx + WEBDOCK_BLACKSMITH_REGION: ${{ vars.WEBDOCK_BLACKSMITH_REGION }} steps: + - name: Require reviewed Blacksmith execution + shell: bash + env: + QUALIFIED_BLACKSMITH_REGION: ${{ vars.WEBDOCK_BLACKSMITH_REGION }} + run: | + set -euo pipefail + case "${BLACKSMITH_REGION:-}" in us-east|us-west|eu-central|eu-west) ;; *) echo 'Recorded Blacksmith region required' >&2; exit 1 ;; esac + test "$BLACKSMITH_REGION" = "$QUALIFIED_BLACKSMITH_REGION" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: persist-credentials: false + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: 1.4.3 + - name: Reject unexpected existing BuildKit before provider setup + shell: bash + run: | + set -euo pipefail + test ! -e buildkitd.toml + python3 scripts/plugin-release/build_oci.py --before-blacksmith-setup + - name: Set up the managed Blacksmith builder + uses: useblacksmith/setup-docker-builder@19215110ab936351210feebdfa5b440b4493e184 # reviewed v2 + with: + cache-key: Dockerfile + nofallback: true + buildx-version: v0.23.0 + platforms: linux/amd64 + - name: Verify provider builder and keep generated configuration outside source + shell: bash + run: | + set -euo pipefail + python3 scripts/plugin-release/build_oci.py --assert-blacksmith-builder + mv -- buildkitd.toml "$RUNNER_TEMP/blacksmith-buildkitd.toml" - name: Require exact source version and preinstalled Bun shell: bash run: | @@ -50,8 +112,6 @@ jobs: shell: bash env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WEBDOCK_BUILDKIT_ADDR: ${{ vars.WEBDOCK_BUILDKIT_ADDR }} - WEBDOCK_BUILDCTL_SHA256: ${{ vars.WEBDOCK_BUILDCTL_SHA256 }} WEBDOCK_PLUGIN_DOCKERFILE: extensions/managed-cms/Dockerfile run: | set -euo pipefail @@ -61,8 +121,6 @@ jobs: shell: bash env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WEBDOCK_BUILDKIT_ADDR: ${{ vars.WEBDOCK_BUILDKIT_ADDR }} - WEBDOCK_BUILDCTL_SHA256: ${{ vars.WEBDOCK_BUILDCTL_SHA256 }} WEBDOCK_PLUGIN_DOCKERFILE: apps/cms-instance/Dockerfile run: | set -euo pipefail diff --git a/.tabularium b/.tabularium new file mode 100644 index 0000000..12111d0 --- /dev/null +++ b/.tabularium @@ -0,0 +1 @@ +{"name":"managed-cms","version":"0.1.0-alpha.1","kind":"extension","description":"Create independent website CMS instances and edit pages, drafts and media in Webdock.","category":"content","tags":["cms","content","websites"],"license":"AGPL-3.0-only","readme":"README.md","documentation_url":"https://github.com/spitzli/cloud-plugin-managed-cms/blob/HEAD/README.md","homepage":"https://github.com/spitzli/webdock/tree/main/extensions/managed-cms","protocolVersion":1,"sdkVersion":1,"dataProtocolVersion":1,"coreVersion":">=0.1.0 <1.0.0","image":"ghcr.io/spitzli/cloud-plugin-managed-cms@sha256:9fffb3662a4d4e87c5a54c43bea9070dd7162822708480997dded12467b1b025","dependencies":[],"capabilities":["plugin.apps.read","plugin.apps.provision","plugin.http.read","plugin.http.request"],"schemaVersion":1,"readableSchema":{"min":1,"max":1},"migrations":[],"tables":[],"services":{"required":[{"name":"http","version":1}],"optional":[{"name":"apps","version":1}]},"configuration":{"type":"object","properties":{},"required":[],"additionalProperties":false},"actions":[{"name":"cms.instances","intent":"read","capabilities":["plugin.apps.read"],"input":{"type":"object","properties":{},"required":[],"additionalProperties":false}},{"name":"cms.status","intent":"read","capabilities":["plugin.apps.read"],"input":{"type":"object","properties":{"provisioning_id":{"type":"string","minLength":1,"maxLength":20}},"required":["provisioning_id"],"additionalProperties":false}},{"name":"cms.create","intent":"write","capabilities":["plugin.apps.provision"],"input":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":160},"request_id":{"type":"string","minLength":1,"maxLength":128}},"required":["name","request_id"],"additionalProperties":false}},{"name":"cms.read","intent":"read","capabilities":["plugin.http.read"],"input":{"type":"object","properties":{"path":{"type":"string","minLength":1,"maxLength":2048}},"required":["path"],"additionalProperties":false},"httpBinding":"cms"},{"name":"cms.write","intent":"write","capabilities":["plugin.http.request"],"input":{"type":"object","properties":{"op":{"type":"string","enum":["begin","append","commit"]},"transfer":{"type":"string","minLength":1,"maxLength":128},"size":{"type":"integer","minimum":1,"maximum":15728640},"sha256":{"type":"string","minLength":1,"maxLength":64},"kind":{"type":"string","enum":["json","media"]},"offset":{"type":"integer","minimum":0,"maximum":15728640},"data":{"type":"string","minLength":0,"maxLength":21848}},"required":["op"],"additionalProperties":false},"httpBinding":"cms"}],"jobs":[],"navigation":[{"id":"cms","label":"CMS","path":"/ui/cms"}],"slots":[],"events":[],"secrets":[],"httpBindings":["cms"],"api":[],"mcp":[],"icon":"assets/icon.svg","screenshots":[{"url":"screenshots/cms-draft-editor.png","caption":"Actual CMS editor with synthetic draft content and a read-only fixture transport. No content was saved or published.","alt":"CMS page editor with title, page address, introduction and draft controls"},{"url":"screenshots/cms-draft-mobile.png","caption":"Actual CMS editor at 320px with synthetic content; no save or publish operation.","alt":"Responsive CMS page editor and Spitzli Cloud wordmark at 320px"}]} diff --git a/README.md b/README.md index 0f37c65..33dddff 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,55 @@ # Managed CMS +![Managed CMS icon](assets/icon.svg) + +Edit website content in Spitzli Cloud while each project keeps its own CMS application and data. + +## What you can do + +Create independent website CMS instances through the hosting integration, then manage pages, drafts and media through the native CMS editor. + +## Setup and access + +Requires an operator-installed protocol-v1 plugin, apps service, a scoped CMS HTTP binding and separately deployed CMS instance. The plugin image and per-project CMS application image are distinct. + +## Data and security + +Core authorizes the project and the bridge enforces CMS permissions. Draft editing and publication are separate. Preserve each instance database, retained media and bridge credentials; no cross-project shared content store is introduced. + +## Status and limits + +Beta. Registry admission, runtime binding and database/image/backup acceptance must be verified separately. Existing integration fixtures use disposable data; source publication is not customer activation. + +Catalog publication does not activate the plugin, enable public signup or grant +access to any customer resource. A new presentation release must use a new version; +previously signed releases and their assets stay immutable. + +## Gallery + +Actual CMS editor with synthetic draft content and a read-only fixture transport. No content was saved or published. + +![CMS page editor with title, page address, introduction and draft controls](screenshots/cms-draft-editor.png) + +![The actual CMS editor at 320px with synthetic content](screenshots/cms-draft-mobile.png) + +## Catalog source + +`.tabularium` is the complete catalog-refresh source. It retains the immutable +runtime image and full protocol/access contract of the currently released +`0.1.0-alpha.1` and adds the prepared presentation assets. Refresh it only from a +reviewed immutable source commit. This does not replace signed release assets +or activate the runtime. + +The Spitzli Cloud CMS branding in this source checkout and gallery requires +a new runtime release. The existing `0.1.0-alpha.1` image retained in +`.tabularium` does not contain that change. + +## Technical reference + +The retained implementation notes below include platform integration procedures. +Commands referring to Core, `tools/` or `apps/console` require the [platform source +checkout](https://github.com/spitzli/webdock); they are not standalone setup steps. + Independent source workspace for the existing managed-cms TypeScript protocol-v1 plugin and its separate per-project CMS application. This is not a Rust v2 release. @@ -26,9 +76,7 @@ operator links; those paths are not part of this independent repository. ## Independent source and release -Source: https://github.com/spitzli/cloud-plugin-managed-cms. Maintainer pushes and -same-repository PRs use the verified persistent EU runner; fork PRs never run on -it. The root Dockerfile `check` target runs typechecking, unit tests and complete +Source: https://github.com/spitzli/cloud-plugin-managed-cms. The Blacksmith GitHub App is installed for the exact 16 Spitzli platform, plugin and SDK repositories. The configured and observed region is `us-west`, under the owner’s EU-only exception for Spitzli’s own GitHub CI. The official managed-builder probe passed with `nofallback: true`. These prepared workflows use only Blacksmith; actual per-repository source checks and publication of the regular workflows remain pending. Provider and customer credentials are not used for source checks, and no builder fallback is allowed. The root Dockerfile `check` target runs typechecking, unit tests and complete production builds of both components. Tests needing a real database or deployed image remain explicit opt-in acceptance checks; no tenant data is used by CI. @@ -38,8 +86,8 @@ A signed `v` source tag builds/pushes separate images: repository/revision label. Release assets include identical TS-v1 `tabularium.json` and `managed-cms-universal.json`, SHA-256 checksums and a separate immutable CMS instance image binding. The release does not call the registry API, invent a -registry signature or activate any customer instance. Registry admission stays -pending until the owner publishes and verifies its actual signed receipt. +registry signature or activate any customer instance. Registry admission is a separate step; verify the actual signed receipt +before installing a release. `export-lock.json` preserves the initial export/standalone source snapshots; later changes are recorded by verified signed Git commits and release image digests. diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 0000000..d9b690b --- /dev/null +++ b/assets/README.md @@ -0,0 +1,14 @@ +# Managed CMS presentation assets + +`icon.svg` is an original Spitzli Cloud pictogram, created for this repository. +Purple `#6515D5` and white match the Spitzli Cloud identity; UI text uses the +platform charcoal. It depicts the capability and does not reproduce a provider +trademark or imply provider endorsement. The repository license applies. + +Screenshots, where present, show actual product components or integration views. +Their README captions state whether data and operations are synthetic. They are +not evidence of a deployed provider, customer activation or delivery. + +Presentation changes require a new immutable release version. Keep existing +signed manifests, release assets and image references unchanged. Resolve relative +asset paths against the new source tag or immutable commit, never `main`. diff --git a/assets/icon.svg b/assets/icon.svg new file mode 100644 index 0000000..e581806 --- /dev/null +++ b/assets/icon.svg @@ -0,0 +1 @@ +Managed CMS diff --git a/extensions/managed-cms/src/manifest.ts b/extensions/managed-cms/src/manifest.ts index 62b54b1..7548cf9 100644 --- a/extensions/managed-cms/src/manifest.ts +++ b/extensions/managed-cms/src/manifest.ts @@ -13,6 +13,6 @@ export function createManifest(image:string){ protocolVersion:1,sdkVersion:1,dataProtocolVersion:1,coreVersion:">=0.1.0 <1.0.0",schemaVersion:1,readableSchema:{min:1,max:1}, capabilities:["plugin.apps.read","plugin.apps.provision","plugin.http.read","plugin.http.request"],services:{required:[{name:"http",version:1}],optional:[{name:"apps",version:1}]},httpBindings:["cms"], configuration:object({}),actions,navigation:[{id:"cms",label:"CMS",path:"/ui/cms"}],api:[],mcp:[],jobs:[],events:[],slots:[],tables:[],migrations:[],dependencies:[],secrets:[], - homepage:"https://github.com/spitzli/cloud-plugin-managed-cms",documentation_url:"https://docs.webdock.dev/guides/managed-cms",readme:"README.md", + homepage:"https://github.com/spitzli/cloud-plugin-managed-cms",documentation_url:"https://github.com/spitzli/cloud-plugin-managed-cms/blob/HEAD/README.md",readme:"README.md",icon:"assets/icon.svg",screenshots:[{"url":"screenshots/cms-draft-editor.png","caption":"Actual CMS editor with synthetic draft content and a read-only fixture transport. No content was saved or published.","alt":"CMS page editor with title, page address, introduction and draft controls"},{"url":"screenshots/cms-draft-mobile.png","caption":"Actual CMS editor at 320px with synthetic content; no save or publish operation.","alt":"Responsive CMS page editor and Spitzli Cloud wordmark at 320px"}], }); } diff --git a/extensions/managed-cms/ui/cms.tsx b/extensions/managed-cms/ui/cms.tsx index 8950120..08f8071 100644 --- a/extensions/managed-cms/ui/cms.tsx +++ b/extensions/managed-cms/ui/cms.tsx @@ -12,7 +12,7 @@ const stepLabels:Record={application:"Prepare the CMS",database:" const stateLabels:Record={pending:"Waiting",running:"In progress",succeeded:"Done",skipped:"Not needed",blocked:"Needs attention",failed:"Failed"}; function openChannel(onReady:(call:Call)=>void,onError:(error:string)=>void){ const nonce=location.hash.slice(1),pending=new Mapvoid;reject:(error:Error)=>void;timeout:ReturnType}>();let port:MessagePort|undefined; - const timeout=setTimeout(()=>onError("Open CMS from your Webdock project to connect."),10000); + const timeout=setTimeout(()=>onError("Open CMS from your Spitzli Cloud project to connect."),10000); const receive=(event:MessageEvent)=>{ if(port||event.source!==parent||event.data?.type!=="webdock:init"||!nonce||event.data.nonce!==nonce||event.ports.length!==1)return; clearTimeout(timeout);port=event.ports[0];port.onmessage=(event)=>{ @@ -57,7 +57,7 @@ function App(){ if(site&&transport)return ; return

PROJECT CONTENT

Your website’s CMS

-

Create pages, manage media and publish from Webdock. Each project gets its own CMS, database and media storage.

+

Create pages, manage media and publish from Spitzli Cloud. Each project gets its own CMS, database and media storage.

{error&&

{error}

} {!loaded&&!error&&

Connecting to your project…

} {loaded&&!instance&&
diff --git a/packages/cms-ui/src/client.tsx b/packages/cms-ui/src/client.tsx index 8429ad5..78ded08 100644 --- a/packages/cms-ui/src/client.tsx +++ b/packages/cms-ui/src/client.tsx @@ -855,7 +855,7 @@ function CMSAppContent(props: CMSAppProps) { className="cms-wordmark" onClick={() => void navigate(manifest?.modules[0]?.slug || "")} > - {"webdock"} + {"Spitzli Cloud"} . {"CMS"} ) : ( @@ -867,7 +867,7 @@ function CMSAppContent(props: CMSAppProps) { if (await leave()) window.location.assign("/cms"); }} > - {"webdock"} + {"Spitzli Cloud"} . {"CMS"} )} @@ -1329,7 +1329,7 @@ function CMSAppContent(props: CMSAppProps) { )) )}
-