Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/canonical-saas-first-success.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-croco-app": patch
---

Make the public `saas-api` scaffold journey executable through the real CLI contract and verify its generated zero-credential demo smoke.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
release_pr_update_pattern='^\.changeset/(pre\.json|[^/]+\.md)$'
release_pr_update_ignore_pattern='^\.changeset/README\.md$'
publish_candidate_pattern='^packages/[^/]+/(package\.json|CHANGELOG\.md)$|^package\.json$|^pnpm-lock\.yaml$|^pnpm-workspace\.yaml$'
release_gate_maintenance_pattern='^\.github/workflows/release\.yml$|^scripts/(alpha-release-smoke\.mts|certification-policy\.mts|changeset-required-check\.mts|core-coverage-warning-check\.mts|create-croco-app-generated-smoke(-(support|matrix))?\.mts|dependency-audit-policy\.mts|first-success-verify\.mts|normalize-packages\.mjs|package-bin-smoke\.mts|package-entrypoint-smoke\.mts|package-manifest-contracts\.mjs|package-quality-report\.mts|production-ready-check\.mts|provider-certification-check\.mts|public-api-surface\.mts|quick-start-lambda-smoke\.mts|release-docs-check\.mts|release-metadata-check\.mts|release-spine-evidence\.mts|security-allowlist-metadata-check\.mts|spine-promotion-check\.mts)$|^scripts/tests/(alpha-release-smoke|changeset-required-check|core-coverage-warning-check|create-croco-app-generated-smoke|dependency-audit-policy|first-success-verify|normalize-packages|package-bin-smoke|package-entrypoint-smoke|package-quality-report|production-ready-check|provider-certification-check|public-api-surface|release-docs-check|release-metadata-check|release-spine-evidence|release-workflow|security-allowlist-metadata-check|spine-promotion-check)\.spec\.ts$'
release_gate_maintenance_pattern='^\.github/workflows/release\.yml$|^scripts/(alpha-release-smoke\.mts|certification-policy\.mts|changeset-required-check\.mts|core-coverage-warning-check\.mts|create-croco-app-generated-smoke(-(support|matrix))?\.mts|dependency-audit-policy\.mts|first-success-generated-contract\.mts|first-success-verify\.mts|normalize-packages\.mjs|package-bin-smoke\.mts|package-entrypoint-smoke\.mts|package-manifest-contracts\.mjs|package-quality-report\.mts|production-ready-check\.mts|provider-certification-check\.mts|public-api-surface\.mts|quick-start-lambda-smoke\.mts|release-docs-check\.mts|release-metadata-check\.mts|release-spine-evidence\.mts|security-allowlist-metadata-check\.mts|spine-promotion-check\.mts)$|^scripts/tests/(alpha-release-smoke|changeset-required-check|core-coverage-warning-check|create-croco-app-generated-smoke|dependency-audit-policy|first-success-verify|normalize-packages|package-bin-smoke|package-entrypoint-smoke|package-quality-report|production-ready-check|provider-certification-check|public-api-surface|release-docs-check|release-metadata-check|release-spine-evidence|release-workflow|security-allowlist-metadata-check|spine-promotion-check)\.spec\.ts$'

changed_files="$(git diff --name-only "$base" HEAD)"
should_update_release_pr=false
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,16 @@ graph LR

## 🚀 Quick Start

