Repository navigation
Turn this repo into the first subscribable Takaro module registry - #41
niekcandaele wants to merge 1 commit into
Conversation
Implements Phase 4 of Takaro PR #2723 (subscribable module registries).
Generates a registry.json manifest plus per-version JSON for each module
into dist/registry/ via build-registry.ts, and publishes the tree to a
`registry` branch on every push to main via a new GitHub Actions workflow.
Module changes:
- All 11 module.json files: name normalized to match directory (no test- prefix);
version bumped from "latest" to "1.0.0" (registry requires semver)
- modules/lottery/ -> modules/jackpot/ (collided with Takaro built-in lottery);
internal renames cascade to commands, permissions (LOTTERY_* -> JACKPOT_*),
function file (lottery-helpers.js -> jackpot-helpers.js), storage keys,
log prefixes, test assertions
- modules/utils/ -> modules/server-toolkit/ (collided with built-in utils);
same cascade: TOOLKIT_* permissions, server-toolkit-{helpers,pure}.js,
log prefixes, test assertions
Infrastructure:
- src/scripts/build-registry.ts (new): validates module names against the
PR's manifest schema (regex/semver/length), writes dist/registry/ tree,
clears stale artifacts before each build
- src/scripts/module-to-json.ts: refactored to expose buildModuleExport()
function for reuse; safeRealpath() helper unifies broken-symlink errors
- src/utils/validate.ts: validateEntityName now throws (was process.exit)
- .github/workflows/publish-registry.yml (new): publishes to `registry`
branch via peaceiris/actions-gh-pages; concurrency guard prevents
overlapping pushes; force_orphan documented
- test/helpers/modules.ts: assertTestSafeHost() guard refuses to run
pushModule/cleanupTestModules against production-looking hosts;
cleanup discovers module names from filesystem (no manual list)
- test/helpers/modules-guard.test.ts (new): 12 unit tests for the guard;
package.json test glob expanded to pick up test/helpers/*.test.ts
Documentation:
- AGENTS.md: module conventions (semver bump on change, name=directory,
Takaro UI shows name verbatim), safety guard reference with full error
template + numbered fix list + auto-accepted hosts list
- README.md: Published Registry section with public URL, status note
about pending Takaro-side content-type fix, and dev local-serve
workflow; warning that npm test refuses production hosts
- testing-methodology.md skill ref: updated to reflect new naming rule
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4be685a82
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| '.dev.', '.test.', '.ci.', '.staging.', '.local', | ||
| '-dev-', '-test-', '-ci-', '-staging-', | ||
| ]; | ||
| if (safePatterns.some((p) => host.includes(p))) return; |
There was a problem hiding this comment.
Match safety patterns against hostname only
assertTestSafeHost currently checks TAKARO_HOST with raw substring matching on the full URL string, so a production host can be misclassified as safe when test-like text appears outside the hostname (for example in userinfo/path/query, e.g. https://localhost@api.takaro.io). In that scenario the new guard is bypassed and pushModule/cleanupTestModules can still delete real modules on production.
Useful? React with 👍 / 👎.
|
|
||
| // Safety invariant: confirm the path ends with 'dist/registry' before wiping it. | ||
| // This prevents accidental data loss if REPO_ROOT is misconfigured. | ||
| if (!DIST_REGISTRY_DIR.endsWith('dist/registry')) { |
There was a problem hiding this comment.
Use path separator-safe check before rmSync
The safety invariant uses DIST_REGISTRY_DIR.endsWith('dist/registry'), which only matches POSIX separators. On Windows this path ends with dist\registry, so build:registry will always throw before writing output. This makes the new registry build script unusable for Windows-based development workflows.
Useful? React with 👍 / 👎.
|
Superseded by #71 (registry build, validator and GitHub Pages publish), part of gettakaro/takaro#4616. Posted by the epic-runner agent on behalf of @niekcandaele. |
Summary
Implements Phase 4 of Takaro PR #2723 (subscribable module registries): reshape
gettakaro/ai-module-writerinto the first registry. A new build script generates aregistry.jsonmanifest plus per-version JSON files for each of the 11 modules intodist/registry/, and a GitHub Actions workflow publishes that tree to aregistrybranch on every push tomain. End state: a Takaro user in any domain can subscribe tohttps://raw.githubusercontent.com/gettakaro/ai-module-writer/registry/registry.jsonand install/update modules from a clean UI flow.The published registry expects modules to use real semver versions, names matching the PR's manifest schema (
/^[a-zA-Z0-9_-]+$/), and to not collide with Takaro built-in module names — which drove a meaningful chunk of this branch's work.Architecture
What Changed
namefield normalized to match directory name (droptest-prefix),versionbumped from"latest"to"1.0.0"(registry requires valid semver). Module descriptions cleaned up for end-user clarity in the install dialog.modules/lottery/→modules/jackpot/(Takaro ships a built-inlottery). Cascaded through commands (/jackpotinfo), permissions (JACKPOT_*), function file (jackpot-helpers.js), storage keys, log prefixes, and test assertions.modules/utils/→modules/server-toolkit/(built-inutils). Cascaded through permissions (TOOLKIT_*), function files (server-toolkit-{helpers,pure}.js), log prefixes, and test assertions.src/scripts/build-registry.ts(new): readsregistry.config.json+ each module, validates against the PR'sRegistryManifestDTOconstraints (name regex, semver, length limits), writesdist/registry/registry.jsonanddist/registry/{name}/{version}.jsonfor each module. Wipesdist/registry/before each build (with path-shape invariant) so renamed/removed modules don't leave stale artifacts.src/scripts/module-to-json.ts: refactored to exposebuildModuleExport()as a reusable function; CLI is a thin wrapper.safeRealpath()helper unifies broken-symlink errors.src/utils/validate.ts:validateEntityNamenow throws (wasprocess.exit(1)) so it's safe to call from long-running processes.registry.config.json(new): registry-level metadata (name, description) shown in Takaro's "Add Registry" UI..github/workflows/publish-registry.yml, new): triggered on push tomain+workflow_dispatch. Logs into AWS CodeArtifact (mirrorsci.ymlpattern for@takaro/*private deps), builds the registry, force-pushes the output to theregistrybranch viapeaceiris/actions-gh-pages@v4. Concurrency guard prevents overlapping runs from racing.test/helpers/modules.ts):assertTestSafeHost()— refuses to runpushModule/cleanupTestModulesagainst production-looking hosts. Substring matcheslocalhost,*.takaro.dev,.staging./.dev./.test./.ci., plus single-label Docker hostnames and.test/.localhostTLDs (RFC 6761 reserved). Override env vars:TAKARO_TEST_HOST_ALLOWLIST(preferred) andTAKARO_TEST_ALLOW_ANY_HOST=1(last resort).getKnownModuleNames()— discovers modules from the filesystem at runtime instead of hand-maintained list.pushModule/cleanupTestModulesswitched fromsearch:(partial-match) tofilters:(exact-match) for deterministic queries.test/helpers/modules-guard.test.ts(new) — 12 unit tests for the safety guard.package.jsontest glob expanded to'{modules/*/test,test/helpers}/*.test.ts'so CI picks up the new file.pretestnow runsbuild:registryso a brokenmodule.jsonfails fast before integration tests start.AGENTS.mdgained a "Module Conventions" section (semver bump on change, name=directory-name) and a "Safety guard for test helpers" reference (full error template, numbered fix list, auto-accepted hosts list).README.mdadded "Published Registry" section with the public URL, the known content-type blocker (see below), and a dev local-serve workflow.Reviewer Guide
src/scripts/build-registry.ts— the new artifact pipeline. Then read the workflow at.github/workflows/publish-registry.yml.test/helpers/modules.tsassertTestSafeHost()— the substring/TLD pattern logic is intentionally narrow; any change to the safe-host criteria should add a unit test inmodules-guard.test.tsfirst.grep -ri 'lottery\|utils' modules/jackpot modules/server-toolkit --include='*.json' --include='*.js' --include='*.ts'returns zero hits.registrybranch served viaraw.githubusercontent.comis the lowest-friction publishing story for third-party registry maintainers (just push files to a branch). However,raw.githubusercontent.comreturnsContent-Type: text/plainfor.jsonfiles, and Takaro PR #2723's strictcontentType.includes('json')check rejects this. Coordinated work: a small Takaro-side PR is needed to either drop that check (DTO validation already rejects malformed bodies) or accepttext/plainwhen the body parses as JSON. Tracked in the README status note.versions: ["1.0.0"]for each module. Multi-version history (walking git tags) is intentionally deferred to Phase 6. This means bumping a module to 1.0.1 will replace the 1.0.0 artifact on the registry branch — acceptable for v1.0.0 release; revisit before any module bumps.lottery → jackpot,utils → server-toolkit: chosen over the alternative of keeping the names + addingBUILTIN_MODULE_NAMESguards inpushModule/cleanupTestModules. The rename is cleaner: no asymmetric guards, no surprises for registry consumers, no risk of clobbering Takaro built-ins.cleanupTestModuleswill delete any module onTAKARO_HOSTmatching amodules/subdir name. Mitigated byassertTestSafeHost. Codex flagged this as sev 7 across multiple review passes; accepted as a documented tradeoff.Implementation Journey
Built via the player-coach loop in 8 turns (severity threshold 3).
jackpot/server-toolkit→ tests 144/144, only naming polish remainedFriction Log
Module-name collisions with Takaro built-ins (turns 1-4): renaming modules to drop the
test-prefix mademodules/lottery/andmodules/utils/collide with Takaro built-ins of the same name. Tests for those modules failed cryptically with "moduleId undefined" —pushModulecouldn't delete the protected built-ins, so import never completed, and teardown crashed on the missing id. Strategic fix: rename tomodules/jackpot/andmodules/server-toolkit/rather than guard-hack around the collision. Reviewers should confirm no third Takaro built-in collides with our remaining 9 module names.Host safety guard pattern matching (turns 2-3): the guard for
assertTestSafeHostoscillated — turn 2 was too strict (rejectednext.takaro.devdespite being the user's actual staging environment), turn 3 was too loose (accepted any.devTLD includingprod.dev). Final design: substring.takaro.devfor staging, single-label hostnames for Docker, RFC 6761 reserved TLDs (.test,.localhost). The 12 unit tests inmodules-guard.test.tslock this in.Module rename completeness (turns 4-7): the lottery→jackpot/utils→server-toolkit rename took 4 incremental turns to fully propagate. Each pass missed a layer: turn 4 kept old internal identifiers (commands, permissions); turn 5 missed test trigger string + log assertion in one specific test file; turn 6 missed directory paths + describe blocks; turn 7 finally renamed source/test directories + describe blocks + comments + capitalized log prefix. Future similar refactors:
grep -ri 'old-name' module-dir/after each rename pass.AGENTS.md guard documentation (turns 2-5): re-edited each turn with new minor issues until turn 5 took the one-shot rewrite approach (concise structure: error block + numbered fixes + auto-accepted hosts list). The pattern of "incrementally polishing prose introduces new mistakes" suggests doing one well-structured rewrite is faster than three rounds of edits.
Below-Threshold Issues (not blocking — for follow-up if useful)
assertTestSafeHostsubstring.takaro.devwould technically matchattacker.takaro.dev.evil.com— dev-time guard, not an auth boundary; flagged for awareness only.build-registry.tsrmSyncinvariant uses POSIX-styleendsWith('dist/registry')— Windows would fail; acceptable since the README recommends WSL2 for Windows.cleanupTestModuleson a shared dev/staging tenant can delete modules other developers installed. Mitigated byassertTestSafeHostand documented in AGENTS.md. The tradeoff is real: getting strict isolation back would require a per-developer namespace or test-instance-per-developer.force_orphan: true+keep_files: falsemeans published versions disappear from theregistrybranch when a newer version ships. Acceptable for v1.0.0 (no module has bumped yet) but revisit before any module hits 1.0.1 — the multi-version history work in Phase 6 of the plan addresses this.Testing Plan
Happy Path
npm install --legacy-peer-depsthennpm run build && npm run build:registry. Confirmdist/registry/registry.jsonexists and contains exactly 11 module entries (afk-kick, community-fund, daily-rewards, hello-world, jackpot, kill-tracker, mini-games, referral-program, server-messages, server-toolkit, vote-restart).dist/registry/jackpot/1.0.0.json— verify command triggers includejackpotinfo(NOTlotteryinfo) and permissions areJACKPOT_*. Same fordist/registry/server-toolkit/1.0.0.json→TOOLKIT_*permissions.python3 -m http.server 8765 --directory dist/registry. From a Takaro dev stack built offfeature/module-registry-systemwithTAKARO_REGISTRY_ALLOWED_PRIVATE_HOSTSset to includehost.docker.internal, subscribe tohttp://host.docker.internal:8765. Confirm 11 modules appear.hello-worldv1.0.0 from the registry. Confirm the module appears with the "Registry" chip, Edit/Delete are disabled, Copy is available.Edge Cases
npm testagainst your local Takaro dev/staging — verify the host guard accepts your host (useTAKARO_TEST_HOST_ALLOWLISTif you have a non-standard hostname). The 12 unit tests intest/helpers/modules-guard.test.tsshould all pass alongside the integration tests.npm testwithTAKARO_HOST=https://api.takaro.io— expect a clean refusal error pointing to AGENTS.md.modules/hello-world/module.json(setversion: "latest"orname: "Hello World!") and runnpm run build:registry— expect a clean exit-1 with a clear error message naming the offending module.modules/hello-world/module.jsonversion to1.0.1, change a command, re-runnpm run build:registry— verify the new file appears atdist/registry/hello-world/1.0.1.jsonand the registry manifest'sversionsfor hello-world updates.Regression Checks
modules/jackpot/test/*andmodules/server-toolkit/test/server-toolkit.test.ts— those went through the most rename churn).grep -ri 'lottery\|utils' modules/jackpot modules/server-toolkit --include='*.json' --include='*.js' --include='*.ts'returns ZERO hits (verifies the rename is comprehensive).npm run module:to-json -- modules/hello-worldstill works (CLI wrapper around the refactored buildModuleExport).npm run module:from-jsonandnpm run module:migrateunaffected.Coordinated Work Before Launch
contentType.includes('json')check inpackages/app-api/src/service/Registry/index.ts—raw.githubusercontent.comreturnsContent-Type: text/plainfor.jsonfiles, so the strict check rejects the official URL. Either drop the check (DTO validation already rejects malformed bodies) or accepttext/plainwhen the body parses as JSON. Without this, the published registry URL is unsubscribable from production Takaro.