@better-openclaw/core 1.0.23 → 1.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon-stack.cjs +673 -0
- package/dist/addon-stack.cjs.map +1 -0
- package/dist/addon-stack.d.cts +23 -0
- package/dist/addon-stack.d.cts.map +1 -0
- package/dist/addon-stack.d.mts +23 -0
- package/dist/addon-stack.d.mts.map +1 -0
- package/dist/addon-stack.mjs +671 -0
- package/dist/addon-stack.mjs.map +1 -0
- package/dist/addon-stack.test.cjs +349 -0
- package/dist/addon-stack.test.cjs.map +1 -0
- package/dist/addon-stack.test.d.cts +1 -0
- package/dist/addon-stack.test.d.mts +1 -0
- package/dist/addon-stack.test.mjs +349 -0
- package/dist/addon-stack.test.mjs.map +1 -0
- package/dist/bare-metal-partition.test.cjs +20 -21
- package/dist/bare-metal-partition.test.cjs.map +1 -1
- package/dist/bare-metal-partition.test.mjs +4 -5
- package/dist/bare-metal-partition.test.mjs.map +1 -1
- package/dist/composer.cjs +17 -1
- package/dist/composer.cjs.map +1 -1
- package/dist/composer.d.cts +24 -1
- package/dist/composer.d.cts.map +1 -1
- package/dist/composer.d.mts +24 -1
- package/dist/composer.d.mts.map +1 -1
- package/dist/composer.mjs +14 -2
- package/dist/composer.mjs.map +1 -1
- package/dist/composer.snapshot.test.cjs +20 -20
- package/dist/composer.snapshot.test.cjs.map +1 -1
- package/dist/composer.snapshot.test.mjs +2 -2
- package/dist/composer.test.cjs +53 -52
- package/dist/composer.test.cjs.map +1 -1
- package/dist/composer.test.mjs +4 -3
- package/dist/composer.test.mjs.map +1 -1
- package/dist/deployers/strip-host-ports.test.cjs +26 -26
- package/dist/deployers/strip-host-ports.test.cjs.map +1 -1
- package/dist/deployers/strip-host-ports.test.mjs +1 -1
- package/dist/generate.cjs +8 -4
- package/dist/generate.cjs.map +1 -1
- package/dist/generate.d.cts.map +1 -1
- package/dist/generate.d.mts.map +1 -1
- package/dist/generate.mjs +9 -5
- package/dist/generate.mjs.map +1 -1
- package/dist/generate.test.cjs +55 -55
- package/dist/generate.test.cjs.map +1 -1
- package/dist/generate.test.mjs +2 -2
- package/dist/generate.test.mjs.map +1 -1
- package/dist/generators/bare-metal-install.test.cjs +18 -18
- package/dist/generators/bare-metal-install.test.cjs.map +1 -1
- package/dist/generators/bare-metal-install.test.mjs +1 -1
- package/dist/generators/caddy.test.cjs +13 -13
- package/dist/generators/caddy.test.cjs.map +1 -1
- package/dist/generators/caddy.test.mjs +1 -1
- package/dist/generators/clone-repos.cjs +140 -0
- package/dist/generators/clone-repos.cjs.map +1 -0
- package/dist/generators/clone-repos.d.cts +11 -0
- package/dist/generators/clone-repos.d.cts.map +1 -0
- package/dist/generators/clone-repos.d.mts +11 -0
- package/dist/generators/clone-repos.d.mts.map +1 -0
- package/dist/generators/clone-repos.mjs +139 -0
- package/dist/generators/clone-repos.mjs.map +1 -0
- package/dist/generators/clone-repos.test.cjs +140 -0
- package/dist/generators/clone-repos.test.cjs.map +1 -0
- package/dist/generators/clone-repos.test.d.cts +1 -0
- package/dist/generators/clone-repos.test.d.mts +1 -0
- package/dist/generators/clone-repos.test.mjs +141 -0
- package/dist/generators/clone-repos.test.mjs.map +1 -0
- package/dist/generators/env.test.cjs +17 -17
- package/dist/generators/env.test.cjs.map +1 -1
- package/dist/generators/env.test.mjs +1 -1
- package/dist/generators/health-check.test.cjs +39 -39
- package/dist/generators/health-check.test.cjs.map +1 -1
- package/dist/generators/health-check.test.mjs +1 -1
- package/dist/generators/postgres-init.cjs +20 -0
- package/dist/generators/postgres-init.cjs.map +1 -1
- package/dist/generators/postgres-init.d.cts.map +1 -1
- package/dist/generators/postgres-init.d.mts.map +1 -1
- package/dist/generators/postgres-init.mjs +20 -0
- package/dist/generators/postgres-init.mjs.map +1 -1
- package/dist/generators/scripts.cjs +332 -3
- package/dist/generators/scripts.cjs.map +1 -1
- package/dist/generators/scripts.d.cts +3 -1
- package/dist/generators/scripts.d.cts.map +1 -1
- package/dist/generators/scripts.d.mts +3 -1
- package/dist/generators/scripts.d.mts.map +1 -1
- package/dist/generators/scripts.mjs +332 -3
- package/dist/generators/scripts.mjs.map +1 -1
- package/dist/generators/scripts.test.cjs +57 -23
- package/dist/generators/scripts.test.cjs.map +1 -1
- package/dist/generators/scripts.test.mjs +39 -5
- package/dist/generators/scripts.test.mjs.map +1 -1
- package/dist/generators/stack-manifest.cjs +1 -0
- package/dist/generators/stack-manifest.cjs.map +1 -1
- package/dist/generators/stack-manifest.d.cts +3 -2
- package/dist/generators/stack-manifest.d.cts.map +1 -1
- package/dist/generators/stack-manifest.d.mts +3 -2
- package/dist/generators/stack-manifest.d.mts.map +1 -1
- package/dist/generators/stack-manifest.mjs +1 -0
- package/dist/generators/stack-manifest.mjs.map +1 -1
- package/dist/generators/traefik.test.cjs +32 -32
- package/dist/generators/traefik.test.cjs.map +1 -1
- package/dist/generators/traefik.test.mjs +1 -1
- package/dist/index.cjs +28 -5
- package/dist/index.d.cts +7 -4
- package/dist/index.d.mts +7 -4
- package/dist/index.mjs +10 -7
- package/dist/migrations.test.cjs +16 -16
- package/dist/migrations.test.cjs.map +1 -1
- package/dist/migrations.test.mjs +1 -1
- package/dist/presets/registry.cjs.map +1 -1
- package/dist/presets/registry.d.cts.map +1 -1
- package/dist/presets/registry.d.mts.map +1 -1
- package/dist/presets/registry.mjs.map +1 -1
- package/dist/presets/registry.test.cjs +14 -14
- package/dist/presets/registry.test.cjs.map +1 -1
- package/dist/presets/registry.test.mjs +1 -1
- package/dist/resolver.cjs +8 -0
- package/dist/resolver.cjs.map +1 -1
- package/dist/resolver.mjs +9 -1
- package/dist/resolver.mjs.map +1 -1
- package/dist/resolver.test.cjs +125 -90
- package/dist/resolver.test.cjs.map +1 -1
- package/dist/resolver.test.mjs +47 -12
- package/dist/resolver.test.mjs.map +1 -1
- package/dist/{schema-B4c64P8N.d.cts → schema-CKBRu-Rt.d.cts} +355 -8
- package/dist/schema-CKBRu-Rt.d.cts.map +1 -0
- package/dist/{schema-CXNhYci1.d.mts → schema-Dn-_Jpb6.d.mts} +355 -8
- package/dist/schema-Dn-_Jpb6.d.mts.map +1 -0
- package/dist/schema.cjs +160 -5
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +150 -6
- package/dist/schema.mjs.map +1 -1
- package/dist/schema.test.cjs +86 -86
- package/dist/schema.test.cjs.map +1 -1
- package/dist/schema.test.mjs +1 -1
- package/dist/services/definitions/apptension-saas.cjs +87 -0
- package/dist/services/definitions/apptension-saas.cjs.map +1 -0
- package/dist/services/definitions/apptension-saas.d.cts +7 -0
- package/dist/services/definitions/apptension-saas.d.cts.map +1 -0
- package/dist/services/definitions/apptension-saas.d.mts +7 -0
- package/dist/services/definitions/apptension-saas.d.mts.map +1 -0
- package/dist/services/definitions/apptension-saas.mjs +86 -0
- package/dist/services/definitions/apptension-saas.mjs.map +1 -0
- package/dist/services/definitions/boxyhq-saas.cjs +88 -0
- package/dist/services/definitions/boxyhq-saas.cjs.map +1 -0
- package/dist/services/definitions/boxyhq-saas.d.cts +7 -0
- package/dist/services/definitions/boxyhq-saas.d.cts.map +1 -0
- package/dist/services/definitions/boxyhq-saas.d.mts +7 -0
- package/dist/services/definitions/boxyhq-saas.d.mts.map +1 -0
- package/dist/services/definitions/boxyhq-saas.mjs +87 -0
- package/dist/services/definitions/boxyhq-saas.mjs.map +1 -0
- package/dist/services/definitions/browserless.cjs +4 -1
- package/dist/services/definitions/browserless.cjs.map +1 -1
- package/dist/services/definitions/browserless.mjs +4 -1
- package/dist/services/definitions/browserless.mjs.map +1 -1
- package/dist/services/definitions/cmsaas-starter.cjs +86 -0
- package/dist/services/definitions/cmsaas-starter.cjs.map +1 -0
- package/dist/services/definitions/cmsaas-starter.d.cts +7 -0
- package/dist/services/definitions/cmsaas-starter.d.cts.map +1 -0
- package/dist/services/definitions/cmsaas-starter.d.mts +7 -0
- package/dist/services/definitions/cmsaas-starter.d.mts.map +1 -0
- package/dist/services/definitions/cmsaas-starter.mjs +85 -0
- package/dist/services/definitions/cmsaas-starter.mjs.map +1 -0
- package/dist/services/definitions/convex.cjs +43 -1
- package/dist/services/definitions/convex.cjs.map +1 -1
- package/dist/services/definitions/convex.mjs +43 -1
- package/dist/services/definitions/convex.mjs.map +1 -1
- package/dist/services/definitions/grafana.cjs +11 -1
- package/dist/services/definitions/grafana.cjs.map +1 -1
- package/dist/services/definitions/grafana.mjs +11 -1
- package/dist/services/definitions/grafana.mjs.map +1 -1
- package/dist/services/definitions/index.cjs +51 -36
- package/dist/services/definitions/index.cjs.map +1 -1
- package/dist/services/definitions/index.d.cts +30 -25
- package/dist/services/definitions/index.d.cts.map +1 -1
- package/dist/services/definitions/index.d.mts +30 -25
- package/dist/services/definitions/index.d.mts.map +1 -1
- package/dist/services/definitions/index.mjs +47 -37
- package/dist/services/definitions/index.mjs.map +1 -1
- package/dist/services/definitions/ixartz-saas.cjs +88 -0
- package/dist/services/definitions/ixartz-saas.cjs.map +1 -0
- package/dist/services/definitions/ixartz-saas.d.cts +7 -0
- package/dist/services/definitions/ixartz-saas.d.cts.map +1 -0
- package/dist/services/definitions/ixartz-saas.d.mts +7 -0
- package/dist/services/definitions/ixartz-saas.d.mts.map +1 -0
- package/dist/services/definitions/ixartz-saas.mjs +87 -0
- package/dist/services/definitions/ixartz-saas.mjs.map +1 -0
- package/dist/services/definitions/meilisearch.cjs +11 -1
- package/dist/services/definitions/meilisearch.cjs.map +1 -1
- package/dist/services/definitions/meilisearch.mjs +11 -1
- package/dist/services/definitions/meilisearch.mjs.map +1 -1
- package/dist/services/definitions/minio.cjs +3 -1
- package/dist/services/definitions/minio.cjs.map +1 -1
- package/dist/services/definitions/minio.mjs +3 -1
- package/dist/services/definitions/minio.mjs.map +1 -1
- package/dist/services/definitions/mission-control.cjs +16 -2
- package/dist/services/definitions/mission-control.cjs.map +1 -1
- package/dist/services/definitions/mission-control.mjs +16 -2
- package/dist/services/definitions/mission-control.mjs.map +1 -1
- package/dist/services/definitions/n8n.cjs +11 -1
- package/dist/services/definitions/n8n.cjs.map +1 -1
- package/dist/services/definitions/n8n.mjs +11 -1
- package/dist/services/definitions/n8n.mjs.map +1 -1
- package/dist/services/definitions/ollama.cjs +3 -1
- package/dist/services/definitions/ollama.cjs.map +1 -1
- package/dist/services/definitions/ollama.mjs +3 -1
- package/dist/services/definitions/ollama.mjs.map +1 -1
- package/dist/services/definitions/open-saas.cjs +81 -0
- package/dist/services/definitions/open-saas.cjs.map +1 -0
- package/dist/services/definitions/open-saas.d.cts +7 -0
- package/dist/services/definitions/open-saas.d.cts.map +1 -0
- package/dist/services/definitions/open-saas.d.mts +7 -0
- package/dist/services/definitions/open-saas.d.mts.map +1 -0
- package/dist/services/definitions/open-saas.mjs +80 -0
- package/dist/services/definitions/open-saas.mjs.map +1 -0
- package/dist/services/definitions/qdrant.cjs +3 -1
- package/dist/services/definitions/qdrant.cjs.map +1 -1
- package/dist/services/definitions/qdrant.mjs +3 -1
- package/dist/services/definitions/qdrant.mjs.map +1 -1
- package/dist/services/definitions/searxng.cjs +8 -1
- package/dist/services/definitions/searxng.cjs.map +1 -1
- package/dist/services/definitions/searxng.mjs +8 -1
- package/dist/services/definitions/searxng.mjs.map +1 -1
- package/dist/services/definitions/uptime-kuma.cjs +8 -1
- package/dist/services/definitions/uptime-kuma.cjs.map +1 -1
- package/dist/services/definitions/uptime-kuma.mjs +8 -1
- package/dist/services/definitions/uptime-kuma.mjs.map +1 -1
- package/dist/services/registry.cjs +3 -0
- package/dist/services/registry.cjs.map +1 -1
- package/dist/services/registry.d.cts.map +1 -1
- package/dist/services/registry.d.mts.map +1 -1
- package/dist/services/registry.mjs +3 -0
- package/dist/services/registry.mjs.map +1 -1
- package/dist/services/registry.test.cjs +40 -33
- package/dist/services/registry.test.cjs.map +1 -1
- package/dist/services/registry.test.mjs +8 -1
- package/dist/services/registry.test.mjs.map +1 -1
- package/dist/{skill-manifest-BVUXU0__.mjs → skill-manifest-6XhrhWsG.mjs} +49 -1
- package/dist/{skill-manifest--IgY9REK.cjs.map → skill-manifest-6XhrhWsG.mjs.map} +1 -1
- package/dist/{skill-manifest--IgY9REK.cjs → skill-manifest-B8znSsym.cjs} +49 -1
- package/dist/{skill-manifest-BVUXU0__.mjs.map → skill-manifest-B8znSsym.cjs.map} +1 -1
- package/dist/skills/registry.cjs +3 -3
- package/dist/skills/registry.cjs.map +1 -1
- package/dist/skills/registry.mjs +3 -3
- package/dist/skills/registry.mjs.map +1 -1
- package/dist/skills/skill-manifest.cjs +1 -1
- package/dist/skills/skill-manifest.mjs +1 -1
- package/dist/{vi.2VT5v0um-DvC3SVNc.mjs → test.CTcmp4Su-ClCHJ3FA.mjs} +6793 -6403
- package/dist/test.CTcmp4Su-ClCHJ3FA.mjs.map +1 -0
- package/dist/{vi.2VT5v0um-CRqXre87.cjs → test.CTcmp4Su-DlzTarwH.cjs} +6793 -6403
- package/dist/test.CTcmp4Su-DlzTarwH.cjs.map +1 -0
- package/dist/track-analytics.cjs +50 -0
- package/dist/track-analytics.cjs.map +1 -0
- package/dist/track-analytics.d.cts +34 -0
- package/dist/track-analytics.d.cts.map +1 -0
- package/dist/track-analytics.d.mts +34 -0
- package/dist/track-analytics.d.mts.map +1 -0
- package/dist/track-analytics.mjs +48 -0
- package/dist/track-analytics.mjs.map +1 -0
- package/dist/track-analytics.test.cjs +91 -0
- package/dist/track-analytics.test.cjs.map +1 -0
- package/dist/track-analytics.test.d.cts +1 -0
- package/dist/track-analytics.test.d.mts +1 -0
- package/dist/track-analytics.test.mjs +92 -0
- package/dist/track-analytics.test.mjs.map +1 -0
- package/dist/types.cjs +7 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +12 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +12 -2
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +7 -0
- package/dist/types.mjs.map +1 -1
- package/dist/validator.test.cjs +15 -15
- package/dist/validator.test.cjs.map +1 -1
- package/dist/validator.test.mjs +2 -2
- package/dist/version-manager.cjs +1 -1
- package/dist/version-manager.cjs.map +1 -1
- package/dist/version-manager.mjs +1 -1
- package/dist/version-manager.mjs.map +1 -1
- package/dist/version-manager.test.cjs +40 -38
- package/dist/version-manager.test.cjs.map +1 -1
- package/dist/version-manager.test.mjs +7 -5
- package/dist/version-manager.test.mjs.map +1 -1
- package/package.json +4 -4
- package/src/__snapshots__/composer.snapshot.test.ts.snap +160 -0
- package/src/addon-stack.test.ts +490 -0
- package/src/addon-stack.ts +998 -0
- package/src/bare-metal-partition.test.ts +4 -3
- package/src/composer.test.ts +4 -2
- package/src/composer.ts +24 -5
- package/src/generate.test.ts +2 -1
- package/src/generate.ts +10 -1
- package/src/generators/clone-repos.test.ts +154 -0
- package/src/generators/clone-repos.ts +159 -0
- package/src/generators/postgres-init.ts +17 -0
- package/src/generators/scripts.test.ts +52 -4
- package/src/generators/scripts.ts +351 -3
- package/src/generators/stack-manifest.ts +4 -2
- package/src/index.ts +28 -2
- package/src/presets/registry.ts +241 -329
- package/src/resolver.test.ts +53 -15
- package/src/resolver.ts +13 -1
- package/src/schema.ts +216 -4
- package/src/services/definitions/apptension-saas.ts +84 -0
- package/src/services/definitions/boxyhq-saas.ts +84 -0
- package/src/services/definitions/browserless.ts +3 -0
- package/src/services/definitions/cmsaas-starter.ts +84 -0
- package/src/services/definitions/convex.ts +31 -0
- package/src/services/definitions/grafana.ts +9 -0
- package/src/services/definitions/index.ts +90 -70
- package/src/services/definitions/ixartz-saas.ts +84 -0
- package/src/services/definitions/meilisearch.ts +9 -0
- package/src/services/definitions/minio.ts +2 -0
- package/src/services/definitions/mission-control.ts +19 -2
- package/src/services/definitions/n8n.ts +9 -0
- package/src/services/definitions/ollama.ts +2 -0
- package/src/services/definitions/open-saas.ts +79 -0
- package/src/services/definitions/qdrant.ts +2 -0
- package/src/services/definitions/searxng.ts +3 -0
- package/src/services/definitions/uptime-kuma.ts +3 -0
- package/src/services/registry.test.ts +8 -0
- package/src/services/registry.ts +7 -0
- package/src/skills/manifest.json +64 -0
- package/src/skills/registry.ts +3 -3
- package/src/track-analytics.test.ts +82 -0
- package/src/track-analytics.ts +76 -0
- package/src/types.ts +29 -0
- package/src/version-manager.test.ts +10 -5
- package/src/version-manager.ts +1 -1
- package/dist/schema-B4c64P8N.d.cts.map +0 -1
- package/dist/schema-CXNhYci1.d.mts.map +0 -1
- package/dist/vi.2VT5v0um-CRqXre87.cjs.map +0 -1
- package/dist/vi.2VT5v0um-DvC3SVNc.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.test.mjs","names":[],"sources":["../src/generate.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { parse } from \"yaml\";\nimport { generate } from \"./generate.js\";\n\ndescribe(\"generate (end-to-end)\", () => {\n\tit(\"generates a minimal stack (redis only)\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"test-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Core files must be present\n\t\texpect(result.files).toHaveProperty(\"docker-compose.yml\");\n\t\texpect(result.files).toHaveProperty(\".env.example\");\n\t\texpect(result.files).toHaveProperty(\".env\");\n\t\texpect(result.files).toHaveProperty(\"README.md\");\n\n\t\t// docker-compose.yml must be valid YAML\n\t\tconst composed = parse(result.files[\"docker-compose.yml\"]!);\n\t\texpect(composed).toHaveProperty(\"services\");\n\n\t\t// .env.example should reference REDIS_PASSWORD\n\t\texpect(result.files[\".env.example\"]).toContain(\"REDIS_PASSWORD\");\n\n\t\t// README should mention the project name\n\t\texpect(result.files[\"README.md\"]).toContain(\"test-stack\");\n\n\t\t// At least one service resolved\n\t\texpect(result.metadata.serviceCount).toBeGreaterThanOrEqual(1);\n\t});\n\n\tit(\"generates research-agent stack from skill pack\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"research-stack\",\n\t\t\tservices: [],\n\t\t\tskillPacks: [\"research-agent\"],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Skill SKILL.md files for each skill in the research-agent pack\n\t\texpect(result.files).toHaveProperty(\"openclaw/workspace/skills/qdrant-memory/SKILL.md\");\n\t\texpect(result.files).toHaveProperty(\"openclaw/workspace/skills/searxng-search/SKILL.md\");\n\t\texpect(result.files).toHaveProperty(\"openclaw/workspace/skills/browserless-browse/SKILL.md\");\n\n\t\t// docker-compose.yml should contain the expected services\n\t\tconst composed = parse(result.files[\"docker-compose.yml\"]!);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).toHaveProperty(\"searxng\");\n\t\texpect(composed.services).toHaveProperty(\"browserless\");\n\t});\n\n\tit(\"generates full preset stack\", () => {\n\t\tconst fullServices = [\n\t\t\t\"redis\",\n\t\t\t\"postgresql\",\n\t\t\t\"qdrant\",\n\t\t\t\"n8n\",\n\t\t\t\"ffmpeg\",\n\t\t\t\"remotion\",\n\t\t\t\"minio\",\n\t\t\t\"caddy\",\n\t\t\t\"browserless\",\n\t\t\t\"searxng\",\n\t\t\t\"meilisearch\",\n\t\t\t\"uptime-kuma\",\n\t\t\t\"grafana\",\n\t\t\t\"prometheus\",\n\t\t\t\"ollama\",\n\t\t\t\"whisper\",\n\t\t\t\"gotify\",\n\t\t];\n\n\t\tconst result = generate({\n\t\t\tprojectName: \"full-stack\",\n\t\t\tservices: fullServices,\n\t\t\tskillPacks: [\n\t\t\t\t\"video-creator\",\n\t\t\t\t\"research-agent\",\n\t\t\t\t\"social-media\",\n\t\t\t\t\"dev-ops\",\n\t\t\t\t\"knowledge-base\",\n\t\t\t\t\"local-ai\",\n\t\t\t],\n\t\t\tproxy: \"caddy\",\n\t\t\tdomain: \"example.com\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Should have many files\n\t\tconst fileCount = Object.keys(result.files).length;\n\t\texpect(fileCount).toBeGreaterThan(10);\n\n\t\t// Should include the start script\n\t\texpect(result.files).toHaveProperty(\"scripts/start.sh\");\n\n\t\t// Should not throw (it already didn't if we got here)\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(5);\n\t});\n\n\tit(\"generates caddy config when proxy is caddy\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"caddy-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"caddy\",\n\t\t\tdomain: \"example.com\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\texpect(result.files).toHaveProperty(\"caddy/Caddyfile\");\n\t\texpect(result.files[\"caddy/Caddyfile\"]!.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"generates prometheus config when monitoring enabled\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"monitored-stack\",\n\t\t\tservices: [],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t\tmonitoring: true,\n\t\t});\n\n\t\texpect(result.files).toHaveProperty(\"prometheus/prometheus.yml\");\n\t\t// Verify it's valid YAML\n\t\tconst promConfig = parse(result.files[\"prometheus/prometheus.yml\"]!);\n\t\texpect(promConfig).toBeDefined();\n\t});\n\n\tit(\"generates La Suite Meet stack with all expected services\", () => {\n\t\tconst lasuiteMeetServices = [\n\t\t\t\"postgresql\",\n\t\t\t\"redis\",\n\t\t\t\"livekit\",\n\t\t\t\"lasuite-meet-backend\",\n\t\t\t\"lasuite-meet-frontend\",\n\t\t\t\"lasuite-meet-agents\",\n\t\t];\n\t\tconst result = generate({\n\t\t\tprojectName: \"lasuite-meet-stack\",\n\t\t\tservices: lasuiteMeetServices,\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Services may be split across main and profile compose files\n\t\tconst allServiceIds = new Set<string>();\n\t\tfor (const [filename, content] of Object.entries(result.files)) {\n\t\t\tif (filename.endsWith(\".yml\") && content) {\n\t\t\t\tconst doc = parse(content);\n\t\t\t\tif (doc?.services && typeof doc.services === \"object\") {\n\t\t\t\t\tfor (const id of Object.keys(doc.services)) {\n\t\t\t\t\t\tallServiceIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const id of lasuiteMeetServices) {\n\t\t\texpect(allServiceIds.has(id), `missing service ${id}`).toBe(true);\n\t\t}\n\t\texpect(result.metadata.serviceCount).toBe(lasuiteMeetServices.length);\n\t});\n\n\tit(\"generates bare-metal installer for Windows (install.ps1)\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"win-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"windows/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tdeploymentType: \"bare-metal\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\t\texpect(result.files).toHaveProperty(\"install.ps1\");\n\t\texpect(result.files[\"install.ps1\"]).toContain(\"docker compose\");\n\t\texpect(result.files[\"install.ps1\"]).toContain(\"PowerShell\");\n\t});\n\n\tit(\"generates bare-metal installer for Linux/macOS (install.sh)\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"linux-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tdeploymentType: \"bare-metal\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\t\texpect(result.files).toHaveProperty(\"install.sh\");\n\t\texpect(result.files[\"install.sh\"]).toContain(\"docker\");\n\t\texpect(result.files[\"install.sh\"]).toContain(\"compose\");\n\t});\n\n\tit(\"bare-metal with redis on Linux: native script, compose excludes redis, gateway has extra_hosts\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"bare-metal-redis\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tdeploymentType: \"bare-metal\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Native install script for Linux\n\t\texpect(result.files).toHaveProperty(\"native/install-linux.sh\");\n\t\texpect(result.files[\"native/install-linux.sh\"]).toContain(\"redis\");\n\n\t\t// Docker compose must NOT include redis (native); only gateway/openclaw\n\t\tconst composed = parse(result.files[\"docker-compose.yml\"]!);\n\t\texpect(composed.services).not.toHaveProperty(\"redis\");\n\t\texpect(composed.services).toHaveProperty(\"openclaw-gateway\");\n\n\t\t// Gateway must have extra_hosts for host.docker.internal\n\t\tconst gateway = composed.services[\"openclaw-gateway\"];\n\t\texpect(gateway).toBeDefined();\n\t\texpect(gateway.extra_hosts).toBeDefined();\n\t\texpect(\n\t\t\t(gateway.extra_hosts as string[]).some(\n\t\t\t\t(h: string) => h.includes(\"host.docker.internal\") && h.includes(\"host-gateway\"),\n\t\t\t),\n\t\t).toBe(true);\n\n\t\t// .env should set REDIS_HOST to host.docker.internal for gateway to reach native Redis\n\t\texpect(result.files[\".env\"]).toContain(\"REDIS_HOST=host.docker.internal\");\n\t});\n\n\tit(\"throws on conflicting services\", () => {\n\t\texpect(() =>\n\t\t\tgenerate({\n\t\t\t\tprojectName: \"conflict-stack\",\n\t\t\t\tservices: [\"redis\", \"valkey\"],\n\t\t\t\tskillPacks: [],\n\t\t\t\tproxy: \"none\",\n\t\t\t\tgpu: false,\n\t\t\t\tplatform: \"linux/amd64\",\n\t\t\t\tdeployment: \"local\",\n\t\t\t\tgenerateSecrets: true,\n\t\t\t\topenclawVersion: \"latest\",\n\t\t\t}),\n\t\t).toThrow();\n\t});\n\n\tit(\"generates scripts directory\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"scripts-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\tconst expectedScripts = [\n\t\t\t\"scripts/start.sh\",\n\t\t\t\"scripts/stop.sh\",\n\t\t\t\"scripts/update.sh\",\n\t\t\t\"scripts/backup.sh\",\n\t\t\t\"scripts/status.sh\",\n\t\t];\n\n\t\tfor (const script of expectedScripts) {\n\t\t\texpect(result.files).toHaveProperty(script);\n\t\t\texpect(result.files[script]!.length).toBeGreaterThan(0);\n\t\t}\n\t});\n\n\tit(\"all generated .env.example vars have comments\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"env-comments-stack\",\n\t\t\tservices: [\"redis\", \"qdrant\", \"n8n\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\tconst envExample = result.files[\".env.example\"]!;\n\t\tconst lines = envExample.split(\"\\n\");\n\n\t\t// Walk through lines: every non-empty, non-comment KEY=VALUE line should\n\t\t// have a preceding comment line (starting with #).\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst line = lines[i]!.trim();\n\t\t\tif (line === \"\" || line.startsWith(\"#\")) continue;\n\n\t\t\t// This line looks like KEY=VALUE\n\t\t\tif (line.includes(\"=\")) {\n\t\t\t\t// There must be a comment somewhere before it (look backwards for a # line)\n\t\t\t\tlet foundComment = false;\n\t\t\t\tfor (let j = i - 1; j >= 0; j--) {\n\t\t\t\t\tconst prev = lines[j]!.trim();\n\t\t\t\t\tif (prev === \"\") continue; // skip blank lines\n\t\t\t\t\tif (prev.startsWith(\"#\")) {\n\t\t\t\t\t\tfoundComment = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t// Hit another non-comment, non-empty line — no comment found\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\texpect(foundComment).toBe(true);\n\t\t\t}\n\t\t}\n\t});\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate.test.mjs","names":[],"sources":["../src/generate.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { parse } from \"yaml\";\nimport { generate } from \"./generate.js\";\n\ndescribe(\"generate (end-to-end)\", () => {\n\tit(\"generates a minimal stack (redis only)\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"test-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Core files must be present\n\t\texpect(result.files).toHaveProperty(\"docker-compose.yml\");\n\t\texpect(result.files).toHaveProperty(\".env.example\");\n\t\texpect(result.files).toHaveProperty(\".env\");\n\t\texpect(result.files).toHaveProperty(\"README.md\");\n\n\t\t// docker-compose.yml must be valid YAML\n\t\tconst composed = parse(result.files[\"docker-compose.yml\"]!);\n\t\texpect(composed).toHaveProperty(\"services\");\n\n\t\t// .env.example should reference REDIS_PASSWORD\n\t\texpect(result.files[\".env.example\"]).toContain(\"REDIS_PASSWORD\");\n\n\t\t// README should mention the project name\n\t\texpect(result.files[\"README.md\"]).toContain(\"test-stack\");\n\n\t\t// At least one service resolved\n\t\texpect(result.metadata.serviceCount).toBeGreaterThanOrEqual(1);\n\t});\n\n\tit(\"generates research-agent stack from skill pack\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"research-stack\",\n\t\t\tservices: [],\n\t\t\tskillPacks: [\"research-agent\"],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Skill SKILL.md files for each skill in the research-agent pack\n\t\texpect(result.files).toHaveProperty(\"openclaw/workspace/skills/qdrant-memory/SKILL.md\");\n\t\texpect(result.files).toHaveProperty(\"openclaw/workspace/skills/searxng-search/SKILL.md\");\n\t\texpect(result.files).toHaveProperty(\"openclaw/workspace/skills/browserless-browse/SKILL.md\");\n\n\t\t// docker-compose.yml should contain the expected services\n\t\tconst composed = parse(result.files[\"docker-compose.yml\"]!);\n\t\texpect(composed.services).toHaveProperty(\"qdrant\");\n\t\texpect(composed.services).toHaveProperty(\"searxng\");\n\t\texpect(composed.services).toHaveProperty(\"browserless\");\n\t});\n\n\tit(\"generates full preset stack\", () => {\n\t\tconst fullServices = [\n\t\t\t\"redis\",\n\t\t\t\"postgresql\",\n\t\t\t\"qdrant\",\n\t\t\t\"n8n\",\n\t\t\t\"ffmpeg\",\n\t\t\t\"remotion\",\n\t\t\t\"minio\",\n\t\t\t\"caddy\",\n\t\t\t\"browserless\",\n\t\t\t\"searxng\",\n\t\t\t\"meilisearch\",\n\t\t\t\"uptime-kuma\",\n\t\t\t\"grafana\",\n\t\t\t\"prometheus\",\n\t\t\t\"ollama\",\n\t\t\t\"whisper\",\n\t\t\t\"gotify\",\n\t\t];\n\n\t\tconst result = generate({\n\t\t\tprojectName: \"full-stack\",\n\t\t\tservices: fullServices,\n\t\t\tskillPacks: [\n\t\t\t\t\"video-creator\",\n\t\t\t\t\"research-agent\",\n\t\t\t\t\"social-media\",\n\t\t\t\t\"dev-ops\",\n\t\t\t\t\"knowledge-base\",\n\t\t\t\t\"local-ai\",\n\t\t\t],\n\t\t\tproxy: \"caddy\",\n\t\t\tdomain: \"example.com\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Should have many files\n\t\tconst fileCount = Object.keys(result.files).length;\n\t\texpect(fileCount).toBeGreaterThan(10);\n\n\t\t// Should include the start script\n\t\texpect(result.files).toHaveProperty(\"scripts/start.sh\");\n\n\t\t// Should not throw (it already didn't if we got here)\n\t\texpect(result.metadata.serviceCount).toBeGreaterThan(5);\n\t});\n\n\tit(\"generates caddy config when proxy is caddy\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"caddy-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"caddy\",\n\t\t\tdomain: \"example.com\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\texpect(result.files).toHaveProperty(\"caddy/Caddyfile\");\n\t\texpect(result.files[\"caddy/Caddyfile\"]!.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"generates prometheus config when monitoring enabled\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"monitored-stack\",\n\t\t\tservices: [],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t\tmonitoring: true,\n\t\t});\n\n\t\texpect(result.files).toHaveProperty(\"prometheus/prometheus.yml\");\n\t\t// Verify it's valid YAML\n\t\tconst promConfig = parse(result.files[\"prometheus/prometheus.yml\"]!);\n\t\texpect(promConfig).toBeDefined();\n\t});\n\n\tit(\"generates La Suite Meet stack with all expected services\", () => {\n\t\tconst lasuiteMeetServices = [\n\t\t\t\"postgresql\",\n\t\t\t\"redis\",\n\t\t\t\"livekit\",\n\t\t\t\"lasuite-meet-backend\",\n\t\t\t\"lasuite-meet-frontend\",\n\t\t\t\"lasuite-meet-agents\",\n\t\t];\n\t\tconst result = generate({\n\t\t\tprojectName: \"lasuite-meet-stack\",\n\t\t\tservices: lasuiteMeetServices,\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Services may be split across main and profile compose files\n\t\tconst allServiceIds = new Set<string>();\n\t\tfor (const [filename, content] of Object.entries(result.files)) {\n\t\t\tif (filename.endsWith(\".yml\") && content) {\n\t\t\t\tconst doc = parse(content);\n\t\t\t\tif (doc?.services && typeof doc.services === \"object\") {\n\t\t\t\t\tfor (const id of Object.keys(doc.services)) {\n\t\t\t\t\t\tallServiceIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const id of lasuiteMeetServices) {\n\t\t\texpect(allServiceIds.has(id), `missing service ${id}`).toBe(true);\n\t\t}\n\t\t// Service count includes user services + mandatory platform services (convex, mission-control, tailscale)\n\t\texpect(result.metadata.serviceCount).toBeGreaterThanOrEqual(lasuiteMeetServices.length);\n\t});\n\n\tit(\"generates bare-metal installer for Windows (install.ps1)\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"win-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"windows/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tdeploymentType: \"bare-metal\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\t\texpect(result.files).toHaveProperty(\"install.ps1\");\n\t\texpect(result.files[\"install.ps1\"]).toContain(\"docker compose\");\n\t\texpect(result.files[\"install.ps1\"]).toContain(\"PowerShell\");\n\t});\n\n\tit(\"generates bare-metal installer for Linux/macOS (install.sh)\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"linux-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tdeploymentType: \"bare-metal\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\t\texpect(result.files).toHaveProperty(\"install.sh\");\n\t\texpect(result.files[\"install.sh\"]).toContain(\"docker\");\n\t\texpect(result.files[\"install.sh\"]).toContain(\"compose\");\n\t});\n\n\tit(\"bare-metal with redis on Linux: native script, compose excludes redis, gateway has extra_hosts\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"bare-metal-redis\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tdeploymentType: \"bare-metal\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\t// Native install script for Linux\n\t\texpect(result.files).toHaveProperty(\"native/install-linux.sh\");\n\t\texpect(result.files[\"native/install-linux.sh\"]).toContain(\"redis\");\n\n\t\t// Docker compose must NOT include redis (native); only gateway/openclaw\n\t\tconst composed = parse(result.files[\"docker-compose.yml\"]!);\n\t\texpect(composed.services).not.toHaveProperty(\"redis\");\n\t\texpect(composed.services).toHaveProperty(\"openclaw-gateway\");\n\n\t\t// Gateway must have extra_hosts for host.docker.internal\n\t\tconst gateway = composed.services[\"openclaw-gateway\"];\n\t\texpect(gateway).toBeDefined();\n\t\texpect(gateway.extra_hosts).toBeDefined();\n\t\texpect(\n\t\t\t(gateway.extra_hosts as string[]).some(\n\t\t\t\t(h: string) => h.includes(\"host.docker.internal\") && h.includes(\"host-gateway\"),\n\t\t\t),\n\t\t).toBe(true);\n\n\t\t// .env should set REDIS_HOST to host.docker.internal for gateway to reach native Redis\n\t\texpect(result.files[\".env\"]).toContain(\"REDIS_HOST=host.docker.internal\");\n\t});\n\n\tit(\"throws on conflicting services\", () => {\n\t\texpect(() =>\n\t\t\tgenerate({\n\t\t\t\tprojectName: \"conflict-stack\",\n\t\t\t\tservices: [\"redis\", \"valkey\"],\n\t\t\t\tskillPacks: [],\n\t\t\t\tproxy: \"none\",\n\t\t\t\tgpu: false,\n\t\t\t\tplatform: \"linux/amd64\",\n\t\t\t\tdeployment: \"local\",\n\t\t\t\tgenerateSecrets: true,\n\t\t\t\topenclawVersion: \"latest\",\n\t\t\t}),\n\t\t).toThrow();\n\t});\n\n\tit(\"generates scripts directory\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"scripts-stack\",\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\tconst expectedScripts = [\n\t\t\t\"scripts/start.sh\",\n\t\t\t\"scripts/stop.sh\",\n\t\t\t\"scripts/update.sh\",\n\t\t\t\"scripts/backup.sh\",\n\t\t\t\"scripts/status.sh\",\n\t\t];\n\n\t\tfor (const script of expectedScripts) {\n\t\t\texpect(result.files).toHaveProperty(script);\n\t\t\texpect(result.files[script]!.length).toBeGreaterThan(0);\n\t\t}\n\t});\n\n\tit(\"all generated .env.example vars have comments\", () => {\n\t\tconst result = generate({\n\t\t\tprojectName: \"env-comments-stack\",\n\t\t\tservices: [\"redis\", \"qdrant\", \"n8n\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tdeployment: \"local\",\n\t\t\tgenerateSecrets: true,\n\t\t\topenclawVersion: \"latest\",\n\t\t});\n\n\t\tconst envExample = result.files[\".env.example\"]!;\n\t\tconst lines = envExample.split(\"\\n\");\n\n\t\t// Walk through lines: every non-empty, non-comment KEY=VALUE line should\n\t\t// have a preceding comment line (starting with #).\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst line = lines[i]!.trim();\n\t\t\tif (line === \"\" || line.startsWith(\"#\")) continue;\n\n\t\t\t// This line looks like KEY=VALUE\n\t\t\tif (line.includes(\"=\")) {\n\t\t\t\t// There must be a comment somewhere before it (look backwards for a # line)\n\t\t\t\tlet foundComment = false;\n\t\t\t\tfor (let j = i - 1; j >= 0; j--) {\n\t\t\t\t\tconst prev = lines[j]!.trim();\n\t\t\t\t\tif (prev === \"\") continue; // skip blank lines\n\t\t\t\t\tif (prev.startsWith(\"#\")) {\n\t\t\t\t\t\tfoundComment = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t// Hit another non-comment, non-empty line — no comment found\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\texpect(foundComment).toBe(true);\n\t\t\t}\n\t\t}\n\t});\n});\n"],"mappings":"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"}
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_test_CTcmp4Su = require("../test.CTcmp4Su-DlzTarwH.cjs");
|
|
2
2
|
const require_generators_bare_metal_install = require("./bare-metal-install.cjs");
|
|
3
3
|
//#region src/generators/bare-metal-install.test.ts
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
require_test_CTcmp4Su.describe("generateBareMetalInstall", () => {
|
|
5
|
+
require_test_CTcmp4Su.it("returns install.ps1 for windows/amd64", () => {
|
|
6
6
|
const result = require_generators_bare_metal_install.generateBareMetalInstall({
|
|
7
7
|
platform: "windows/amd64",
|
|
8
8
|
projectName: "my-stack"
|
|
9
9
|
});
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
require_test_CTcmp4Su.globalExpect(result).toHaveProperty("install.ps1");
|
|
11
|
+
require_test_CTcmp4Su.globalExpect(Object.keys(result)).toHaveLength(1);
|
|
12
|
+
require_test_CTcmp4Su.globalExpect(result["install.ps1"]).toContain("docker compose");
|
|
13
|
+
require_test_CTcmp4Su.globalExpect(result["install.ps1"]).toContain("PowerShell");
|
|
14
14
|
});
|
|
15
|
-
|
|
15
|
+
require_test_CTcmp4Su.it("returns install.sh for linux/amd64", () => {
|
|
16
16
|
const result = require_generators_bare_metal_install.generateBareMetalInstall({
|
|
17
17
|
platform: "linux/amd64",
|
|
18
18
|
projectName: "my-stack"
|
|
19
19
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
require_test_CTcmp4Su.globalExpect(result).toHaveProperty("install.sh");
|
|
21
|
+
require_test_CTcmp4Su.globalExpect(Object.keys(result)).toHaveLength(1);
|
|
22
|
+
require_test_CTcmp4Su.globalExpect(result["install.sh"]).toContain("docker");
|
|
23
|
+
require_test_CTcmp4Su.globalExpect(result["install.sh"]).toContain("compose");
|
|
24
24
|
});
|
|
25
|
-
|
|
25
|
+
require_test_CTcmp4Su.it("returns install.sh for linux/arm64", () => {
|
|
26
26
|
const result = require_generators_bare_metal_install.generateBareMetalInstall({
|
|
27
27
|
platform: "linux/arm64",
|
|
28
28
|
projectName: "my-stack"
|
|
29
29
|
});
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
require_test_CTcmp4Su.globalExpect(result).toHaveProperty("install.sh");
|
|
31
|
+
require_test_CTcmp4Su.globalExpect(result["install.sh"]).toContain("docker");
|
|
32
32
|
});
|
|
33
|
-
|
|
33
|
+
require_test_CTcmp4Su.it("returns install.sh for macos/amd64 and macos/arm64", () => {
|
|
34
34
|
for (const platform of ["macos/amd64", "macos/arm64"]) {
|
|
35
35
|
const result = require_generators_bare_metal_install.generateBareMetalInstall({
|
|
36
36
|
platform,
|
|
37
37
|
projectName: "my-stack"
|
|
38
38
|
});
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
require_test_CTcmp4Su.globalExpect(result).toHaveProperty("install.sh");
|
|
40
|
+
require_test_CTcmp4Su.globalExpect(result["install.sh"]).toContain("Docker");
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bare-metal-install.test.cjs","names":["describe","generateBareMetalInstall"],"sources":["../../src/generators/bare-metal-install.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { generateBareMetalInstall } from \"./bare-metal-install.js\";\n\ndescribe(\"generateBareMetalInstall\", () => {\n\tit(\"returns install.ps1 for windows/amd64\", () => {\n\t\tconst result = generateBareMetalInstall({\n\t\t\tplatform: \"windows/amd64\",\n\t\t\tprojectName: \"my-stack\",\n\t\t});\n\t\texpect(result).toHaveProperty(\"install.ps1\");\n\t\texpect(Object.keys(result)).toHaveLength(1);\n\t\texpect(result[\"install.ps1\"]).toContain(\"docker compose\");\n\t\texpect(result[\"install.ps1\"]).toContain(\"PowerShell\");\n\t});\n\n\tit(\"returns install.sh for linux/amd64\", () => {\n\t\tconst result = generateBareMetalInstall({\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tprojectName: \"my-stack\",\n\t\t});\n\t\texpect(result).toHaveProperty(\"install.sh\");\n\t\texpect(Object.keys(result)).toHaveLength(1);\n\t\texpect(result[\"install.sh\"]).toContain(\"docker\");\n\t\texpect(result[\"install.sh\"]).toContain(\"compose\");\n\t});\n\n\tit(\"returns install.sh for linux/arm64\", () => {\n\t\tconst result = generateBareMetalInstall({\n\t\t\tplatform: \"linux/arm64\",\n\t\t\tprojectName: \"my-stack\",\n\t\t});\n\t\texpect(result).toHaveProperty(\"install.sh\");\n\t\texpect(result[\"install.sh\"]).toContain(\"docker\");\n\t});\n\n\tit(\"returns install.sh for macos/amd64 and macos/arm64\", () => {\n\t\tfor (const platform of [\"macos/amd64\", \"macos/arm64\"] as const) {\n\t\t\tconst result = generateBareMetalInstall({ platform, projectName: \"my-stack\" });\n\t\t\texpect(result).toHaveProperty(\"install.sh\");\n\t\t\texpect(result[\"install.sh\"]).toContain(\"Docker\");\n\t\t}\n\t});\n});\n"],"mappings":";;;AAGAA,
|
|
1
|
+
{"version":3,"file":"bare-metal-install.test.cjs","names":["describe","generateBareMetalInstall"],"sources":["../../src/generators/bare-metal-install.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { generateBareMetalInstall } from \"./bare-metal-install.js\";\n\ndescribe(\"generateBareMetalInstall\", () => {\n\tit(\"returns install.ps1 for windows/amd64\", () => {\n\t\tconst result = generateBareMetalInstall({\n\t\t\tplatform: \"windows/amd64\",\n\t\t\tprojectName: \"my-stack\",\n\t\t});\n\t\texpect(result).toHaveProperty(\"install.ps1\");\n\t\texpect(Object.keys(result)).toHaveLength(1);\n\t\texpect(result[\"install.ps1\"]).toContain(\"docker compose\");\n\t\texpect(result[\"install.ps1\"]).toContain(\"PowerShell\");\n\t});\n\n\tit(\"returns install.sh for linux/amd64\", () => {\n\t\tconst result = generateBareMetalInstall({\n\t\t\tplatform: \"linux/amd64\",\n\t\t\tprojectName: \"my-stack\",\n\t\t});\n\t\texpect(result).toHaveProperty(\"install.sh\");\n\t\texpect(Object.keys(result)).toHaveLength(1);\n\t\texpect(result[\"install.sh\"]).toContain(\"docker\");\n\t\texpect(result[\"install.sh\"]).toContain(\"compose\");\n\t});\n\n\tit(\"returns install.sh for linux/arm64\", () => {\n\t\tconst result = generateBareMetalInstall({\n\t\t\tplatform: \"linux/arm64\",\n\t\t\tprojectName: \"my-stack\",\n\t\t});\n\t\texpect(result).toHaveProperty(\"install.sh\");\n\t\texpect(result[\"install.sh\"]).toContain(\"docker\");\n\t});\n\n\tit(\"returns install.sh for macos/amd64 and macos/arm64\", () => {\n\t\tfor (const platform of [\"macos/amd64\", \"macos/arm64\"] as const) {\n\t\t\tconst result = generateBareMetalInstall({ platform, projectName: \"my-stack\" });\n\t\t\texpect(result).toHaveProperty(\"install.sh\");\n\t\t\texpect(result[\"install.sh\"]).toContain(\"Docker\");\n\t\t}\n\t});\n});\n"],"mappings":";;;AAGAA,sBAAAA,SAAS,kCAAkC;AAC1C,uBAAA,GAAG,+CAA+C;EACjD,MAAM,SAASC,sCAAAA,yBAAyB;GACvC,UAAU;GACV,aAAa;GACb,CAAC;AACF,wBAAA,aAAO,OAAO,CAAC,eAAe,cAAc;AAC5C,wBAAA,aAAO,OAAO,KAAK,OAAO,CAAC,CAAC,aAAa,EAAE;AAC3C,wBAAA,aAAO,OAAO,eAAe,CAAC,UAAU,iBAAiB;AACzD,wBAAA,aAAO,OAAO,eAAe,CAAC,UAAU,aAAa;GACpD;AAEF,uBAAA,GAAG,4CAA4C;EAC9C,MAAM,SAASA,sCAAAA,yBAAyB;GACvC,UAAU;GACV,aAAa;GACb,CAAC;AACF,wBAAA,aAAO,OAAO,CAAC,eAAe,aAAa;AAC3C,wBAAA,aAAO,OAAO,KAAK,OAAO,CAAC,CAAC,aAAa,EAAE;AAC3C,wBAAA,aAAO,OAAO,cAAc,CAAC,UAAU,SAAS;AAChD,wBAAA,aAAO,OAAO,cAAc,CAAC,UAAU,UAAU;GAChD;AAEF,uBAAA,GAAG,4CAA4C;EAC9C,MAAM,SAASA,sCAAAA,yBAAyB;GACvC,UAAU;GACV,aAAa;GACb,CAAC;AACF,wBAAA,aAAO,OAAO,CAAC,eAAe,aAAa;AAC3C,wBAAA,aAAO,OAAO,cAAc,CAAC,UAAU,SAAS;GAC/C;AAEF,uBAAA,GAAG,4DAA4D;AAC9D,OAAK,MAAM,YAAY,CAAC,eAAe,cAAc,EAAW;GAC/D,MAAM,SAASA,sCAAAA,yBAAyB;IAAE;IAAU,aAAa;IAAY,CAAC;AAC9E,yBAAA,aAAO,OAAO,CAAC,eAAe,aAAa;AAC3C,yBAAA,aAAO,OAAO,cAAc,CAAC,UAAU,SAAS;;GAEhD;EACD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as describe, r as it, t as globalExpect } from "../
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "../test.CTcmp4Su-ClCHJ3FA.mjs";
|
|
2
2
|
import { generateBareMetalInstall } from "./bare-metal-install.mjs";
|
|
3
3
|
//#region src/generators/bare-metal-install.test.ts
|
|
4
4
|
describe("generateBareMetalInstall", () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_test_CTcmp4Su = require("../test.CTcmp4Su-DlzTarwH.cjs");
|
|
2
2
|
const require_generate = require("../generate.cjs");
|
|
3
3
|
//#region src/generators/caddy.test.ts
|
|
4
|
-
|
|
4
|
+
require_test_CTcmp4Su.describe("generateCaddyfile (via generate)", () => {
|
|
5
5
|
const baseInput = {
|
|
6
6
|
projectName: "caddy-test",
|
|
7
7
|
services: ["redis", "n8n"],
|
|
@@ -14,28 +14,28 @@ require_vi_2VT5v0um.describe("generateCaddyfile (via generate)", () => {
|
|
|
14
14
|
generateSecrets: true,
|
|
15
15
|
openclawVersion: "latest"
|
|
16
16
|
};
|
|
17
|
-
|
|
17
|
+
require_test_CTcmp4Su.it("generates Caddyfile when proxy is caddy", () => {
|
|
18
18
|
const result = require_generate.generate(baseInput);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
require_test_CTcmp4Su.globalExpect(result.files).toHaveProperty("caddy/Caddyfile");
|
|
20
|
+
require_test_CTcmp4Su.globalExpect(result.files["caddy/Caddyfile"].length).toBeGreaterThan(0);
|
|
21
21
|
});
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
require_test_CTcmp4Su.it("does not generate Caddyfile when proxy is none", () => {
|
|
23
|
+
require_test_CTcmp4Su.globalExpect(require_generate.generate({
|
|
24
24
|
...baseInput,
|
|
25
25
|
proxy: "none"
|
|
26
26
|
}).files).not.toHaveProperty("caddy/Caddyfile");
|
|
27
27
|
});
|
|
28
|
-
|
|
28
|
+
require_test_CTcmp4Su.it("Caddyfile contains the domain", () => {
|
|
29
29
|
const caddyfile = require_generate.generate(baseInput).files["caddy/Caddyfile"];
|
|
30
|
-
|
|
30
|
+
require_test_CTcmp4Su.globalExpect(caddyfile).toContain("example.com");
|
|
31
31
|
});
|
|
32
|
-
|
|
32
|
+
require_test_CTcmp4Su.it("Caddyfile includes reverse proxy directives for services with exposed ports", () => {
|
|
33
33
|
const caddyfile = require_generate.generate(baseInput).files["caddy/Caddyfile"];
|
|
34
|
-
|
|
34
|
+
require_test_CTcmp4Su.globalExpect(caddyfile).toContain("n8n");
|
|
35
35
|
});
|
|
36
|
-
|
|
36
|
+
require_test_CTcmp4Su.it("Caddyfile includes TLS configuration", () => {
|
|
37
37
|
const caddyfile = require_generate.generate(baseInput).files["caddy/Caddyfile"];
|
|
38
|
-
|
|
38
|
+
require_test_CTcmp4Su.globalExpect(caddyfile.length).toBeGreaterThan(50);
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caddy.test.cjs","names":["describe","generate"],"sources":["../../src/generators/caddy.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { generate } from \"../generate.js\";\n\ndescribe(\"generateCaddyfile (via generate)\", () => {\n\tconst baseInput = {\n\t\tprojectName: \"caddy-test\",\n\t\tservices: [\"redis\", \"n8n\"],\n\t\tskillPacks: [] as string[],\n\t\tproxy: \"caddy\" as const,\n\t\tdomain: \"example.com\",\n\t\tgpu: false,\n\t\tplatform: \"linux/amd64\" as const,\n\t\tdeployment: \"local\" as const,\n\t\tgenerateSecrets: true,\n\t\topenclawVersion: \"latest\",\n\t};\n\n\tit(\"generates Caddyfile when proxy is caddy\", () => {\n\t\tconst result = generate(baseInput);\n\n\t\texpect(result.files).toHaveProperty(\"caddy/Caddyfile\");\n\t\texpect(result.files[\"caddy/Caddyfile\"]!.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"does not generate Caddyfile when proxy is none\", () => {\n\t\tconst result = generate({\n\t\t\t...baseInput,\n\t\t\tproxy: \"none\",\n\t\t});\n\n\t\texpect(result.files).not.toHaveProperty(\"caddy/Caddyfile\");\n\t});\n\n\tit(\"Caddyfile contains the domain\", () => {\n\t\tconst result = generate(baseInput);\n\t\tconst caddyfile = result.files[\"caddy/Caddyfile\"]!;\n\n\t\texpect(caddyfile).toContain(\"example.com\");\n\t});\n\n\tit(\"Caddyfile includes reverse proxy directives for services with exposed ports\", () => {\n\t\tconst result = generate(baseInput);\n\t\tconst caddyfile = result.files[\"caddy/Caddyfile\"]!;\n\n\t\t// Should reference n8n since it has exposed ports\n\t\texpect(caddyfile).toContain(\"n8n\");\n\t});\n\n\tit(\"Caddyfile includes TLS configuration\", () => {\n\t\tconst result = generate(baseInput);\n\t\tconst caddyfile = result.files[\"caddy/Caddyfile\"]!;\n\n\t\t// Caddy auto-enables TLS, so should reference HTTPS or TLS\n\t\texpect(caddyfile.length).toBeGreaterThan(50);\n\t});\n});\n"],"mappings":";;;AAGAA,
|
|
1
|
+
{"version":3,"file":"caddy.test.cjs","names":["describe","generate"],"sources":["../../src/generators/caddy.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { generate } from \"../generate.js\";\n\ndescribe(\"generateCaddyfile (via generate)\", () => {\n\tconst baseInput = {\n\t\tprojectName: \"caddy-test\",\n\t\tservices: [\"redis\", \"n8n\"],\n\t\tskillPacks: [] as string[],\n\t\tproxy: \"caddy\" as const,\n\t\tdomain: \"example.com\",\n\t\tgpu: false,\n\t\tplatform: \"linux/amd64\" as const,\n\t\tdeployment: \"local\" as const,\n\t\tgenerateSecrets: true,\n\t\topenclawVersion: \"latest\",\n\t};\n\n\tit(\"generates Caddyfile when proxy is caddy\", () => {\n\t\tconst result = generate(baseInput);\n\n\t\texpect(result.files).toHaveProperty(\"caddy/Caddyfile\");\n\t\texpect(result.files[\"caddy/Caddyfile\"]!.length).toBeGreaterThan(0);\n\t});\n\n\tit(\"does not generate Caddyfile when proxy is none\", () => {\n\t\tconst result = generate({\n\t\t\t...baseInput,\n\t\t\tproxy: \"none\",\n\t\t});\n\n\t\texpect(result.files).not.toHaveProperty(\"caddy/Caddyfile\");\n\t});\n\n\tit(\"Caddyfile contains the domain\", () => {\n\t\tconst result = generate(baseInput);\n\t\tconst caddyfile = result.files[\"caddy/Caddyfile\"]!;\n\n\t\texpect(caddyfile).toContain(\"example.com\");\n\t});\n\n\tit(\"Caddyfile includes reverse proxy directives for services with exposed ports\", () => {\n\t\tconst result = generate(baseInput);\n\t\tconst caddyfile = result.files[\"caddy/Caddyfile\"]!;\n\n\t\t// Should reference n8n since it has exposed ports\n\t\texpect(caddyfile).toContain(\"n8n\");\n\t});\n\n\tit(\"Caddyfile includes TLS configuration\", () => {\n\t\tconst result = generate(baseInput);\n\t\tconst caddyfile = result.files[\"caddy/Caddyfile\"]!;\n\n\t\t// Caddy auto-enables TLS, so should reference HTTPS or TLS\n\t\texpect(caddyfile.length).toBeGreaterThan(50);\n\t});\n});\n"],"mappings":";;;AAGAA,sBAAAA,SAAS,0CAA0C;CAClD,MAAM,YAAY;EACjB,aAAa;EACb,UAAU,CAAC,SAAS,MAAM;EAC1B,YAAY,EAAE;EACd,OAAO;EACP,QAAQ;EACR,KAAK;EACL,UAAU;EACV,YAAY;EACZ,iBAAiB;EACjB,iBAAiB;EACjB;AAED,uBAAA,GAAG,iDAAiD;EACnD,MAAM,SAASC,iBAAAA,SAAS,UAAU;AAElC,wBAAA,aAAO,OAAO,MAAM,CAAC,eAAe,kBAAkB;AACtD,wBAAA,aAAO,OAAO,MAAM,mBAAoB,OAAO,CAAC,gBAAgB,EAAE;GACjE;AAEF,uBAAA,GAAG,wDAAwD;AAM1D,wBAAA,aALeA,iBAAAA,SAAS;GACvB,GAAG;GACH,OAAO;GACP,CAAC,CAEY,MAAM,CAAC,IAAI,eAAe,kBAAkB;GACzD;AAEF,uBAAA,GAAG,uCAAuC;EAEzC,MAAM,YADSA,iBAAAA,SAAS,UAAU,CACT,MAAM;AAE/B,wBAAA,aAAO,UAAU,CAAC,UAAU,cAAc;GACzC;AAEF,uBAAA,GAAG,qFAAqF;EAEvF,MAAM,YADSA,iBAAAA,SAAS,UAAU,CACT,MAAM;AAG/B,wBAAA,aAAO,UAAU,CAAC,UAAU,MAAM;GACjC;AAEF,uBAAA,GAAG,8CAA8C;EAEhD,MAAM,YADSA,iBAAAA,SAAS,UAAU,CACT,MAAM;AAG/B,wBAAA,aAAO,UAAU,OAAO,CAAC,gBAAgB,GAAG;GAC3C;EACD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as describe, r as it, t as globalExpect } from "../
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "../test.CTcmp4Su-ClCHJ3FA.mjs";
|
|
2
2
|
import { generate } from "../generate.mjs";
|
|
3
3
|
//#region src/generators/caddy.test.ts
|
|
4
4
|
describe("generateCaddyfile (via generate)", () => {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/generators/clone-repos.ts
|
|
3
|
+
/**
|
|
4
|
+
* Generates clone scripts for git-based services (SaaS boilerplates).
|
|
5
|
+
* Returns empty object if no git-based services exist in the resolved stack.
|
|
6
|
+
*/
|
|
7
|
+
function generateCloneScripts(resolved) {
|
|
8
|
+
const gitServices = resolved.services.filter((s) => s.definition.gitSource && s.definition.buildContext);
|
|
9
|
+
if (gitServices.length === 0) return {};
|
|
10
|
+
const files = {};
|
|
11
|
+
files["scripts/clone-repos.sh"] = `#!/usr/bin/env bash
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
# ─── Clone/Update Git-Based Service Repositories ────────────────────────────
|
|
15
|
+
# Idempotent: clones if missing, pulls if already present.
|
|
16
|
+
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
19
|
+
REPOS_DIR="$PROJECT_DIR/repos"
|
|
20
|
+
|
|
21
|
+
# ── Colour helpers ──────────────────────────────────────────────────────────
|
|
22
|
+
if [ -t 1 ]; then
|
|
23
|
+
GREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; CYAN='\\033[0;36m'; RED='\\033[0;31m'; NC='\\033[0m'
|
|
24
|
+
else
|
|
25
|
+
GREEN=''; YELLOW=''; CYAN=''; RED=''; NC=''
|
|
26
|
+
fi
|
|
27
|
+
info() { echo -e "\${CYAN}i $*\${NC}"; }
|
|
28
|
+
ok() { echo -e "\${GREEN}✓ $*\${NC}"; }
|
|
29
|
+
warn() { echo -e "\${YELLOW}⚠ $*\${NC}"; }
|
|
30
|
+
err() { echo -e "\${RED}✗ $*\${NC}" >&2; }
|
|
31
|
+
|
|
32
|
+
# ── Check git ───────────────────────────────────────────────────────────────
|
|
33
|
+
if ! command -v git &> /dev/null; then
|
|
34
|
+
err "git is not installed. Please install git first."
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
mkdir -p "$REPOS_DIR"
|
|
39
|
+
|
|
40
|
+
clone_or_update() {
|
|
41
|
+
local name="$1" url="$2" branch="\${3:-}"
|
|
42
|
+
local dir="$REPOS_DIR/$name"
|
|
43
|
+
|
|
44
|
+
if [ -d "$dir/.git" ]; then
|
|
45
|
+
info "Updating $name..."
|
|
46
|
+
git -C "$dir" pull --ff-only 2>/dev/null || warn "Could not fast-forward $name (you may have local changes)"
|
|
47
|
+
else
|
|
48
|
+
info "Cloning $name..."
|
|
49
|
+
if [ -n "$branch" ]; then
|
|
50
|
+
git clone --depth 1 --branch "$branch" "$url" "$dir"
|
|
51
|
+
else
|
|
52
|
+
git clone --depth 1 "$url" "$dir"
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
echo ""
|
|
58
|
+
info "Cloning/updating SaaS boilerplate repositories..."
|
|
59
|
+
echo ""
|
|
60
|
+
|
|
61
|
+
${gitServices.map((s) => {
|
|
62
|
+
const gs = s.definition.gitSource;
|
|
63
|
+
const branchArg = gs.branch ? `"${gs.branch}"` : "\"\"";
|
|
64
|
+
let block = `clone_or_update "${s.definition.id}" "${gs.repoUrl}" ${branchArg}`;
|
|
65
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
66
|
+
const cmds = gs.postCloneCommands.map((cmd) => ` (cd "$REPOS_DIR/${s.definition.id}${gs.subdirectory ? `/${gs.subdirectory}` : ""}" && ${cmd})`).join("\n");
|
|
67
|
+
block += `\n${cmds}`;
|
|
68
|
+
}
|
|
69
|
+
return block;
|
|
70
|
+
}).join("\n\n")}
|
|
71
|
+
|
|
72
|
+
echo ""
|
|
73
|
+
ok "All repositories ready."
|
|
74
|
+
`;
|
|
75
|
+
files["scripts/clone-repos.ps1"] = `#Requires -Version 5.1
|
|
76
|
+
<#
|
|
77
|
+
.SYNOPSIS
|
|
78
|
+
Clone/update git-based service repositories.
|
|
79
|
+
Idempotent: clones if missing, pulls if already present.
|
|
80
|
+
#>
|
|
81
|
+
$ErrorActionPreference = "Stop"
|
|
82
|
+
|
|
83
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
84
|
+
$ProjectDir = Split-Path -Parent $ScriptDir
|
|
85
|
+
$ReposDir = Join-Path $ProjectDir "repos"
|
|
86
|
+
|
|
87
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
88
|
+
Write-Error "git is not installed. Please install git first."
|
|
89
|
+
exit 1
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (-not (Test-Path $ReposDir)) { New-Item -ItemType Directory -Path $ReposDir -Force | Out-Null }
|
|
93
|
+
|
|
94
|
+
function Clone-OrUpdate {
|
|
95
|
+
param(
|
|
96
|
+
[string]$Name,
|
|
97
|
+
[string]$Url,
|
|
98
|
+
[string]$Branch = ""
|
|
99
|
+
)
|
|
100
|
+
$dir = Join-Path $ReposDir $Name
|
|
101
|
+
|
|
102
|
+
if (Test-Path (Join-Path $dir ".git")) {
|
|
103
|
+
Write-Host " Updating $Name..." -ForegroundColor Cyan
|
|
104
|
+
git -C $dir pull --ff-only 2>$null
|
|
105
|
+
if ($LASTEXITCODE -ne 0) { Write-Warning "Could not fast-forward $Name" }
|
|
106
|
+
} else {
|
|
107
|
+
Write-Host " Cloning $Name..." -ForegroundColor Cyan
|
|
108
|
+
if ($Branch) {
|
|
109
|
+
git clone --depth 1 --branch $Branch $Url $dir
|
|
110
|
+
} else {
|
|
111
|
+
git clone --depth 1 $Url $dir
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
Write-Host ""
|
|
117
|
+
Write-Host "Cloning/updating SaaS boilerplate repositories..." -ForegroundColor Cyan
|
|
118
|
+
Write-Host ""
|
|
119
|
+
|
|
120
|
+
${gitServices.map((s) => {
|
|
121
|
+
const gs = s.definition.gitSource;
|
|
122
|
+
const branchArg = gs.branch ? ` -Branch "${gs.branch}"` : "";
|
|
123
|
+
let block = `Clone-OrUpdate -Name "${s.definition.id}" -Url "${gs.repoUrl}"${branchArg}`;
|
|
124
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
125
|
+
const subdir = gs.subdirectory ? `/${gs.subdirectory}` : "";
|
|
126
|
+
const cmds = gs.postCloneCommands.map((cmd) => `Push-Location "$ReposDir/${s.definition.id}${subdir}"; ${cmd}; Pop-Location`).join("\n");
|
|
127
|
+
block += `\n${cmds}`;
|
|
128
|
+
}
|
|
129
|
+
return block;
|
|
130
|
+
}).join("\n\n")}
|
|
131
|
+
|
|
132
|
+
Write-Host ""
|
|
133
|
+
Write-Host "All repositories ready." -ForegroundColor Green
|
|
134
|
+
`;
|
|
135
|
+
return files;
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
exports.generateCloneScripts = generateCloneScripts;
|
|
139
|
+
|
|
140
|
+
//# sourceMappingURL=clone-repos.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clone-repos.cjs","names":[],"sources":["../../src/generators/clone-repos.ts"],"sourcesContent":["import type { ResolverOutput } from \"../types.js\";\n\n/**\n * Generates clone scripts for git-based services (SaaS boilerplates).\n * Returns empty object if no git-based services exist in the resolved stack.\n */\nexport function generateCloneScripts(resolved: ResolverOutput): Record<string, string> {\n\tconst gitServices = resolved.services.filter(\n\t\t(s) => s.definition.gitSource && s.definition.buildContext,\n\t);\n\n\tif (gitServices.length === 0) return {};\n\n\tconst files: Record<string, string> = {};\n\n\t// ── scripts/clone-repos.sh ─────────────────────────────────────────────\n\n\tconst bashEntries = gitServices\n\t\t.map((s) => {\n\t\t\tconst gs = s.definition.gitSource!;\n\t\t\tconst branchArg = gs.branch ? `\"${gs.branch}\"` : '\"\"';\n\t\t\tlet block = `clone_or_update \"${s.definition.id}\" \"${gs.repoUrl}\" ${branchArg}`;\n\t\t\tif (gs.postCloneCommands && gs.postCloneCommands.length > 0) {\n\t\t\t\tconst cmds = gs.postCloneCommands\n\t\t\t\t\t.map((cmd) => ` (cd \"$REPOS_DIR/${s.definition.id}${gs.subdirectory ? `/${gs.subdirectory}` : \"\"}\" && ${cmd})`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\tblock += `\\n${cmds}`;\n\t\t\t}\n\t\t\treturn block;\n\t\t})\n\t\t.join(\"\\n\\n\");\n\n\tfiles[\"scripts/clone-repos.sh\"] = `#!/usr/bin/env bash\nset -euo pipefail\n\n# ─── Clone/Update Git-Based Service Repositories ────────────────────────────\n# Idempotent: clones if missing, pulls if already present.\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nPROJECT_DIR=\"$(dirname \"$SCRIPT_DIR\")\"\nREPOS_DIR=\"$PROJECT_DIR/repos\"\n\n# ── Colour helpers ──────────────────────────────────────────────────────────\nif [ -t 1 ]; then\n GREEN='\\\\033[0;32m'; YELLOW='\\\\033[1;33m'; CYAN='\\\\033[0;36m'; RED='\\\\033[0;31m'; NC='\\\\033[0m'\nelse\n GREEN=''; YELLOW=''; CYAN=''; RED=''; NC=''\nfi\ninfo() { echo -e \"\\${CYAN}i $*\\${NC}\"; }\nok() { echo -e \"\\${GREEN}✓ $*\\${NC}\"; }\nwarn() { echo -e \"\\${YELLOW}⚠ $*\\${NC}\"; }\nerr() { echo -e \"\\${RED}✗ $*\\${NC}\" >&2; }\n\n# ── Check git ───────────────────────────────────────────────────────────────\nif ! command -v git &> /dev/null; then\n err \"git is not installed. Please install git first.\"\n exit 1\nfi\n\nmkdir -p \"$REPOS_DIR\"\n\nclone_or_update() {\n local name=\"$1\" url=\"$2\" branch=\"\\${3:-}\"\n local dir=\"$REPOS_DIR/$name\"\n\n if [ -d \"$dir/.git\" ]; then\n info \"Updating $name...\"\n git -C \"$dir\" pull --ff-only 2>/dev/null || warn \"Could not fast-forward $name (you may have local changes)\"\n else\n info \"Cloning $name...\"\n if [ -n \"$branch\" ]; then\n git clone --depth 1 --branch \"$branch\" \"$url\" \"$dir\"\n else\n git clone --depth 1 \"$url\" \"$dir\"\n fi\n fi\n}\n\necho \"\"\ninfo \"Cloning/updating SaaS boilerplate repositories...\"\necho \"\"\n\n${bashEntries}\n\necho \"\"\nok \"All repositories ready.\"\n`;\n\n\t// ── scripts/clone-repos.ps1 ────────────────────────────────────────────\n\n\tconst psEntries = gitServices\n\t\t.map((s) => {\n\t\t\tconst gs = s.definition.gitSource!;\n\t\t\tconst branchArg = gs.branch ? ` -Branch \"${gs.branch}\"` : \"\";\n\t\t\tlet block = `Clone-OrUpdate -Name \"${s.definition.id}\" -Url \"${gs.repoUrl}\"${branchArg}`;\n\t\t\tif (gs.postCloneCommands && gs.postCloneCommands.length > 0) {\n\t\t\t\tconst subdir = gs.subdirectory ? `/${gs.subdirectory}` : \"\";\n\t\t\t\tconst cmds = gs.postCloneCommands\n\t\t\t\t\t.map((cmd) => `Push-Location \"$ReposDir/${s.definition.id}${subdir}\"; ${cmd}; Pop-Location`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\tblock += `\\n${cmds}`;\n\t\t\t}\n\t\t\treturn block;\n\t\t})\n\t\t.join(\"\\n\\n\");\n\n\tfiles[\"scripts/clone-repos.ps1\"] = `#Requires -Version 5.1\n<#\n.SYNOPSIS\n Clone/update git-based service repositories.\n Idempotent: clones if missing, pulls if already present.\n#>\n$ErrorActionPreference = \"Stop\"\n\n$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path\n$ProjectDir = Split-Path -Parent $ScriptDir\n$ReposDir = Join-Path $ProjectDir \"repos\"\n\nif (-not (Get-Command git -ErrorAction SilentlyContinue)) {\n Write-Error \"git is not installed. Please install git first.\"\n exit 1\n}\n\nif (-not (Test-Path $ReposDir)) { New-Item -ItemType Directory -Path $ReposDir -Force | Out-Null }\n\nfunction Clone-OrUpdate {\n param(\n [string]$Name,\n [string]$Url,\n [string]$Branch = \"\"\n )\n $dir = Join-Path $ReposDir $Name\n\n if (Test-Path (Join-Path $dir \".git\")) {\n Write-Host \" Updating $Name...\" -ForegroundColor Cyan\n git -C $dir pull --ff-only 2>$null\n if ($LASTEXITCODE -ne 0) { Write-Warning \"Could not fast-forward $Name\" }\n } else {\n Write-Host \" Cloning $Name...\" -ForegroundColor Cyan\n if ($Branch) {\n git clone --depth 1 --branch $Branch $Url $dir\n } else {\n git clone --depth 1 $Url $dir\n }\n }\n}\n\nWrite-Host \"\"\nWrite-Host \"Cloning/updating SaaS boilerplate repositories...\" -ForegroundColor Cyan\nWrite-Host \"\"\n\n${psEntries}\n\nWrite-Host \"\"\nWrite-Host \"All repositories ready.\" -ForegroundColor Green\n`;\n\n\treturn files;\n}\n"],"mappings":";;;;;;AAMA,SAAgB,qBAAqB,UAAkD;CACtF,MAAM,cAAc,SAAS,SAAS,QACpC,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,aAC9C;AAED,KAAI,YAAY,WAAW,EAAG,QAAO,EAAE;CAEvC,MAAM,QAAgC,EAAE;AAmBxC,OAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAfd,YAClB,KAAK,MAAM;EACX,MAAM,KAAK,EAAE,WAAW;EACxB,MAAM,YAAY,GAAG,SAAS,IAAI,GAAG,OAAO,KAAK;EACjD,IAAI,QAAQ,oBAAoB,EAAE,WAAW,GAAG,KAAK,GAAG,QAAQ,IAAI;AACpE,MAAI,GAAG,qBAAqB,GAAG,kBAAkB,SAAS,GAAG;GAC5D,MAAM,OAAO,GAAG,kBACd,KAAK,QAAQ,qBAAqB,EAAE,WAAW,KAAK,GAAG,eAAe,IAAI,GAAG,iBAAiB,GAAG,OAAO,IAAI,GAAG,CAC/G,KAAK,KAAK;AACZ,YAAS,KAAK;;AAEf,SAAO;GACN,CACD,KAAK,OAAO,CAoDD;;;;;AAwBb,OAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAhBjB,YAChB,KAAK,MAAM;EACX,MAAM,KAAK,EAAE,WAAW;EACxB,MAAM,YAAY,GAAG,SAAS,aAAa,GAAG,OAAO,KAAK;EAC1D,IAAI,QAAQ,yBAAyB,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG;AAC7E,MAAI,GAAG,qBAAqB,GAAG,kBAAkB,SAAS,GAAG;GAC5D,MAAM,SAAS,GAAG,eAAe,IAAI,GAAG,iBAAiB;GACzD,MAAM,OAAO,GAAG,kBACd,KAAK,QAAQ,4BAA4B,EAAE,WAAW,KAAK,OAAO,KAAK,IAAI,gBAAgB,CAC3F,KAAK,KAAK;AACZ,YAAS,KAAK;;AAEf,SAAO;GACN,CACD,KAAK,OAAO,CA+CH;;;;;AAMX,QAAO"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ResolverOutput } from "../types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/generators/clone-repos.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Generates clone scripts for git-based services (SaaS boilerplates).
|
|
6
|
+
* Returns empty object if no git-based services exist in the resolved stack.
|
|
7
|
+
*/
|
|
8
|
+
declare function generateCloneScripts(resolved: ResolverOutput): Record<string, string>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { generateCloneScripts };
|
|
11
|
+
//# sourceMappingURL=clone-repos.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clone-repos.d.cts","names":[],"sources":["../../src/generators/clone-repos.ts"],"mappings":";;;;;AAMA;;iBAAgB,oBAAA,CAAqB,QAAA,EAAU,cAAA,GAAiB,MAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ResolverOutput } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/generators/clone-repos.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Generates clone scripts for git-based services (SaaS boilerplates).
|
|
6
|
+
* Returns empty object if no git-based services exist in the resolved stack.
|
|
7
|
+
*/
|
|
8
|
+
declare function generateCloneScripts(resolved: ResolverOutput): Record<string, string>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { generateCloneScripts };
|
|
11
|
+
//# sourceMappingURL=clone-repos.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clone-repos.d.mts","names":[],"sources":["../../src/generators/clone-repos.ts"],"mappings":";;;;;;AAMA;iBAAgB,oBAAA,CAAqB,QAAA,EAAU,cAAA,GAAiB,MAAA"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
//#region src/generators/clone-repos.ts
|
|
2
|
+
/**
|
|
3
|
+
* Generates clone scripts for git-based services (SaaS boilerplates).
|
|
4
|
+
* Returns empty object if no git-based services exist in the resolved stack.
|
|
5
|
+
*/
|
|
6
|
+
function generateCloneScripts(resolved) {
|
|
7
|
+
const gitServices = resolved.services.filter((s) => s.definition.gitSource && s.definition.buildContext);
|
|
8
|
+
if (gitServices.length === 0) return {};
|
|
9
|
+
const files = {};
|
|
10
|
+
files["scripts/clone-repos.sh"] = `#!/usr/bin/env bash
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# ─── Clone/Update Git-Based Service Repositories ────────────────────────────
|
|
14
|
+
# Idempotent: clones if missing, pulls if already present.
|
|
15
|
+
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
18
|
+
REPOS_DIR="$PROJECT_DIR/repos"
|
|
19
|
+
|
|
20
|
+
# ── Colour helpers ──────────────────────────────────────────────────────────
|
|
21
|
+
if [ -t 1 ]; then
|
|
22
|
+
GREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; CYAN='\\033[0;36m'; RED='\\033[0;31m'; NC='\\033[0m'
|
|
23
|
+
else
|
|
24
|
+
GREEN=''; YELLOW=''; CYAN=''; RED=''; NC=''
|
|
25
|
+
fi
|
|
26
|
+
info() { echo -e "\${CYAN}i $*\${NC}"; }
|
|
27
|
+
ok() { echo -e "\${GREEN}✓ $*\${NC}"; }
|
|
28
|
+
warn() { echo -e "\${YELLOW}⚠ $*\${NC}"; }
|
|
29
|
+
err() { echo -e "\${RED}✗ $*\${NC}" >&2; }
|
|
30
|
+
|
|
31
|
+
# ── Check git ───────────────────────────────────────────────────────────────
|
|
32
|
+
if ! command -v git &> /dev/null; then
|
|
33
|
+
err "git is not installed. Please install git first."
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
mkdir -p "$REPOS_DIR"
|
|
38
|
+
|
|
39
|
+
clone_or_update() {
|
|
40
|
+
local name="$1" url="$2" branch="\${3:-}"
|
|
41
|
+
local dir="$REPOS_DIR/$name"
|
|
42
|
+
|
|
43
|
+
if [ -d "$dir/.git" ]; then
|
|
44
|
+
info "Updating $name..."
|
|
45
|
+
git -C "$dir" pull --ff-only 2>/dev/null || warn "Could not fast-forward $name (you may have local changes)"
|
|
46
|
+
else
|
|
47
|
+
info "Cloning $name..."
|
|
48
|
+
if [ -n "$branch" ]; then
|
|
49
|
+
git clone --depth 1 --branch "$branch" "$url" "$dir"
|
|
50
|
+
else
|
|
51
|
+
git clone --depth 1 "$url" "$dir"
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
echo ""
|
|
57
|
+
info "Cloning/updating SaaS boilerplate repositories..."
|
|
58
|
+
echo ""
|
|
59
|
+
|
|
60
|
+
${gitServices.map((s) => {
|
|
61
|
+
const gs = s.definition.gitSource;
|
|
62
|
+
const branchArg = gs.branch ? `"${gs.branch}"` : "\"\"";
|
|
63
|
+
let block = `clone_or_update "${s.definition.id}" "${gs.repoUrl}" ${branchArg}`;
|
|
64
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
65
|
+
const cmds = gs.postCloneCommands.map((cmd) => ` (cd "$REPOS_DIR/${s.definition.id}${gs.subdirectory ? `/${gs.subdirectory}` : ""}" && ${cmd})`).join("\n");
|
|
66
|
+
block += `\n${cmds}`;
|
|
67
|
+
}
|
|
68
|
+
return block;
|
|
69
|
+
}).join("\n\n")}
|
|
70
|
+
|
|
71
|
+
echo ""
|
|
72
|
+
ok "All repositories ready."
|
|
73
|
+
`;
|
|
74
|
+
files["scripts/clone-repos.ps1"] = `#Requires -Version 5.1
|
|
75
|
+
<#
|
|
76
|
+
.SYNOPSIS
|
|
77
|
+
Clone/update git-based service repositories.
|
|
78
|
+
Idempotent: clones if missing, pulls if already present.
|
|
79
|
+
#>
|
|
80
|
+
$ErrorActionPreference = "Stop"
|
|
81
|
+
|
|
82
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
83
|
+
$ProjectDir = Split-Path -Parent $ScriptDir
|
|
84
|
+
$ReposDir = Join-Path $ProjectDir "repos"
|
|
85
|
+
|
|
86
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
87
|
+
Write-Error "git is not installed. Please install git first."
|
|
88
|
+
exit 1
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (-not (Test-Path $ReposDir)) { New-Item -ItemType Directory -Path $ReposDir -Force | Out-Null }
|
|
92
|
+
|
|
93
|
+
function Clone-OrUpdate {
|
|
94
|
+
param(
|
|
95
|
+
[string]$Name,
|
|
96
|
+
[string]$Url,
|
|
97
|
+
[string]$Branch = ""
|
|
98
|
+
)
|
|
99
|
+
$dir = Join-Path $ReposDir $Name
|
|
100
|
+
|
|
101
|
+
if (Test-Path (Join-Path $dir ".git")) {
|
|
102
|
+
Write-Host " Updating $Name..." -ForegroundColor Cyan
|
|
103
|
+
git -C $dir pull --ff-only 2>$null
|
|
104
|
+
if ($LASTEXITCODE -ne 0) { Write-Warning "Could not fast-forward $Name" }
|
|
105
|
+
} else {
|
|
106
|
+
Write-Host " Cloning $Name..." -ForegroundColor Cyan
|
|
107
|
+
if ($Branch) {
|
|
108
|
+
git clone --depth 1 --branch $Branch $Url $dir
|
|
109
|
+
} else {
|
|
110
|
+
git clone --depth 1 $Url $dir
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
Write-Host ""
|
|
116
|
+
Write-Host "Cloning/updating SaaS boilerplate repositories..." -ForegroundColor Cyan
|
|
117
|
+
Write-Host ""
|
|
118
|
+
|
|
119
|
+
${gitServices.map((s) => {
|
|
120
|
+
const gs = s.definition.gitSource;
|
|
121
|
+
const branchArg = gs.branch ? ` -Branch "${gs.branch}"` : "";
|
|
122
|
+
let block = `Clone-OrUpdate -Name "${s.definition.id}" -Url "${gs.repoUrl}"${branchArg}`;
|
|
123
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
124
|
+
const subdir = gs.subdirectory ? `/${gs.subdirectory}` : "";
|
|
125
|
+
const cmds = gs.postCloneCommands.map((cmd) => `Push-Location "$ReposDir/${s.definition.id}${subdir}"; ${cmd}; Pop-Location`).join("\n");
|
|
126
|
+
block += `\n${cmds}`;
|
|
127
|
+
}
|
|
128
|
+
return block;
|
|
129
|
+
}).join("\n\n")}
|
|
130
|
+
|
|
131
|
+
Write-Host ""
|
|
132
|
+
Write-Host "All repositories ready." -ForegroundColor Green
|
|
133
|
+
`;
|
|
134
|
+
return files;
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { generateCloneScripts };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=clone-repos.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clone-repos.mjs","names":[],"sources":["../../src/generators/clone-repos.ts"],"sourcesContent":["import type { ResolverOutput } from \"../types.js\";\n\n/**\n * Generates clone scripts for git-based services (SaaS boilerplates).\n * Returns empty object if no git-based services exist in the resolved stack.\n */\nexport function generateCloneScripts(resolved: ResolverOutput): Record<string, string> {\n\tconst gitServices = resolved.services.filter(\n\t\t(s) => s.definition.gitSource && s.definition.buildContext,\n\t);\n\n\tif (gitServices.length === 0) return {};\n\n\tconst files: Record<string, string> = {};\n\n\t// ── scripts/clone-repos.sh ─────────────────────────────────────────────\n\n\tconst bashEntries = gitServices\n\t\t.map((s) => {\n\t\t\tconst gs = s.definition.gitSource!;\n\t\t\tconst branchArg = gs.branch ? `\"${gs.branch}\"` : '\"\"';\n\t\t\tlet block = `clone_or_update \"${s.definition.id}\" \"${gs.repoUrl}\" ${branchArg}`;\n\t\t\tif (gs.postCloneCommands && gs.postCloneCommands.length > 0) {\n\t\t\t\tconst cmds = gs.postCloneCommands\n\t\t\t\t\t.map((cmd) => ` (cd \"$REPOS_DIR/${s.definition.id}${gs.subdirectory ? `/${gs.subdirectory}` : \"\"}\" && ${cmd})`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\tblock += `\\n${cmds}`;\n\t\t\t}\n\t\t\treturn block;\n\t\t})\n\t\t.join(\"\\n\\n\");\n\n\tfiles[\"scripts/clone-repos.sh\"] = `#!/usr/bin/env bash\nset -euo pipefail\n\n# ─── Clone/Update Git-Based Service Repositories ────────────────────────────\n# Idempotent: clones if missing, pulls if already present.\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nPROJECT_DIR=\"$(dirname \"$SCRIPT_DIR\")\"\nREPOS_DIR=\"$PROJECT_DIR/repos\"\n\n# ── Colour helpers ──────────────────────────────────────────────────────────\nif [ -t 1 ]; then\n GREEN='\\\\033[0;32m'; YELLOW='\\\\033[1;33m'; CYAN='\\\\033[0;36m'; RED='\\\\033[0;31m'; NC='\\\\033[0m'\nelse\n GREEN=''; YELLOW=''; CYAN=''; RED=''; NC=''\nfi\ninfo() { echo -e \"\\${CYAN}i $*\\${NC}\"; }\nok() { echo -e \"\\${GREEN}✓ $*\\${NC}\"; }\nwarn() { echo -e \"\\${YELLOW}⚠ $*\\${NC}\"; }\nerr() { echo -e \"\\${RED}✗ $*\\${NC}\" >&2; }\n\n# ── Check git ───────────────────────────────────────────────────────────────\nif ! command -v git &> /dev/null; then\n err \"git is not installed. Please install git first.\"\n exit 1\nfi\n\nmkdir -p \"$REPOS_DIR\"\n\nclone_or_update() {\n local name=\"$1\" url=\"$2\" branch=\"\\${3:-}\"\n local dir=\"$REPOS_DIR/$name\"\n\n if [ -d \"$dir/.git\" ]; then\n info \"Updating $name...\"\n git -C \"$dir\" pull --ff-only 2>/dev/null || warn \"Could not fast-forward $name (you may have local changes)\"\n else\n info \"Cloning $name...\"\n if [ -n \"$branch\" ]; then\n git clone --depth 1 --branch \"$branch\" \"$url\" \"$dir\"\n else\n git clone --depth 1 \"$url\" \"$dir\"\n fi\n fi\n}\n\necho \"\"\ninfo \"Cloning/updating SaaS boilerplate repositories...\"\necho \"\"\n\n${bashEntries}\n\necho \"\"\nok \"All repositories ready.\"\n`;\n\n\t// ── scripts/clone-repos.ps1 ────────────────────────────────────────────\n\n\tconst psEntries = gitServices\n\t\t.map((s) => {\n\t\t\tconst gs = s.definition.gitSource!;\n\t\t\tconst branchArg = gs.branch ? ` -Branch \"${gs.branch}\"` : \"\";\n\t\t\tlet block = `Clone-OrUpdate -Name \"${s.definition.id}\" -Url \"${gs.repoUrl}\"${branchArg}`;\n\t\t\tif (gs.postCloneCommands && gs.postCloneCommands.length > 0) {\n\t\t\t\tconst subdir = gs.subdirectory ? `/${gs.subdirectory}` : \"\";\n\t\t\t\tconst cmds = gs.postCloneCommands\n\t\t\t\t\t.map((cmd) => `Push-Location \"$ReposDir/${s.definition.id}${subdir}\"; ${cmd}; Pop-Location`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\tblock += `\\n${cmds}`;\n\t\t\t}\n\t\t\treturn block;\n\t\t})\n\t\t.join(\"\\n\\n\");\n\n\tfiles[\"scripts/clone-repos.ps1\"] = `#Requires -Version 5.1\n<#\n.SYNOPSIS\n Clone/update git-based service repositories.\n Idempotent: clones if missing, pulls if already present.\n#>\n$ErrorActionPreference = \"Stop\"\n\n$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path\n$ProjectDir = Split-Path -Parent $ScriptDir\n$ReposDir = Join-Path $ProjectDir \"repos\"\n\nif (-not (Get-Command git -ErrorAction SilentlyContinue)) {\n Write-Error \"git is not installed. Please install git first.\"\n exit 1\n}\n\nif (-not (Test-Path $ReposDir)) { New-Item -ItemType Directory -Path $ReposDir -Force | Out-Null }\n\nfunction Clone-OrUpdate {\n param(\n [string]$Name,\n [string]$Url,\n [string]$Branch = \"\"\n )\n $dir = Join-Path $ReposDir $Name\n\n if (Test-Path (Join-Path $dir \".git\")) {\n Write-Host \" Updating $Name...\" -ForegroundColor Cyan\n git -C $dir pull --ff-only 2>$null\n if ($LASTEXITCODE -ne 0) { Write-Warning \"Could not fast-forward $Name\" }\n } else {\n Write-Host \" Cloning $Name...\" -ForegroundColor Cyan\n if ($Branch) {\n git clone --depth 1 --branch $Branch $Url $dir\n } else {\n git clone --depth 1 $Url $dir\n }\n }\n}\n\nWrite-Host \"\"\nWrite-Host \"Cloning/updating SaaS boilerplate repositories...\" -ForegroundColor Cyan\nWrite-Host \"\"\n\n${psEntries}\n\nWrite-Host \"\"\nWrite-Host \"All repositories ready.\" -ForegroundColor Green\n`;\n\n\treturn files;\n}\n"],"mappings":";;;;;AAMA,SAAgB,qBAAqB,UAAkD;CACtF,MAAM,cAAc,SAAS,SAAS,QACpC,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,aAC9C;AAED,KAAI,YAAY,WAAW,EAAG,QAAO,EAAE;CAEvC,MAAM,QAAgC,EAAE;AAmBxC,OAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAfd,YAClB,KAAK,MAAM;EACX,MAAM,KAAK,EAAE,WAAW;EACxB,MAAM,YAAY,GAAG,SAAS,IAAI,GAAG,OAAO,KAAK;EACjD,IAAI,QAAQ,oBAAoB,EAAE,WAAW,GAAG,KAAK,GAAG,QAAQ,IAAI;AACpE,MAAI,GAAG,qBAAqB,GAAG,kBAAkB,SAAS,GAAG;GAC5D,MAAM,OAAO,GAAG,kBACd,KAAK,QAAQ,qBAAqB,EAAE,WAAW,KAAK,GAAG,eAAe,IAAI,GAAG,iBAAiB,GAAG,OAAO,IAAI,GAAG,CAC/G,KAAK,KAAK;AACZ,YAAS,KAAK;;AAEf,SAAO;GACN,CACD,KAAK,OAAO,CAoDD;;;;;AAwBb,OAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAhBjB,YAChB,KAAK,MAAM;EACX,MAAM,KAAK,EAAE,WAAW;EACxB,MAAM,YAAY,GAAG,SAAS,aAAa,GAAG,OAAO,KAAK;EAC1D,IAAI,QAAQ,yBAAyB,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG;AAC7E,MAAI,GAAG,qBAAqB,GAAG,kBAAkB,SAAS,GAAG;GAC5D,MAAM,SAAS,GAAG,eAAe,IAAI,GAAG,iBAAiB;GACzD,MAAM,OAAO,GAAG,kBACd,KAAK,QAAQ,4BAA4B,EAAE,WAAW,KAAK,OAAO,KAAK,IAAI,gBAAgB,CAC3F,KAAK,KAAK;AACZ,YAAS,KAAK;;AAEf,SAAO;GACN,CACD,KAAK,OAAO,CA+CH;;;;;AAMX,QAAO"}
|