-
Notifications
You must be signed in to change notification settings - Fork 0
feat(cli): project scaffolding — connectum init and generate service
#229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
intech
wants to merge
13
commits into
main
Choose a base branch
from
feature/cli-scaffolding
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
e3896ee
feat(cli): scaffold base project via `connectum init`
intech 4e97cde
feat(cli): module composition engine + otel fragment for `connectum i…
intech b2028f6
feat(cli): events module fragment for `connectum init`
intech 4eedee6
feat(cli): implement `connectum generate service` (Phase 3)
intech 9c8719c
feat(cli): auth module fragment for `connectum init`
intech a737e52
feat(cli): interactive TUI wizard for `connectum init` (task 2.1)
intech 6197a35
feat(cli): resilience interceptors + healthcheck/reflection toggles (…
intech 0d6fa20
feat(cli): service catalog / multi-service module (2.7)
intech e6289b3
feat(cli): events testing scaffold — MemoryAdapter smoke test (2.8)
intech 1f9b57b
ci(cli): anti-drift scaffold matrix for `connectum init` (1.7 / D-9)
intech 3eaad87
docs(cli): document init / generate service in the package README + c…
intech f8c5caa
fix(cli): use pnpm's `allowBuilds` key so scaffolded pnpm projects ca…
intech ed43844
fix(cli): address review findings on the scaffolding PR
intech File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| --- | ||
| "@connectum/cli": minor | ||
| --- | ||
|
|
||
| feat: `connectum init` and `connectum generate service` — project scaffolding | ||
|
|
||
| - **`connectum init`** scaffolds a production-ready standalone project, interactively (a `@clack/prompts` wizard) or fully from flags (`--yes` / CI / non-TTY). The base is fetched from the dogfooded `getting-started` example via a degit-style clone, so the starter layout stays in sync with a tested example instead of a drift-prone template copy; the selected modules are composed on top. | ||
| - **Modules:** OpenTelemetry (`--otel`), EventBus with an adapter (`--events nats|kafka|redpanda|redis|amqp`), auth (`--auth`, JWT + proto-driven authorization), service catalog (`--catalog`, typed `ctx.call`/`ctx.stream`), opt-in resilience interceptors (`--resilience timeout,retry,...`), and health/reflection toggles. Runtime (`node`/`bun`), package manager (`pnpm`/`npm`) and the Node execution model (`raw` `.ts` >= 25.2 vs `tsx` >= 22.13) are all first-class choices. | ||
| - **Deterministic interceptor order.** When several interceptor-adding modules are selected the composition root emits one canonical chain (outermost → innermost): OpenTelemetry → error handler → auth → validation → resilience → custom, with exactly one error handler. | ||
| - **Lifecycle fix baked in.** `buf generate` is chained into the generated `start` / `test` / `typecheck` scripts (not a pnpm `pre*` hook, which silently no-ops), so a fresh clone never fails with an unresolved `#gen/...` import. Standalone pnpm projects also get the `buf` build-approval that pnpm 11 requires. | ||
| - **`connectum generate service <name>`** adds a service to an existing project: a starter proto plus a `defineService` skeleton whose rpc handlers throw `Code.Unimplemented` (a deliberate, documented trade-off — the handler-map key must still exist, so a later proto method addition remains a compile error). `--with-events` also scaffolds an event-handler service and an ack-by-default `EventRoute`. It never edits your `src/server.ts`; it prints the exact registration to add. | ||
| - **Generated tests are runtime-agnostic**: the e2e test uses the public in-process `createLocalClient` from `@connectum/testing` (no socket, identical on Node and Bun); event-enabled projects also get a broker-free `MemoryAdapter` smoke test. | ||
| - A CI scaffold matrix (`cli-scaffold-matrix`) scaffolds each named module combination and runs `buf generate` → typecheck → test, so a broken fragment fails CI. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| name: CLI Scaffold Matrix | ||
|
|
||
| # Anti-drift gate for `connectum init` (OpenSpec change cli-scaffolding, task 1.7 / D-9): | ||
| # scaffold a project for each named combo, then buf generate -> typecheck -> test, so a | ||
| # broken module fragment fails CI. Scaffolded projects install PUBLISHED @connectum/* | ||
| # from npm (consumer perspective) and fetch the base via tiged from GitHub. | ||
| # | ||
| # NOT covered here (named per the "no silent caps" rule): the tsx Node-exec model; pnpm | ||
| # beyond the one base cell; event adapters other than nats; and live-broker event | ||
| # integration (the generated events test is the broker-free MemoryAdapter smoke). | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - "packages/cli/**" | ||
| - ".github/workflows/cli-scaffold-matrix.yml" | ||
| push: | ||
| branches: [main] | ||
| paths: | ||
| - "packages/cli/**" | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| scaffold: | ||
| name: "init ${{ matrix.combo.name }}" | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| combo: | ||
| - { name: base-node-pnpm, pm: pnpm, args: "" } | ||
| - { name: base-node-npm, pm: npm, args: "" } | ||
| - { name: otel, pm: npm, args: "--otel" } | ||
| - { name: events-nats, pm: npm, args: "--events nats" } | ||
| - { name: auth, pm: npm, args: "--auth" } | ||
| - { name: catalog, pm: npm, args: "--catalog" } | ||
| - { name: kitchen-sink, pm: npm, args: "--otel --events nats --auth --catalog --resilience retry,timeout" } | ||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| with: | ||
| persist-credentials: false | ||
| - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v4 | ||
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | ||
| with: | ||
| node-version: "25.2.0" | ||
| cache: pnpm | ||
| - name: Install + build the CLI | ||
| run: | | ||
| pnpm install --frozen-lockfile | ||
| pnpm --filter @connectum/cli build | ||
| - name: Scaffold a project | ||
| run: node packages/cli/dist/index.js init out --package-manager ${{ matrix.combo.pm }} --yes ${{ matrix.combo.args }} | ||
| - name: Install the scaffolded project | ||
| working-directory: out | ||
| run: ${{ matrix.combo.pm }} install | ||
| - name: Typecheck (runs buf generate first) | ||
| working-directory: out | ||
| run: ${{ matrix.combo.pm }} run typecheck | ||
| - name: Test | ||
| working-directory: out | ||
| run: ${{ matrix.combo.pm }} run test | ||
|
|
||
| scaffold-bun: | ||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| name: "init bun" | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| persist-credentials: false | ||
| - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v4 | ||
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | ||
| with: | ||
| node-version: "25.2.0" | ||
| cache: pnpm | ||
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | ||
| with: | ||
| bun-version: latest | ||
| - name: Install + build the CLI | ||
| run: | | ||
| pnpm install --frozen-lockfile | ||
| pnpm --filter @connectum/cli build | ||
| - name: Scaffold a Bun project | ||
| run: node packages/cli/dist/index.js init out --runtime bun --package-manager npm --yes | ||
| - name: Install + test (bun test) | ||
| working-directory: out | ||
| run: | | ||
| npm install | ||
| npm run test | ||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /** | ||
| * `connectum generate service <name>` — scaffold a service with empty handlers. | ||
| * | ||
| * Scaffolds a starter proto + a `defineService` skeleton (rpc stubs throw | ||
| * Unimplemented, D-6) and, with `--with-events`, an event-handler `EventRoute`. | ||
| * Per D-11 the composition root (`src/server.ts`) is user-owned and NOT edited — | ||
| * the command prints the exact registration edit instead. Refuse-to-clobber. | ||
| * | ||
| * @module commands/generate-service | ||
| */ | ||
|
|
||
| import { resolve } from "node:path"; | ||
| import { defineCommand } from "citty"; | ||
| import { buildServiceFiles, packageName, registrationMessage } from "../scaffold/generateService.ts"; | ||
| import { emitFiles } from "../utils/emit.ts"; | ||
|
|
||
| /** | ||
| * Options for the `generate service` pipeline. | ||
| */ | ||
| export interface GenerateServiceOptions { | ||
| /** Service name. */ | ||
| name: string; | ||
| /** Also scaffold an event-handler service. */ | ||
| withEvents?: boolean | undefined; | ||
| /** Overwrite existing files instead of skipping them. */ | ||
| force?: boolean | undefined; | ||
| /** Target directory (defaults to cwd). */ | ||
| cwd?: string | undefined; | ||
| } | ||
|
|
||
| /** | ||
| * Execute the `generate service` pipeline. | ||
| * | ||
| * @param options - Generate-service configuration | ||
| */ | ||
| export async function executeGenerateService(options: GenerateServiceOptions): Promise<void> { | ||
| const name = options.name.trim(); | ||
| if (name === "") { | ||
| throw new Error("connectum generate service: a service name is required (e.g. `connectum generate service billing`)"); | ||
| } | ||
| // A name with no alphanumerics (e.g. "!!!") reduces to an empty proto package and an | ||
| // empty service identifier, which would emit a malformed `package .v1;` — reject it. | ||
| if (packageName(name) === "") { | ||
| throw new Error(`connectum generate service: "${options.name}" has no alphanumeric characters — cannot derive a proto package or service name.`); | ||
| } | ||
| const withEvents = options.withEvents ?? false; | ||
| const targetDir = resolve(options.cwd ?? process.cwd()); | ||
|
|
||
| const files = buildServiceFiles(name, withEvents); | ||
| const result = emitFiles(targetDir, files, { force: options.force ?? false }); | ||
|
|
||
| console.log(`Generated ${result.written.length} file(s) for service "${name}":`); | ||
| for (const path of result.written) { | ||
| console.log(` + ${path}`); | ||
| } | ||
| if (result.skipped.length > 0) { | ||
| console.log(`Skipped ${result.skipped.length} existing file(s): ${result.skipped.join(", ")}`); | ||
| } | ||
| console.log(""); | ||
| console.log(registrationMessage(name, withEvents)); | ||
| } | ||
|
|
||
| /** | ||
| * citty command definition for `connectum generate service`. | ||
| */ | ||
| export const generateServiceCommand = defineCommand({ | ||
| meta: { | ||
| name: "service", | ||
| description: "Scaffold a service with empty rpc/event handlers", | ||
| }, | ||
| args: { | ||
| name: { | ||
| type: "positional", | ||
| description: "Service name", | ||
| required: true, | ||
| }, | ||
| "with-events": { | ||
| type: "boolean", | ||
| description: "Also scaffold an event-handler service", | ||
| default: false, | ||
| }, | ||
| force: { | ||
| type: "boolean", | ||
| description: "Overwrite existing files", | ||
| default: false, | ||
| }, | ||
| }, | ||
| async run({ args }) { | ||
| await executeGenerateService({ | ||
| name: args.name, | ||
| withEvents: args["with-events"], | ||
| force: args.force, | ||
| }); | ||
| }, | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.