feat(scaffold): create a new product module with one command - #101
Conversation
|
Warning Review limit reached
Next review available in: 47 minutes Limit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds contributor tooling that scaffolds a new WSO2 CLI product module with a single command (make new-module NAMESPACE=<ns>). It creates modules/<namespace>, composes it into the workspace, and produces a module that builds and passes its own generated test with no editing. The SDK/Cobra versions and declared protocol versions are read from the checkout (via go mod edit -json and protocol.Supported()) rather than baked into a template, so a generated module never disagrees with what the repository builds. Four namespaces are refused before anything is written, and the boundaries tests now discover product modules from modules/ so every rule covers a scaffolded module the moment it exists.
Changes:
- New
internal/scaffoldpackage (generator + embedded.tmplfiles) pluscmd/wso2-module-newCLI andmake new-moduletarget, with namespace validation and atomic (all-or-nothing) generation. internal/app.CommandNames()exposes shell command names so the "shadowed namespace" refusal cannot fall behind new commands.internal/boundariesnow discovers product modules dynamically instead of a hardcoded list, extending every boundary to generated modules.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
internal/scaffold/scaffold.go |
Core generation, namespace refusals, workspace composition, reference-version reading |
internal/scaffold/templates.go |
Embeds template files and declares the generation file set |
internal/scaffold/templates/*.tmpl |
Generated module's go.mod, module.json, main.go, main_test.go, README |
internal/scaffold/scaffold_test.go |
Generates/builds/tests modules in a temp repo; asserts refusals and workspace joining |
internal/app/app.go |
Adds CommandNames() derived from the shell's builtins |
internal/boundaries/boundaries_test.go |
Discovers product modules so boundaries cover new modules |
cmd/wso2-module-new/main.go |
CLI entry point invoking the scaffold generator |
Makefile |
Adds new-module target with required NAMESPACE guard |
CONTRIBUTING.md |
Documents the new-module flow |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if outcome.Problem != nil { | ||
| t.Fatalf("status returned the problem %v", outcome.Problem) | ||
| } | ||
| if outcome.Result.Schema != StatusSchema { | ||
| t.Errorf("schema = %q, want %q", outcome.Result.Schema, StatusSchema) | ||
| } |
There was a problem hiding this comment.
Correct, and the template was the wrong place to get this wrong: every scaffolded module would have inherited it.
Fixed in 0e493b6. The generated test now checks outcome.Err first and outcome.Result == nil before reading the result, matching the guards in modules/reference that the readme points at. Verified by generating a module and running its test.
d0cd481 to
0e493b6
Compare
f307b33 to
9e96a2b
Compare
9e96a2b to
ed15a8c
Compare
ed15a8c to
7bf18d7
Compare
7bf18d7 to
e27370e
Compare
…space entries whole
e27370e to
888d29b
Compare
Closes #96. Stacked on #100 → #99 → #91; review those first.
creates
modules/mycloud, composes it in the workspace, and produces a module that builds and passes its own test with nothing edited. A developer's first hour goes on their product rather than on reconstructing the shape of a module from the reference module's source.Two facts are read from the checkout rather than written into a template: the SDK and Cobra versions a module depends on, and the protocol versions it declares. A literal is correct until the next release and then produces a release-gate refusal the developer did not cause. They are read from the module graph with
go mod edit -jsonrather than by scanninggo.modas text, because anexclude, a comment, or a versionlessreplaceall mention a module path and only the graph knows which one is the requirement.The generated module contains no authentication path. Acquiring access is where the interesting design lives, so the readme points at
modules/referenceas the complete worked example and says what this module deliberately leaves out.Four namespaces are refused, and nothing is written when one is. The load-bearing refusal is a namespace a shell command owns: the shell resolves its own commands before consulting an installed module, so such a module would build, release, install, and then never run. The names come from
app.CommandNames(), derived from the shell's own command list, so the check cannot fall behind a command being added.How it is proven
One test generates a module, builds it, and runs the test the generation itself produced — which drives the module through the real module contract with
sdk/testkit. A golden file would prove the template unchanged while the SDK moved out from under it; this proves the template works against the SDK as it is. It caught a wrong field name in the template during development, which is exactly the class of bug a golden file would have blessed.Generation happens in a temporary repository, never in the checkout under test. Generating here would write into
modules/and editgo.workwhile the rest of the suite builds in parallel, so a package compiling at the wrong moment could see a workspace entry for a directory being deleted.The test runs in the acceptance gate already, at its "Test the shell" stage — no script change needed.
Boundaries now cover every module
internal/boundarieslisted its modules by hand ({".", "sdk", "modules/reference"}), so a scaffolded module was covered by none of its rules: not the license header, not the prohibition onreplacedirectives, not workspace composition, and not the rule that a module may not import shell internals. Product modules are now discovered frommodules/, so every boundary covers a module the moment it exists. This also makes the generated readme's claim about those rules true, which it otherwise would not have been.Review fixes in the second commit
cloudwhencloudopsexisted reported success while leaving the module out of the workspace — where it would then fail to resolve the SDK at all. Now matched as whole lines, with a test using exactly that pair.reference, which short-circuits on the reserved check. There is now a fixture module declaring a namespace from a directory of another name, which is the case a directory check cannot see.-repoflag and an exportedRemoveFromWorkspacethat nothing called.Verification
go build ./...,go vet ./...,gofmt,golangci-lint run(0 issues), and the fullgo test ./...are clean apart fromTestThePreviousProtocolGateResolvesThePublishedSDKItsGenerationNames, which is inherited from #91's head and described on #99.Correction. Earlier revisions of this description said the full suite carries a failure inherited from #91 (
TestThePreviousProtocolGateResolvesThePublishedSDKItsGenerationNames). That was wrong, and it is withdrawn. CI is green on #91 and on this branch. The failure reproduces only under Go 1.26, which is newer than the 1.25.x the workflows pin: under 1.26 the gate's file-proxy fixture stops resolvingsdk/cobratree, although the fixture zip does contain it. The same commit passes withGOTOOLCHAIN=go1.25.6. It is a toolchain-compatibility problem in the gate's fixture, not a defect in #91 and not a defect in this change.