> Lambda 기반 REST API를 빠르게 시작하세요.
> 실행 가능한 SaaS REST API 골든 패스를 빠르게 시작하세요.
>
> **첫 번째 프로젝트 생성**:
>
> ```bash
> npx create-croco-app@latest my-project --preset ddd-api --scope @myorg --api graphql --backend-deploy lambda --no-install --no-git
> cd my-project && pnpm install && pnpm dev
> npx create-croco-app@latest my-saas-api --goal saas-api --scope @myorg --no-install --no-git
> cd my-saas-api && pnpm install && pnpm demo:smoke
> ```
>
> Generated projects are pnpm workspaces; use `--no-install` if you want to skip the automatic `pnpm install` step.
> `demo:smoke` validates the generated REST contracts, in-memory SaaS flow, and operational smoke without external credentials.
>
> **Route A (Scaffold)**: [Getting Started Guide](packages/docs/src/content/docs/en/guides/getting-started.mdx)에서 scaffold부터 Auth, Metering, Lambda 배포까지 단계별로 SaaS API를 구축하세요.
>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"docs:catalog:check": "node --experimental-strip-types scripts/package-docs-check.mts --check",
"docs:catalog:write": "node --experimental-strip-types scripts/package-docs-check.mts --write",
"dependency-boundaries:check": "node --experimental-strip-types scripts/package-quality-report.mts --boundary-check-only",
"first-success:verify": "node --experimental-strip-types scripts/first-success-verify.mts",
"first-success:verify": "pnpm build --filter=create-croco-app && node --experimental-strip-types scripts/first-success-verify.mts",
"package-quality:report": "node --experimental-strip-types scripts/package-quality-report.mts",
"provider-certification:check": "node --experimental-strip-types scripts/provider-certification-check.mts",
"production-ready:check": "node --experimental-strip-types scripts/production-ready-check.mts",
Expand Down
10 changes: 6 additions & 4 deletions packages/create-croco-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ provides generated-app smoke coverage for the supported presets.
## Usage

```bash
pnpm create croco-app my-service
pnpm create croco-app my-service --preset saas-api --package-manager pnpm --json
npx create-croco-app@latest my-saas-api --goal saas-api --scope @myorg --no-install --no-git
cd my-saas-api && pnpm install && pnpm demo:smoke
```

Generated projects include the package manager command and next-step instructions in
the CLI result.
The `saas-api` goal generates the REST SaaS workspace, provider and tenant manifests,
and the zero-credential `demo:smoke` success path. Generated projects are pnpm
workspaces; `--no-install --no-git` keeps the documented setup deterministic before
the explicit install and smoke commands.

## Verification

Expand Down
48 changes: 48 additions & 0 deletions packages/create-croco-app/src/cli-program.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Command } from "commander";
import { getPackageVersion } from "./package-version.js";
import { formatSaasProviderProfileChoices } from "./saas-provider-profiles.js";

export function createCreateCrocoAppProgram(): Command {
return configureCreateCrocoAppProgram(new Command());
}

export function configureCreateCrocoAppProgram(program: Command): Command {
return program
.name("create-croco-app")
.description("Create a pnpm-based Croco application")
.version(getPackageVersion())
.argument("[directory]", "Target directory")
.option(
"--goal <goal>",
"App goal (saas-api|spa-backend-split|worker|internal-tool). Chooses the supported stack and writes croco.app.json",
)
.option(
"--preset <preset>",
[
"Project preset (blank|ddd-api|ddd-fullstack|ddd-vike-fullstack|production-app|admin-console|saas|ai-saas).",
"ddd-vike-fullstack is a legacy compatibility name for the meta-vite Worker profile",
].join(" "),
)
.option("--scope <scope>", "Package scope (e.g. @myorg)")
.option(
"--saas-profile <profile>",
`Production SaaS provider profile (${formatSaasProviderProfileChoices()})`,
)
.option(
"--tenant-model <model>",
"SaaS tenant model (single|org|workspace|shared-schema|rls-backed)",
)
.option("--api <api>", "API type (graphql|trpc)")
.option("--api-hosting <hosting>", "API hosting (standalone|nextjs)")
.option("--web-apps <apps>", "Comma-separated web app names")
.option("--backend-deploy <deploy>", "Backend deploy (docker|lambda)")
.option(
"--frontend-deploy <deploy>",
"Frontend deploy (opennext|vercel|docker|cloudflare-meta-vite|vite-spa)",
)
.option("--db <dbs>", "Comma-separated DB types (postgres,mongodb,redis)")
.option("--no-agent-rules", "Skip agent rules")
.option("--no-install", "Skip pnpm dependency installation")
.option("--no-git", "Skip git initialization")
.option("--json", "Print a machine-readable JSON result");
}
54 changes: 6 additions & 48 deletions packages/create-croco-app/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,18 @@
import { intro, outro } from "@clack/prompts";
import { Command } from "commander";
import {
createFailureResult,
createSuccessResult,
formatHumanFailure,
formatHumanSuccess,
formatJsonResult,
} from "./cli-result.js";
import { createCreateCrocoAppProgram } from "./cli-program.js";
import { InvalidCliOptionProblem } from "./libs/problems/InvalidCliOptionProblem.js";
import { getPackageVersion } from "./package-version.js";
import { formatSaasProviderProfileChoices } from "./saas-provider-profiles.js";
import type { GeneratorOptions } from "./types.js";

