docs(34-03): complete summercms.io registry and marketplace plan
This commit is contained in:
298
.planning/phases/34-plugin-marketplace/34-03-SUMMARY.md
Normal file
298
.planning/phases/34-plugin-marketplace/34-03-SUMMARY.md
Normal file
@@ -0,0 +1,298 @@
|
||||
---
|
||||
phase: 34-plugin-marketplace
|
||||
plan: 03
|
||||
subsystem: registry
|
||||
tags: [registry, kiosk, ingester, git, marketplace, nuxt-ssr, cabana-admin, token-scope, river, nginx, changelog-post]
|
||||
|
||||
requires:
|
||||
- phase: 34-plugin-marketplace (34-01)
|
||||
provides: "kiosk.Parse/Validate/Marshal, ValidVersion/CompareVersions, registry v1 wire types (ListResponse, DetailResponse, VersionsResponse, ErrorResponse, IngestRequest/IngestResponse/IngestResult, RegistryError* codes, RegistryAPIPath), summer plugin:manifest --check"
|
||||
provides:
|
||||
- "Site plugin golem15.summercms: registry tables, models Repository/Plugin/Version, pull ingester (registry.Ingester.Repository, IngestAll), public API GET /api/registry/v1/plugins, /plugins/{id}, /plugins/{id}/versions"
|
||||
- "POST /api/registry/v1/ingest (token scope registry, permission golem15.summercms.registry_ingest, unique River job registry.IngestArgs)"
|
||||
- "Marketplace admin: Repositories and Plugins screens, permissions golem15.summercms.registry and registry_ingest"
|
||||
- "Console summercms:registry-ingest [url] [--force] (scheduled hourly) and summercms:registry-add <url>..."
|
||||
- "/marketplace and /marketplace/<vendor>/<name> in the changelog Node SSR app (useRegistry, utils/marketplace.ts), header and footer links"
|
||||
- "changelog-post.py: plugin manifest check before posting, ingest-now after posting (https_remote, manifest_dirs, ingest_outcome)"
|
||||
- "Deploy: nginx /marketplace and ingest locations, NUXT_REGISTRY_API_BASE, queue.work_in_serve true, cors profile registry, upload limit 4 MiB, DEPLOY.md marketplace release"
|
||||
affects: [34-04 install CLI (consumes /api/registry/v1), 34-05 admin Plugins screen (consumes the API), 34-06 tests and release ledger, sm-summercmsio-app deploy]
|
||||
|
||||
actuals:
|
||||
tokens: 59000
|
||||
tasks: 4
|
||||
commits: 10
|
||||
plan_head_before: 69c1fea766bdde7fd38d73967a1dff70d69f21d5
|
||||
plan_head_after: 69c1fea766bdde7fd38d73967a1dff70d69f21d5
|
||||
repo_heads:
|
||||
sm-summercmsio-plugin: { before: ca5941540c456cd850cf3b2f50e1d3c2e3ab6ee5, after: 5853cf5be0bb681a8751a75c65bcaa90a3b19740, commits: 4 }
|
||||
vue-summercmsio-app: { before: b3bfc5da80d020d318e8d6d4b3b60a45c1d84f42, after: 05f1cea5ac73ac45e632703a5e406012ac426bd2, commits: 1 }
|
||||
sm-summercmsio-app: { before: b688a80e6b4f59fda13bf01e7bfabc0ebd6884f6, after: 97c0c08, commits: 5 }
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Hardened git via exec: no shell, -- before URLs, throwaway HOME, GIT_ALLOW_PROTOCOL allow-list, no system/global config, capped reads through readLimited"
|
||||
- "Cheap change detection: digest of ls-remote strict tags + peeled commits, compared with the stored tags_digest before cloning"
|
||||
- "Per-repository pg_advisory_xact_lock around one write transaction"
|
||||
- "River unique job by args (JobArgsWithInsertOpts) for idempotent ingest-now"
|
||||
- "Public JSON with strong ETag from the body bytes, 304 on If-None-Match, Cache-Control public max-age=300"
|
||||
- "Facts on the cabana preview screen (context: preview), presentation fields on the update form, Fillable restricted to presentation"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/updates/202610300001_create_golem15_summercms_registry.go
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/models/{registry,repository,plugin,version}.go
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/registry/{git,ingest,query}.go
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/controllers/api/registry.go
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/controllers/{repositories,plugins}.go (+ config_list/config_form YAML)
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/models/{repository,plugin}/{columns,fields}.yaml
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/console/registry.go
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/{routes_registry,schedule,jobs,token_scopes,admin,admin_navigation,admin_permissions,lang}.go
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/lang/{en,pl}/lang.yaml
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/{testmain_test,registry_smoke_test}.go
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/changelog/app/pages/marketplace/index.vue
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/changelog/app/pages/marketplace/[vendor]/[name].vue
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/changelog/app/composables/useRegistry.ts
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/changelog/app/utils/marketplace.ts
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/changelog/testing/{marketplace-smoke.mjs,expect-marketplace.txt,fixtures/registry/*.json}
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/tests/marketplace.test.ts
|
||||
modified:
|
||||
- ../sm-summercmsio-app/plugins/golem15/summercms/{plugin.go,go.mod,go.sum,README.md,routes_test.go,links_test.go}
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/{package.json,README.md,nuxt.config.ts,app/components/SiteHeader.vue,app/components/SiteFooter.vue,i18n/locales/en.json}
|
||||
- ../sm-summercmsio-app/vue-summercmsio-app/changelog/{nuxt.config.ts,i18n/locales/en.json,app/layouts/default.vue,app/error.vue}
|
||||
- ../sm-summercmsio-app/{config/queue.yaml,config/http.yaml,go.mod,deploy/nginx/summercms.io.conf,deploy/supervisor/summercms-io-changelog.conf,deploy/changelog.env.example,scripts/changelog-post.py,scripts/test_changelog_post.py,scripts/smoke.sh,DEPLOY.md,README.md,CLAUDE.md}
|
||||
|
||||
key-decisions:
|
||||
- "Registry API bodies are only kiosk wire types; updated_at is the latest tag's date (falls back to the row's update time); errors carry no-cache so a fresh listing is never hidden behind a cached 404"
|
||||
- "Ingester.Repository returns (Result, error) instead of error, so the console and IngestAll can report versions added, unchanged and notes; Ingester.Force re-reads unchanged repositories (summercms:registry-ingest --force)"
|
||||
- "Identity at ingest: module must equal the repository's module, package must equal module + directory, the first repository listing an ID owns it; a plugin no tag publishes any more is unpublished"
|
||||
- "Ingest-now answers 400 invalid_request for a non-https URL and 401/403 with codes unauthenticated/forbidden (outside kiosk's two public codes; the endpoint is authenticated and only changelog-post.py calls it)"
|
||||
- "Repeated ingest-now requests keep one queued job: IngestArgs implements River InsertOpts with UniqueOpts ByArgs over available/pending/running/retryable/scheduled"
|
||||
- "The registry token scope declares GET, HEAD and POST: the framework requires GET in every scope (the plan said POST only)"
|
||||
- "App go.mod now requires summercms v0.6.0 like the site plugin: the workspace already resolved v0.6.0, so the landing version check failed in scripts/build.sh dev without it"
|
||||
- "Marketplace pages keep only the sanitized README HTML in the payload (no markdown source) and use plain GET forms and anchors, so they work without JavaScript"
|
||||
|
||||
patterns-established:
|
||||
- "Site-plugin registry as the producer of the kiosk v1 contract; the Nuxt pages and the summer CLI consume it"
|
||||
|
||||
requirements-completed: [SC-2, D-04, D-13, D-14, D-15, D-16]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "A curated repository with a tagged plugin.yaml is cloned (hardened git, bare partial clone), parsed through kiosk and served at GET /api/registry/v1/plugins/{id} with a breakwater-sanitized README"
|
||||
requirement: D-13
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "plugins/golem15/summercms/registry_smoke_test.go#TestRegistrySmoke/detail"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "List, search (escaped, capped), category filter and versions newest first, with open CORS, Cache-Control, ETag and 304"
|
||||
requirement: D-16
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "plugins/golem15/summercms/registry_smoke_test.go#TestRegistrySmoke/list,search,versions,headers"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "Every vX.Y.Z tag is a version row; tags without a valid plugin.yaml are skipped and noted; nested plugin.yaml files; module and ID identity rules"
|
||||
requirement: D-14
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "plugins/golem15/summercms/registry_smoke_test.go#TestRegistrySmoke/tags,nested,identity"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "Hourly scheduled ingest that skips the clone when the ls-remote tag digest is unchanged; IngestAll survives a failing repository"
|
||||
requirement: D-15
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "plugins/golem15/summercms/registry_smoke_test.go#TestRegistrySmoke/unchanged"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D5
|
||||
description: "Ingest-now endpoint: 401 anonymous, 403 wrong scope or missing permission, 202 with one unique queued job, 404 uncurated or disabled, 400 non-https"
|
||||
requirement: D-13
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "plugins/golem15/summercms/registry_smoke_test.go#TestRegistrySmoke/ingest_auth"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D6
|
||||
description: "Admin curation (URL rules, derived module, duplicates, registry-add command) and presentation fields (category, featured, sort order, icon, screenshots) appear in the API; facts read-only"
|
||||
requirement: SC-2
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "plugins/golem15/summercms/registry_smoke_test.go#TestRegistrySmoke/curation,presentation"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D7
|
||||
description: "changelog-post.py refuses a plugin release that is not exactly the tag, is dirty or has a stale plugin.yaml, and asks the registry to ingest after posting (401/403/404/network are warnings)"
|
||||
requirement: D-04
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "python3 -B scripts/test_changelog_post.py (HttpsRemoteTest, ManifestDirsTest, IngestOutcomeTest, RegistrySettingsTest)"
|
||||
status: pass
|
||||
- kind: other
|
||||
ref: "check_plugin_release on a fixture repo from modules/kiosk/testdata/literal: passes at v0.1.0, refuses a drifted plugin.yaml at v0.1.1"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D8
|
||||
description: "/marketplace list, search, category and detail pages rendered by the changelog Node SSR app from the registry API"
|
||||
requirement: SC-2
|
||||
verification:
|
||||
- kind: automated_ui
|
||||
ref: "pnpm test:marketplace (changelog/testing/marketplace-smoke.mjs, 30 checks)"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "pnpm test (tests/marketplace.test.ts)"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "scripts/build.sh dev && scripts/smoke.sh (registry API on the real binary, /marketplace on Node against it)"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "Layout and visuals of the marketplace pages are Claude's discretion per CONTEXT; the visual check of /marketplace is a manual item in 34-VALIDATION.md"
|
||||
- id: D9
|
||||
description: "Deploy: nginx locations, supervisor env, jobs in serve, CORS profile, DEPLOY.md marketplace release; production ingest after deploy"
|
||||
verification:
|
||||
- kind: other
|
||||
ref: "scripts/check-deploy.sh"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "Production ingest of the launch repositories happens only after the user-gated deploy (nothing was deployed)"
|
||||
|
||||
duration: 51min
|
||||
completed: 2026-10-29
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 34 Plan 03: summercms.io plugin registry and /marketplace Summary
|
||||
|
||||
**The site plugin ingests curated plugin repositories over hardened anonymous git into Postgres and serves them through the frozen kiosk v1 API at /api/registry/v1. It adds an admin for curation and presentation, a `registry`-scoped ingest-now endpoint and an hourly schedule. The changelog Node SSR app renders /marketplace from that API, and changelog-post.py checks plugin manifests before posting and asks the registry to ingest afterwards.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** about 51 min
|
||||
- **Started:** 2026-10-29T16:17:50Z
|
||||
- **Completed:** 2026-10-29T17:09:20Z
|
||||
- **Tasks:** 4 of 4
|
||||
- **Files modified:** 70 across three repositories (38 site plugin, 20 vue app, 12 app)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Registry tables and ingester.** The three registry tables come from migration `202610300001_create_golem15_summercms_registry`. `registry.Ingester` makes a bare partial clone (`--filter=blob:none`, retried without the filter), lists strict tags with peeled commits and reads each tag's `plugin.yaml` files (64 KiB cap, `kiosk.Parse`/`Validate`). It checks identity: the module must equal the repository's module, the package must match its directory, and the first repository owns an ID. It reads the newest tag's README (512 KiB cap, `breakwater.Render`) and writes everything in one transaction under a per-repository advisory lock.
|
||||
- **Public API.** `GET /plugins`, `/plugins/{id}` and `/plugins/{id}/versions` return only kiosk wire types. Every response has open CORS through the `registry` profile, `throttle:registry-api` (300/min per IP), `Cache-Control: public, max-age=300` and a strong ETag with 304. `q` is escaped and capped at 100 characters, and `category` is checked against `^[a-z0-9-]{1,40}$`.
|
||||
- **Change detection.** An `ls-remote` tag digest skips the clone when the tags have not changed. `IngestAll` keeps going when one repository fails. `summercms:registry-ingest [url] [--force]` runs hourly through `pact.HasSchedule`, and the app now sets `queue.work_in_serve: true` so the worker runs.
|
||||
- **Admin.** A Marketplace main item has two screens. Repositories edits the URL and the enabled switch, with the same rules as `summercms:registry-add`. Plugins shows the facts read-only on the preview screen and lets you edit category, featured, sort order, icon and screenshots with captions. The icon and screenshots reach the API as absolute URLs on `app.url`.
|
||||
- **Ingest now.** `POST /api/registry/v1/ingest` needs a backend principal holding `golem15.summercms.registry_ingest` (a token scoped `registry`). It only queues repositories that are curated and enabled, and keeps one unique River job per repository.
|
||||
- **Marketplace pages.** `/marketplace` is a list with a GET search form and category links. `/marketplace/<vendor>/<name>` shows the install command with a copy button, the README (sanitized again), screenshots and a versions table. The header and footer link to the marketplace, the layout and error page know about it, and the landing prerender ignores `/marketplace`.
|
||||
- **Release script.** `changelog-post.py publish --repo` requires that HEAD is the tag, the worktree is clean, and `summer plugin:manifest --check` passes for every committed `plugin.yaml`. After posting it sends `POST /ingest` with the registry token, and a refusal is only a warning.
|
||||
- **Deploy.** nginx sends `/marketplace` to Node and allows POST only on the ingest path. The supervisor program gets `NUXT_REGISTRY_API_BASE`. `smoke.sh` now checks the registry and the marketplace. DEPLOY.md has a "Plugin marketplace" section with git on rome, the fourteen launch repositories and the registry token.
|
||||
|
||||
## Task Commits
|
||||
|
||||
**sm-summercmsio-plugin** (`plugins/golem15/summercms`, branch master):
|
||||
1. Task 1: `242d641` feat: plugin registry tables, ingester and plugin detail API
|
||||
2. Task 2: `f6a5949` feat: registry list, search and versions API, hourly ingest
|
||||
3. Task 3: `08e04de` feat: registry admin, token scope and ingest-now endpoint
|
||||
4. Task 4: `5853cf5` feat: marketplace link
|
||||
|
||||
**vue-summercmsio-app** (branch master):
|
||||
5. Task 4: `05f1cea` feat: marketplace pages
|
||||
|
||||
**sm-summercmsio-app** (branch master):
|
||||
6. Task 2: `458a4f1` feat: registry CORS profile and jobs in serve
|
||||
7. Task 3: `06593c3` feat: manifest check and ingest-now in changelog-post
|
||||
8. Task 3: `0707fdc` feat: upload limit for the marketplace admin (deviation 3)
|
||||
9. Task 4: `fd8fe3b` feat: marketplace deploy config and docs
|
||||
10. Task 4: `97c0c08` chore: require summercms v0.6.0 (deviation 4)
|
||||
|
||||
**Plan metadata:** the summercms.go docs commit containing this SUMMARY.
|
||||
|
||||
**Pending submodule pointers (release ledger, not committed in the app; gitlinks left modified):**
|
||||
- `plugins/golem15/summercms` → `5853cf5be0bb681a8751a75c65bcaa90a3b19740` (recorded in the app: `ca59415`)
|
||||
- `vue-summercmsio-app` → `05f1cea5ac73ac45e632703a5e406012ac426bd2` (recorded in the app: `b3bfc5d`)
|
||||
|
||||
Nothing was pushed, tagged or deployed.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- Site plugin: the `updates/`, `models/`, `registry/`, `controllers/`, `console/` and `lang/` packages and files listed in key-files; `plugin.go` (Register/Boot keep the app, migrations, models); `routes_registry.go` (API groups and throttle buckets); `schedule.go`, `jobs.go`, `token_scopes.go` and the `admin*.go` files; tests `testmain_test.go` (testcontainers Postgres) and `registry_smoke_test.go` (12 subtests); `routes_test.go` and `links_test.go` adjusted; README "Plugin registry" section.
|
||||
- vue app: the marketplace pages, `useRegistry.ts`, `utils/marketplace.ts`, the SSR smoke and fixtures, `tests/marketplace.test.ts`; config, header, footer, layout, error page and strings.
|
||||
- App: `config/http.yaml` (cors profile, upload limit), `config/queue.yaml`, `go.mod`, the deploy files, `scripts/changelog-post.py` and its tests, `scripts/smoke.sh`, DEPLOY.md, README.md, CLAUDE.md.
|
||||
|
||||
## Decisions Made
|
||||
|
||||
See key-decisions in the frontmatter. In addition:
|
||||
- `Repository.BeforeSave` derives the module only from an https URL. The admin form, `registry-add` and the ingest endpoint all validate through `models.CheckRepository`. Test fixtures insert `file://` repositories with an explicit module. `registry.SetAllowedProtocolsForTest` (exported, test-only) widens the protocol allow-list to `https:file`.
|
||||
- The plugin rows have no create and no delete in the admin; the ingester alone creates and withdraws them. Removing a repository cascades to its plugins.
|
||||
- Categories: `content`, `users`, `commerce`, `communication`, `seo`, `tools`. They are checked in `FormBeforeUpdate` and are a contract with `utils/marketplace.ts`.
|
||||
- The USER-SETUP content stays in this SUMMARY ("User Setup Required" below) instead of a separate `34-USER-SETUP.md`, so this plan does not collide with sibling plans of the same wave.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] Site tests needed config once the plugin has a CORS profile and admin screens**
|
||||
- **Found during:** Tasks 2 and 3
|
||||
- **Issue:** `cors:registry` and the admin made `surf.Assemble(backpack.New(nil), …)` fail to boot in routes_test/links_test (unknown CORS profile, empty admin JWT secret, upload limit). `/backend` is now the admin's, not the site 404.
|
||||
- **Fix:** A `siteApp(t)` helper supplies body limits, the CORS profile and a JWT secret. The route-pattern test allows surf's OPTIONS preflight routes and the admin's own routes. TestPluginIdentity now asserts that the plugin has admin controllers, and the `/backend` expectation became `GET /backend/api/v1/auth/me` 401.
|
||||
- **Commits:** f6a5949, 08e04de
|
||||
|
||||
**2. [Rule 1 - Bug] cabana requires `Rules()` on every editable model**
|
||||
- **Found during:** Task 3 (every Plugins show/update answered an opaque 500)
|
||||
- **Fix:** Added `models.Plugin.Rules()`. The cause was traced with a `go build -overlay` print in cabana's error writer; the framework was not modified.
|
||||
- **Commit:** 08e04de
|
||||
|
||||
**3. [Rule 3 - Blocking] App upload limit too small for the admin's image fields**
|
||||
- **Issue:** cabana refuses to boot when `maxFilesize` plus multipart framing exceeds `http.body_limits.upload_bytes` (1 MiB in production config).
|
||||
- **Fix:** `upload_bytes: 4194304` in config/http.yaml. Without it the binary would not start.
|
||||
- **Commit:** 0707fdc (separate app commit)
|
||||
|
||||
**4. [Rule 3 - Blocking] App go.mod framework requirement**
|
||||
- **Issue:** The site plugin requires v0.6.0, so the workspace resolves v0.6.0, while the app's go.mod said v0.5.0. As a result `scripts/build.sh dev` failed `TestLandingVersion` (the page showed 0.6, go.mod said 0.5).
|
||||
- **Fix:** `go mod edit -require=…summercms@v0.6.0` only. A full `go mod tidy` would also have dropped indirect requirements based on the framework working tree, so it was not committed.
|
||||
- **Commit:** 97c0c08
|
||||
|
||||
**5. [Rule 2 - Missing critical] Extra protections and files beyond the plan's list**
|
||||
- Package path must equal module + directory at ingest. More than 20 plugin.yaml files per tag are not read. A plugin no longer published by any tag is unpublished. The ingest body is capped at 4 KiB, and nginx sets `client_max_body_size 8k` on the ingest path.
|
||||
- The detail page payload carries only sanitized README HTML; `readme_markdown` is dropped.
|
||||
- Extra files: `tests/marketplace.test.ts`, `SiteFooter.vue` (Marketplace link; the header link hides below 480px), the landing `nuxt.config.ts` (prerender/linkChecker ignore `/marketplace`; `nuxt generate` failed without it), the changelog `layouts/default.vue` and `error.vue` (active menu entry, marketplace 404 text).
|
||||
|
||||
**6. [Plan text vs framework] Token scope methods**
|
||||
- The plan said Methods POST. cabana's scope validation requires GET, so the scope declares GET, HEAD and POST.
|
||||
|
||||
**7. [Plan text vs layer] parseExpectLine**
|
||||
- `smoke-ssr.mjs` runs its `main()` when imported, so `marketplace-smoke.mjs` repeats its expectation parser (same grammar) instead of importing it.
|
||||
|
||||
**8. [Process] TDD**
|
||||
- Tasks 2 and 3 wrote their subtests first and saw assertion-level RED: 404s from missing routes, and 405s from the admin/ingest routes. Stubs were added only so the code compiled. `msd_run check tdd-red-evidence` parses TAP/Surefire output, not `go test -v`, so the RED evidence is recorded here rather than machine-checked. One commit per task per repository, following the plan's Execution units table.
|
||||
|
||||
**Total deviations:** 8 (1 Rule 1, 3 Rule 3, 1 Rule 2, 3 plan/process notes). **Impact:** no change to the v1 contract. The extra commits are needed for boot and for the build.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
- `grep -n "location ^~ /marketplace/\|location = /api/registry/v1/ingest"` (the plan's acceptance command) prints only the second line under GNU grep because of how it parses `^~` inside the alternation. `grep -nF -e … -e …` prints both lines (143 and 162). The file is correct.
|
||||
- `scripts/build.sh dev` regenerates `main.go` and `plugins.gen.go` (34-01's PluginInfo/FrameworkVersion). They were restored to the committed versions, and regenerating them belongs to the release bump.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
Before the deploy that ships the registry (nothing was deployed):
|
||||
1. **Release ledger:** push `sm-summercmsio-plugin` (`5853cf5`) and `vue-summercmsio-app` (`05f1cea`), then commit the two gitlink bumps in sm-summercmsio-app. Tag framework `v0.6.0` (34-06) so the release build finds it. Regenerate and commit `main.go`/`plugins.gen.go` with that bump.
|
||||
2. **git on rome:** run `ssh rome 'command -v git && git --version'` (DEPLOY.md "Plugin marketplace", step 2).
|
||||
3. **Reinstall nginx and the changelog supervisor file** from the repository, then run `migrate` (registry and queue tables). `work_in_serve` is now true.
|
||||
4. **Registry token (only you mint it):** `admin:token:create <you> --name registry --scope registry`, stored in `~/.config/summercms/registry-token` (mode 600). Until then the ingest-now call gets 403, which is only a warning, and the hourly run picks the tag up.
|
||||
5. **Curate the fourteen launch repositories** (DEPLOY.md lists them) once their first `plugin.yaml` tags exist (34-02, user-gated), with `summercms:registry-add …` then `summercms:registry-ingest`, or in the admin over the tunnel.
|
||||
6. **Manual checks (34-VALIDATION.md):** run a production ingest after the deploy and look over /marketplace visually.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- 34-04 (install CLI) and 34-05 (admin Plugins screen) can call `GET /api/registry/v1/...` locally by running the app (`scripts/build.sh dev`, then `bin/summercms-io serve`) with a curated `file://` or https repository.
|
||||
- The registry answers with exactly the kiosk wire types, so any change to the v1 shape must start in `modules/kiosk/registry.go` and stay additive.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- Key files exist: registry/ingest.go (contains `breakwater.Render`, `kiosk.Parse(`, `io.LimitReader`), controllers/api/registry.go (`kiosk.ListResponse`, `kiosk.DetailResponse`), the migration (`golem15_summercms_registry_versions` in create and drop), registry_smoke_test.go (`func TestRegistrySmoke(`), pages/marketplace/index.vue (`plugin:install`), changelog-post.py (`api/registry/v1/ingest`).
|
||||
- Commits found: site 242d641, f6a5949, 08e04de, 5853cf5; vue 05f1cea; app 458a4f1, 06593c3, 0707fdc, fd8fe3b, 97c0c08.
|
||||
- Verification: TestRegistrySmoke 12/12 PASS (Docker); site `go vet` and `go test -short` ok; app `go vet` and `go test` ok; `test_changelog_post.py` OK (36 tests); `check-deploy.sh` ok; `pnpm test:marketplace` ok (30 checks); `pnpm test:changelog` ok (77 checks); `pnpm test` 77 pass; `scripts/build.sh dev` and `scripts/smoke.sh` ok; framework `go vet ./...` and `go test -short ./...` green.
|
||||
- Acceptance greps: every Task 1-4 criterion passes. Two notes: the GIT_* grep prints exactly 3 lines, and the nginx alternation grep is explained under Issues Encountered.
|
||||
Reference in New Issue
Block a user