@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
|
@@ -25,9 +25,10 @@ describe("bare-metal partition", () => {
|
|
|
25
25
|
});
|
|
26
26
|
const result = partitionBareMetal(resolved, "linux/amd64");
|
|
27
27
|
expect(result.nativeIds.has("redis")).toBe(true);
|
|
28
|
-
|
|
29
|
-
expect(
|
|
30
|
-
|
|
28
|
+
const redisNative = result.nativeServices.find((s) => s.definition.id === "redis");
|
|
29
|
+
expect(redisNative).toBeDefined();
|
|
30
|
+
// Mandatory services (convex, mission-control, tailscale) are Docker-only
|
|
31
|
+
expect(result.dockerOnlyServices.length).toBeGreaterThanOrEqual(0);
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
it("partitions n8n as Docker-only (no native recipe)", () => {
|
package/src/composer.test.ts
CHANGED
|
@@ -38,8 +38,10 @@ describe("compose", () => {
|
|
|
38
38
|
// Should have CLI companion service
|
|
39
39
|
expect(parsed.services).toHaveProperty("openclaw-cli");
|
|
40
40
|
|
|
41
|
-
// Gateway should have
|
|
42
|
-
|
|
41
|
+
// Gateway should have depends_on for mandatory services (convex, mission-control, tailscale)
|
|
42
|
+
// Even with no user-selected services, mandatory services are always present
|
|
43
|
+
expect(parsed.services).toHaveProperty("convex");
|
|
44
|
+
expect(parsed.services).toHaveProperty("mission-control");
|
|
43
45
|
|
|
44
46
|
// Gateway should have restart policy
|
|
45
47
|
expect(parsed.services["openclaw-gateway"].restart).toBe("unless-stopped");
|
package/src/composer.ts
CHANGED
|
@@ -23,7 +23,7 @@ function getOpenclawImage(variant: OpenclawImageVariant, version: string): strin
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** Creates a YAML scalar that is always quoted — avoids YAML 1.1 bare `no` → false. */
|
|
26
|
-
function quotedStr(value: string): Scalar {
|
|
26
|
+
export function quotedStr(value: string): Scalar {
|
|
27
27
|
const s = new Scalar(value);
|
|
28
28
|
s.type = Scalar.QUOTE_DOUBLE;
|
|
29
29
|
return s;
|
|
@@ -50,9 +50,10 @@ const CATEGORY_PROFILE_MAP: Partial<Record<ServiceCategory, { file: string; prof
|
|
|
50
50
|
"social-media": { file: "docker-compose.social.yml", profile: "social" },
|
|
51
51
|
knowledge: { file: "docker-compose.knowledge.yml", profile: "knowledge" },
|
|
52
52
|
communication: { file: "docker-compose.communication.yml", profile: "communication" },
|
|
53
|
+
"saas-boilerplate": { file: "docker-compose.saas.yml", profile: "saas" },
|
|
53
54
|
};
|
|
54
55
|
|
|
55
|
-
const YAML_OPTIONS = { lineWidth: 120, nullStr: "" };
|
|
56
|
+
export const YAML_OPTIONS = { lineWidth: 120, nullStr: "" };
|
|
56
57
|
|
|
57
58
|
// ── Shared Gateway Builder ──────────────────────────────────────────────────
|
|
58
59
|
|
|
@@ -221,7 +222,7 @@ function buildGatewayServices(
|
|
|
221
222
|
|
|
222
223
|
// ── Shared Companion Service Builder ────────────────────────────────────────
|
|
223
224
|
|
|
224
|
-
function buildCompanionService(
|
|
225
|
+
export function buildCompanionService(
|
|
225
226
|
def: ResolverOutput["services"][number]["definition"],
|
|
226
227
|
resolved: ResolverOutput,
|
|
227
228
|
options: ComposeOptions,
|
|
@@ -230,7 +231,25 @@ function buildCompanionService(
|
|
|
230
231
|
const svc: Record<string, unknown> = {};
|
|
231
232
|
const volumeNames: string[] = [];
|
|
232
233
|
|
|
233
|
-
|
|
234
|
+
// Git-based services use build: context; image-based services use image:
|
|
235
|
+
if (def.gitSource && def.buildContext) {
|
|
236
|
+
const subdir = def.gitSource.subdirectory || ".";
|
|
237
|
+
const ctxPath = def.buildContext.context || ".";
|
|
238
|
+
const contextFull = subdir === "." ? `./repos/${def.id}/${ctxPath}` : `./repos/${def.id}/${subdir}/${ctxPath}`;
|
|
239
|
+
const buildBlock: Record<string, unknown> = { context: contextFull };
|
|
240
|
+
if (def.buildContext.dockerfile) {
|
|
241
|
+
buildBlock.dockerfile = def.buildContext.dockerfile;
|
|
242
|
+
}
|
|
243
|
+
if (def.buildContext.args && Object.keys(def.buildContext.args).length > 0) {
|
|
244
|
+
buildBlock.args = def.buildContext.args;
|
|
245
|
+
}
|
|
246
|
+
if (def.buildContext.target) {
|
|
247
|
+
buildBlock.target = def.buildContext.target;
|
|
248
|
+
}
|
|
249
|
+
svc.build = buildBlock;
|
|
250
|
+
} else {
|
|
251
|
+
svc.image = `${def.image}:${def.imageTag}`;
|
|
252
|
+
}
|
|
234
253
|
|
|
235
254
|
if (def.environment.length > 0) {
|
|
236
255
|
const env: Record<string, string> = {};
|
|
@@ -426,7 +445,7 @@ function buildCompanionService(
|
|
|
426
445
|
*
|
|
427
446
|
* Returns null when no setup is needed (no PostgreSQL or no DB requirements).
|
|
428
447
|
*/
|
|
429
|
-
function buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null {
|
|
448
|
+
export function buildPostgresSetup(resolved: ResolverOutput): Record<string, unknown> | null {
|
|
430
449
|
const hasPostgres = resolved.services.some((s) => s.definition.id === "postgresql");
|
|
431
450
|
if (!hasPostgres) return null;
|
|
432
451
|
|
package/src/generate.test.ts
CHANGED
|
@@ -187,7 +187,8 @@ describe("generate (end-to-end)", () => {
|
|
|
187
187
|
for (const id of lasuiteMeetServices) {
|
|
188
188
|
expect(allServiceIds.has(id), `missing service ${id}`).toBe(true);
|
|
189
189
|
}
|
|
190
|
-
|
|
190
|
+
// Service count includes user services + mandatory platform services (convex, mission-control, tailscale)
|
|
191
|
+
expect(result.metadata.serviceCount).toBeGreaterThanOrEqual(lasuiteMeetServices.length);
|
|
191
192
|
});
|
|
192
193
|
|
|
193
194
|
it("generates bare-metal installer for Windows (install.ps1)", () => {
|
package/src/generate.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { composeMultiFile } from "./composer.js";
|
|
|
7
7
|
import { StackConfigError, ValidationError } from "./errors.js";
|
|
8
8
|
import { generateBareMetalInstall } from "./generators/bare-metal-install.js";
|
|
9
9
|
import { generateCaddyfile } from "./generators/caddy.js";
|
|
10
|
+
import { generateCloneScripts } from "./generators/clone-repos.js";
|
|
10
11
|
import { generateCloudInit } from "./generators/cloud-init.js";
|
|
11
12
|
import { generateEnvFiles } from "./generators/env.js";
|
|
12
13
|
import { generateGsdScripts } from "./generators/get-shit-done.js";
|
|
@@ -149,6 +150,7 @@ export function generate(rawInput: GenerationInput): GenerationResult {
|
|
|
149
150
|
".env.*.local",
|
|
150
151
|
"*.log",
|
|
151
152
|
"docker-compose.override.yml",
|
|
153
|
+
"repos/",
|
|
152
154
|
].join("\n");
|
|
153
155
|
|
|
154
156
|
// Stack manifest (consumed by Mission Control)
|
|
@@ -181,11 +183,18 @@ export function generate(rawInput: GenerationInput): GenerationResult {
|
|
|
181
183
|
});
|
|
182
184
|
|
|
183
185
|
// Scripts
|
|
184
|
-
const
|
|
186
|
+
const hasGitServices = resolvedForCompose.services.some((s) => s.definition.gitSource);
|
|
187
|
+
const scripts = generateScripts({ hasGitServices });
|
|
185
188
|
for (const [path, content] of Object.entries(scripts)) {
|
|
186
189
|
files[path] = content;
|
|
187
190
|
}
|
|
188
191
|
|
|
192
|
+
// Clone scripts for git-based services (SaaS boilerplates)
|
|
193
|
+
const cloneScripts = generateCloneScripts(resolvedForCompose);
|
|
194
|
+
for (const [path, content] of Object.entries(cloneScripts)) {
|
|
195
|
+
files[path] = content;
|
|
196
|
+
}
|
|
197
|
+
|
|
189
198
|
// Health check scripts (dynamic, stack-specific)
|
|
190
199
|
const healthCheckFiles = generateHealthCheck(resolved, {
|
|
191
200
|
projectName: input.projectName,
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { generateCloneScripts } from "./clone-repos.js";
|
|
3
|
+
import type { ResolverOutput } from "../types.js";
|
|
4
|
+
|
|
5
|
+
/** Minimal resolved output with no services */
|
|
6
|
+
function emptyResolved(): ResolverOutput {
|
|
7
|
+
return {
|
|
8
|
+
services: [],
|
|
9
|
+
addedDependencies: [],
|
|
10
|
+
removedConflicts: [],
|
|
11
|
+
warnings: [],
|
|
12
|
+
errors: [],
|
|
13
|
+
isValid: true,
|
|
14
|
+
estimatedMemoryMB: 0,
|
|
15
|
+
aiProviders: [],
|
|
16
|
+
gsdRuntimes: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Create a resolved output with image-based services only */
|
|
21
|
+
function imageOnlyResolved(): ResolverOutput {
|
|
22
|
+
return {
|
|
23
|
+
...emptyResolved(),
|
|
24
|
+
services: [
|
|
25
|
+
{
|
|
26
|
+
definition: {
|
|
27
|
+
id: "redis",
|
|
28
|
+
name: "Redis",
|
|
29
|
+
description: "In-memory cache",
|
|
30
|
+
category: "database",
|
|
31
|
+
icon: "🔴",
|
|
32
|
+
image: "redis",
|
|
33
|
+
imageTag: "7-alpine",
|
|
34
|
+
ports: [],
|
|
35
|
+
volumes: [],
|
|
36
|
+
environment: [],
|
|
37
|
+
dependsOn: [],
|
|
38
|
+
restartPolicy: "unless-stopped",
|
|
39
|
+
networks: ["openclaw-network"],
|
|
40
|
+
skills: [],
|
|
41
|
+
openclawEnvVars: [],
|
|
42
|
+
docsUrl: "https://redis.io",
|
|
43
|
+
tags: [],
|
|
44
|
+
maturity: "stable",
|
|
45
|
+
requires: [],
|
|
46
|
+
recommends: [],
|
|
47
|
+
conflictsWith: [],
|
|
48
|
+
gpuRequired: false,
|
|
49
|
+
},
|
|
50
|
+
addedBy: "user",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Create a resolved output with a git-based service */
|
|
57
|
+
function gitServiceResolved(): ResolverOutput {
|
|
58
|
+
return {
|
|
59
|
+
...emptyResolved(),
|
|
60
|
+
services: [
|
|
61
|
+
{
|
|
62
|
+
definition: {
|
|
63
|
+
id: "open-saas",
|
|
64
|
+
name: "Open SaaS",
|
|
65
|
+
description: "SaaS boilerplate",
|
|
66
|
+
category: "saas-boilerplate",
|
|
67
|
+
icon: "🚀",
|
|
68
|
+
gitSource: {
|
|
69
|
+
repoUrl: "https://github.com/wasp-lang/open-saas.git",
|
|
70
|
+
branch: "main",
|
|
71
|
+
subdirectory: "template",
|
|
72
|
+
postCloneCommands: ["cp .env.example .env"],
|
|
73
|
+
},
|
|
74
|
+
buildContext: {
|
|
75
|
+
dockerfile: "Dockerfile",
|
|
76
|
+
context: ".",
|
|
77
|
+
},
|
|
78
|
+
ports: [{ host: 3100, container: 3000, description: "Web app", exposed: true }],
|
|
79
|
+
volumes: [],
|
|
80
|
+
environment: [],
|
|
81
|
+
dependsOn: [],
|
|
82
|
+
restartPolicy: "unless-stopped",
|
|
83
|
+
networks: ["openclaw-network"],
|
|
84
|
+
skills: [],
|
|
85
|
+
openclawEnvVars: [],
|
|
86
|
+
docsUrl: "https://opensaas.sh/docs",
|
|
87
|
+
tags: [],
|
|
88
|
+
maturity: "beta",
|
|
89
|
+
requires: [],
|
|
90
|
+
recommends: [],
|
|
91
|
+
conflictsWith: [],
|
|
92
|
+
gpuRequired: false,
|
|
93
|
+
},
|
|
94
|
+
addedBy: "user",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
describe("generateCloneScripts", () => {
|
|
101
|
+
it("returns empty object when no git-based services exist", () => {
|
|
102
|
+
const result = generateCloneScripts(emptyResolved());
|
|
103
|
+
expect(result).toEqual({});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("returns empty object when only image-based services exist", () => {
|
|
107
|
+
const result = generateCloneScripts(imageOnlyResolved());
|
|
108
|
+
expect(result).toEqual({});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("generates bash and PowerShell scripts for git-based services", () => {
|
|
112
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
113
|
+
expect(Object.keys(result)).toHaveLength(2);
|
|
114
|
+
expect(result).toHaveProperty("scripts/clone-repos.sh");
|
|
115
|
+
expect(result).toHaveProperty("scripts/clone-repos.ps1");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("bash script contains clone command with repo URL", () => {
|
|
119
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
120
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
121
|
+
expect(bash).toContain("https://github.com/wasp-lang/open-saas.git");
|
|
122
|
+
expect(bash).toContain('"open-saas"');
|
|
123
|
+
expect(bash).toContain('"main"');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("bash script includes postCloneCommands", () => {
|
|
127
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
128
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
129
|
+
expect(bash).toContain("cp .env.example .env");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("bash script includes git check and idempotency logic", () => {
|
|
133
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
134
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
135
|
+
expect(bash).toContain("command -v git");
|
|
136
|
+
expect(bash).toContain("clone_or_update");
|
|
137
|
+
expect(bash).toContain("pull --ff-only");
|
|
138
|
+
expect(bash).toContain("git clone --depth 1");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("PowerShell script contains clone command with repo URL", () => {
|
|
142
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
143
|
+
const ps = result["scripts/clone-repos.ps1"];
|
|
144
|
+
expect(ps).toContain("https://github.com/wasp-lang/open-saas.git");
|
|
145
|
+
expect(ps).toContain('"open-saas"');
|
|
146
|
+
expect(ps).toContain("Clone-OrUpdate");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("bash script is executable (starts with shebang)", () => {
|
|
150
|
+
const result = generateCloneScripts(gitServiceResolved());
|
|
151
|
+
const bash = result["scripts/clone-repos.sh"];
|
|
152
|
+
expect(bash).toMatch(/^#!/);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { ResolverOutput } from "../types.js";
|
|
2
|
+
|
|
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
|
+
export function generateCloneScripts(resolved: ResolverOutput): Record<string, string> {
|
|
8
|
+
const gitServices = resolved.services.filter(
|
|
9
|
+
(s) => s.definition.gitSource && s.definition.buildContext,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
if (gitServices.length === 0) return {};
|
|
13
|
+
|
|
14
|
+
const files: Record<string, string> = {};
|
|
15
|
+
|
|
16
|
+
// ── scripts/clone-repos.sh ─────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const bashEntries = gitServices
|
|
19
|
+
.map((s) => {
|
|
20
|
+
const gs = s.definition.gitSource!;
|
|
21
|
+
const branchArg = gs.branch ? `"${gs.branch}"` : '""';
|
|
22
|
+
let block = `clone_or_update "${s.definition.id}" "${gs.repoUrl}" ${branchArg}`;
|
|
23
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
24
|
+
const cmds = gs.postCloneCommands
|
|
25
|
+
.map((cmd) => ` (cd "$REPOS_DIR/${s.definition.id}${gs.subdirectory ? `/${gs.subdirectory}` : ""}" && ${cmd})`)
|
|
26
|
+
.join("\n");
|
|
27
|
+
block += `\n${cmds}`;
|
|
28
|
+
}
|
|
29
|
+
return block;
|
|
30
|
+
})
|
|
31
|
+
.join("\n\n");
|
|
32
|
+
|
|
33
|
+
files["scripts/clone-repos.sh"] = `#!/usr/bin/env bash
|
|
34
|
+
set -euo pipefail
|
|
35
|
+
|
|
36
|
+
# ─── Clone/Update Git-Based Service Repositories ────────────────────────────
|
|
37
|
+
# Idempotent: clones if missing, pulls if already present.
|
|
38
|
+
|
|
39
|
+
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
40
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
41
|
+
REPOS_DIR="$PROJECT_DIR/repos"
|
|
42
|
+
|
|
43
|
+
# ── Colour helpers ──────────────────────────────────────────────────────────
|
|
44
|
+
if [ -t 1 ]; then
|
|
45
|
+
GREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; CYAN='\\033[0;36m'; RED='\\033[0;31m'; NC='\\033[0m'
|
|
46
|
+
else
|
|
47
|
+
GREEN=''; YELLOW=''; CYAN=''; RED=''; NC=''
|
|
48
|
+
fi
|
|
49
|
+
info() { echo -e "\${CYAN}i $*\${NC}"; }
|
|
50
|
+
ok() { echo -e "\${GREEN}✓ $*\${NC}"; }
|
|
51
|
+
warn() { echo -e "\${YELLOW}⚠ $*\${NC}"; }
|
|
52
|
+
err() { echo -e "\${RED}✗ $*\${NC}" >&2; }
|
|
53
|
+
|
|
54
|
+
# ── Check git ───────────────────────────────────────────────────────────────
|
|
55
|
+
if ! command -v git &> /dev/null; then
|
|
56
|
+
err "git is not installed. Please install git first."
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
mkdir -p "$REPOS_DIR"
|
|
61
|
+
|
|
62
|
+
clone_or_update() {
|
|
63
|
+
local name="$1" url="$2" branch="\${3:-}"
|
|
64
|
+
local dir="$REPOS_DIR/$name"
|
|
65
|
+
|
|
66
|
+
if [ -d "$dir/.git" ]; then
|
|
67
|
+
info "Updating $name..."
|
|
68
|
+
git -C "$dir" pull --ff-only 2>/dev/null || warn "Could not fast-forward $name (you may have local changes)"
|
|
69
|
+
else
|
|
70
|
+
info "Cloning $name..."
|
|
71
|
+
if [ -n "$branch" ]; then
|
|
72
|
+
git clone --depth 1 --branch "$branch" "$url" "$dir"
|
|
73
|
+
else
|
|
74
|
+
git clone --depth 1 "$url" "$dir"
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
echo ""
|
|
80
|
+
info "Cloning/updating SaaS boilerplate repositories..."
|
|
81
|
+
echo ""
|
|
82
|
+
|
|
83
|
+
${bashEntries}
|
|
84
|
+
|
|
85
|
+
echo ""
|
|
86
|
+
ok "All repositories ready."
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
// ── scripts/clone-repos.ps1 ────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
const psEntries = gitServices
|
|
92
|
+
.map((s) => {
|
|
93
|
+
const gs = s.definition.gitSource!;
|
|
94
|
+
const branchArg = gs.branch ? ` -Branch "${gs.branch}"` : "";
|
|
95
|
+
let block = `Clone-OrUpdate -Name "${s.definition.id}" -Url "${gs.repoUrl}"${branchArg}`;
|
|
96
|
+
if (gs.postCloneCommands && gs.postCloneCommands.length > 0) {
|
|
97
|
+
const subdir = gs.subdirectory ? `/${gs.subdirectory}` : "";
|
|
98
|
+
const cmds = gs.postCloneCommands
|
|
99
|
+
.map((cmd) => `Push-Location "$ReposDir/${s.definition.id}${subdir}"; ${cmd}; Pop-Location`)
|
|
100
|
+
.join("\n");
|
|
101
|
+
block += `\n${cmds}`;
|
|
102
|
+
}
|
|
103
|
+
return block;
|
|
104
|
+
})
|
|
105
|
+
.join("\n\n");
|
|
106
|
+
|
|
107
|
+
files["scripts/clone-repos.ps1"] = `#Requires -Version 5.1
|
|
108
|
+
<#
|
|
109
|
+
.SYNOPSIS
|
|
110
|
+
Clone/update git-based service repositories.
|
|
111
|
+
Idempotent: clones if missing, pulls if already present.
|
|
112
|
+
#>
|
|
113
|
+
$ErrorActionPreference = "Stop"
|
|
114
|
+
|
|
115
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
116
|
+
$ProjectDir = Split-Path -Parent $ScriptDir
|
|
117
|
+
$ReposDir = Join-Path $ProjectDir "repos"
|
|
118
|
+
|
|
119
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
120
|
+
Write-Error "git is not installed. Please install git first."
|
|
121
|
+
exit 1
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (-not (Test-Path $ReposDir)) { New-Item -ItemType Directory -Path $ReposDir -Force | Out-Null }
|
|
125
|
+
|
|
126
|
+
function Clone-OrUpdate {
|
|
127
|
+
param(
|
|
128
|
+
[string]$Name,
|
|
129
|
+
[string]$Url,
|
|
130
|
+
[string]$Branch = ""
|
|
131
|
+
)
|
|
132
|
+
$dir = Join-Path $ReposDir $Name
|
|
133
|
+
|
|
134
|
+
if (Test-Path (Join-Path $dir ".git")) {
|
|
135
|
+
Write-Host " Updating $Name..." -ForegroundColor Cyan
|
|
136
|
+
git -C $dir pull --ff-only 2>$null
|
|
137
|
+
if ($LASTEXITCODE -ne 0) { Write-Warning "Could not fast-forward $Name" }
|
|
138
|
+
} else {
|
|
139
|
+
Write-Host " Cloning $Name..." -ForegroundColor Cyan
|
|
140
|
+
if ($Branch) {
|
|
141
|
+
git clone --depth 1 --branch $Branch $Url $dir
|
|
142
|
+
} else {
|
|
143
|
+
git clone --depth 1 $Url $dir
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
Write-Host ""
|
|
149
|
+
Write-Host "Cloning/updating SaaS boilerplate repositories..." -ForegroundColor Cyan
|
|
150
|
+
Write-Host ""
|
|
151
|
+
|
|
152
|
+
${psEntries}
|
|
153
|
+
|
|
154
|
+
Write-Host ""
|
|
155
|
+
Write-Host "All repositories ready." -ForegroundColor Green
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
return files;
|
|
159
|
+
}
|
|
@@ -52,6 +52,23 @@ const DB_REQUIREMENTS: Record<string, Omit<DbRequirement, "serviceId" | "service
|
|
|
52
52
|
openpanel: { dbName: "openpanel", dbUser: "openpanel", passwordEnvVar: "OPENPANEL_DB_PASSWORD" },
|
|
53
53
|
usesend: { dbName: "usesend", dbUser: "usesend", passwordEnvVar: "USESEND_DB_PASSWORD" },
|
|
54
54
|
nextcloud: { dbName: "nextcloud", dbUser: "nextcloud", passwordEnvVar: "NEXTCLOUD_DB_PASSWORD" },
|
|
55
|
+
// ── SaaS Boilerplates ────────────────────────────────────────────────────
|
|
56
|
+
"open-saas": { dbName: "opensaas", dbUser: "opensaas", passwordEnvVar: "OPENSAAS_DB_PASSWORD" },
|
|
57
|
+
"apptension-saas": {
|
|
58
|
+
dbName: "apptensionsaas",
|
|
59
|
+
dbUser: "apptensionsaas",
|
|
60
|
+
passwordEnvVar: "APPTENSION_SAAS_DB_PASSWORD",
|
|
61
|
+
},
|
|
62
|
+
"boxyhq-saas": {
|
|
63
|
+
dbName: "boxyhqsaas",
|
|
64
|
+
dbUser: "boxyhqsaas",
|
|
65
|
+
passwordEnvVar: "BOXYHQ_SAAS_DB_PASSWORD",
|
|
66
|
+
},
|
|
67
|
+
"ixartz-saas": {
|
|
68
|
+
dbName: "ixartzsaas",
|
|
69
|
+
dbUser: "ixartzsaas",
|
|
70
|
+
passwordEnvVar: "IXARTZ_SAAS_DB_PASSWORD",
|
|
71
|
+
},
|
|
55
72
|
};
|
|
56
73
|
|
|
57
74
|
/**
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { generateScripts } from "./scripts.js";
|
|
3
3
|
|
|
4
4
|
describe("generateScripts", () => {
|
|
5
|
-
it("generates all
|
|
5
|
+
it("generates all 10 expected scripts (5 bash + 5 PowerShell)", () => {
|
|
6
6
|
const result = generateScripts();
|
|
7
7
|
|
|
8
8
|
const expectedScripts = [
|
|
@@ -11,6 +11,11 @@ describe("generateScripts", () => {
|
|
|
11
11
|
"scripts/update.sh",
|
|
12
12
|
"scripts/backup.sh",
|
|
13
13
|
"scripts/status.sh",
|
|
14
|
+
"scripts/start.ps1",
|
|
15
|
+
"scripts/stop.ps1",
|
|
16
|
+
"scripts/update.ps1",
|
|
17
|
+
"scripts/backup.ps1",
|
|
18
|
+
"scripts/status.ps1",
|
|
14
19
|
];
|
|
15
20
|
|
|
16
21
|
for (const script of expectedScripts) {
|
|
@@ -50,11 +55,54 @@ describe("generateScripts", () => {
|
|
|
50
55
|
expect(result["scripts/status.sh"]).toContain("ps");
|
|
51
56
|
});
|
|
52
57
|
|
|
53
|
-
it("all scripts start with
|
|
58
|
+
it("all bash scripts start with shebang", () => {
|
|
54
59
|
const result = generateScripts();
|
|
55
60
|
|
|
56
|
-
for (const [, content] of Object.entries(result)) {
|
|
57
|
-
|
|
61
|
+
for (const [path, content] of Object.entries(result)) {
|
|
62
|
+
if (path.endsWith(".sh")) {
|
|
63
|
+
expect(content.startsWith("#!/")).toBe(true);
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
66
|
});
|
|
67
|
+
|
|
68
|
+
it("all PowerShell scripts start with #Requires", () => {
|
|
69
|
+
const result = generateScripts();
|
|
70
|
+
|
|
71
|
+
for (const [path, content] of Object.entries(result)) {
|
|
72
|
+
if (path.endsWith(".ps1")) {
|
|
73
|
+
expect(content.startsWith("#Requires")).toBe(true);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("start.ps1 calls docker compose up", () => {
|
|
79
|
+
const result = generateScripts();
|
|
80
|
+
expect(result["scripts/start.ps1"]).toContain("docker compose");
|
|
81
|
+
expect(result["scripts/start.ps1"]).toContain("up");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("stop.ps1 calls docker compose down", () => {
|
|
85
|
+
const result = generateScripts();
|
|
86
|
+
expect(result["scripts/stop.ps1"]).toContain("docker compose");
|
|
87
|
+
expect(result["scripts/stop.ps1"]).toContain("down");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("update.ps1 calls docker compose pull", () => {
|
|
91
|
+
const result = generateScripts();
|
|
92
|
+
expect(result["scripts/update.ps1"]).toContain("docker compose");
|
|
93
|
+
expect(result["scripts/update.ps1"]).toContain("pull");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("backup.ps1 references volumes or backup", () => {
|
|
97
|
+
const result = generateScripts();
|
|
98
|
+
const backup = result["scripts/backup.ps1"]!;
|
|
99
|
+
expect(backup).toBeDefined();
|
|
100
|
+
expect(backup.length).toBeGreaterThan(50);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("status.ps1 calls docker compose ps", () => {
|
|
104
|
+
const result = generateScripts();
|
|
105
|
+
expect(result["scripts/status.ps1"]).toContain("docker compose");
|
|
106
|
+
expect(result["scripts/status.ps1"]).toContain("ps");
|
|
107
|
+
});
|
|
60
108
|
});
|