export function createProgram(): Command {
const program = new Command();

program
.name("create-croco-app")
.description("Create a pnpm-based Croco application")
.version(getPackageVersion())
.argument("[directory]", "Target directory")
.option(
"--goal <goal>",
"App goal (saas-api|spa-backend-split|worker|internal-tool). Chooses the supported stack and writes croco.app.json",
)
.option(
"--preset <preset>",
[
"Project preset (blank|ddd-api|ddd-fullstack|ddd-vike-fullstack|production-app|admin-console|saas|ai-saas).",
"ddd-vike-fullstack is a legacy compatibility name for the meta-vite Worker profile",
].join(" "),
)
.option("--scope <scope>", "Package scope (e.g. @myorg)")
.option(
"--saas-profile <profile>",
`Production SaaS provider profile (${formatSaasProviderProfileChoices()})`,
)
.option(
"--tenant-model <model>",
"SaaS tenant model (single|org|workspace|shared-schema|rls-backed)",
)
.option("--api <api>", "API type (graphql|trpc)")
.option("--api-hosting <hosting>", "API hosting (standalone|nextjs)")
.option("--web-apps <apps>", "Comma-separated web app names")
.option("--backend-deploy <deploy>", "Backend deploy (docker|lambda)")
.option(
"--frontend-deploy <deploy>",
"Frontend deploy (opennext|vercel|docker|cloudflare-meta-vite|vite-spa)",
)
.option("--db <dbs>", "Comma-separated DB types (postgres,mongodb,redis)")
.option("--no-agent-rules", "Skip agent rules")
.option("--no-install", "Skip pnpm dependency installation")
.option("--no-git", "Skip git initialization")
.option("--json", "Print a machine-readable JSON result")
.action(async (directory: string | undefined, rawOptions: Record<string, string | boolean>) => {
export function createProgram(): ReturnType<typeof createCreateCrocoAppProgram> {
return createCreateCrocoAppProgram().action(
async (directory: string | undefined, rawOptions: Record<string, string | boolean>) => {
const outputJson = rawOptions.json === true;

try {
Expand Down Expand Up @@ -105,7 +64,6 @@ export function createProgram(): Command {
console.error(outputJson ? formatJsonResult(result) : formatHumanFailure(result));
process.exit(1);
}
});

return program;
},
);
}
18 changes: 18 additions & 0 deletions packages/create-croco-app/src/tests/options.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Problem } from "@croco/problems-core";
import { existsSync, rmSync } from "node:fs";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createProgram } from "../cli.js";
import { createCreateCrocoAppProgram } from "../cli-program.js";
import { InvalidGoalOptionProblem } from "../libs/problems/InvalidGoalOptionProblem.js";
import {
normalizeNonInteractiveOptions,
Expand Down Expand Up @@ -44,6 +45,23 @@ describe("noninteractive CLI option validation", () => {
expect(help).not.toContain("--package-manager");
});

it("parses the canonical documentation command through the action-free CLI contract", () => {
const program = createCreateCrocoAppProgram().exitOverride();

program.parse(
["my-saas-api", "--goal", "saas-api", "--scope", "@myorg", "--no-install", "--no-git"],
{ from: "user" },
);

expect(program.processedArgs).toEqual(["my-saas-api"]);
expect(program.opts()).toMatchObject({
goal: "saas-api",
scope: "@myorg",
install: false,
git: false,
});
});

it("marks retained Vike preset naming as meta-vite compatibility", () => {
expect(CREATE_CROCO_APP_COMPATIBILITY_CHOICES.presets["ddd-vike-fullstack"]).toEqual({
status: "legacy-compatibility-name",
Expand Down
7 changes: 7 additions & 0 deletions packages/create-croco-app/src/verification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { createCreateCrocoAppProgram } from "./cli-program.js";
export { generate } from "./generator.js";
export {
isNonInteractiveOptions,
normalizeNonInteractiveOptions,
parseCliOptions,
} from "./options.js";
2 changes: 1 addition & 1 deletion packages/create-croco-app/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
entry: ["src/index.ts", "src/verification.ts"],
format: ["esm"],
target: "node22",
clean: true,
Expand Down
Loading
Loading