@arcote.tech/arc-cli 0.6.1 → 0.7.0
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/index.js +1214 -1217
- package/package.json +7 -7
- package/src/builder/access-extractor.ts +79 -47
- package/src/builder/build-cache.ts +3 -1
- package/src/builder/chunk-planner.ts +107 -0
- package/src/builder/dependency-collector.ts +86 -32
- package/src/builder/framework-peers.ts +81 -0
- package/src/builder/module-builder.ts +186 -110
- package/src/commands/platform-deploy.ts +103 -55
- package/src/commands/platform-dev.ts +11 -100
- package/src/commands/platform-start.ts +4 -90
- package/src/deploy/bootstrap.ts +157 -6
- package/src/deploy/caddyfile.ts +19 -23
- package/src/deploy/compose.ts +43 -27
- package/src/deploy/config.ts +29 -0
- package/src/deploy/deploy-env.ts +129 -0
- package/src/deploy/htpasswd.ts +28 -0
- package/src/deploy/image-template.ts +74 -0
- package/src/deploy/image.ts +237 -0
- package/src/deploy/registry.ts +79 -0
- package/src/deploy/ssh.ts +5 -124
- package/src/deploy/survey.ts +64 -0
- package/src/index.ts +15 -13
- package/src/platform/server.ts +69 -44
- package/src/platform/shared.ts +124 -65
- package/src/platform/startup.ts +160 -0
- package/runtime/Dockerfile +0 -29
- package/runtime/build-and-push.sh +0 -23
- package/runtime/entrypoint.sh +0 -58
- package/src/commands/build-shell.ts +0 -152
- package/src/deploy/remote-sync.ts +0 -323
- package/src/platform/deploy-api.ts +0 -396
package/src/platform/shared.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync,
|
|
|
3
3
|
import { dirname, join } from "path";
|
|
4
4
|
import {
|
|
5
5
|
buildContextPackages,
|
|
6
|
-
|
|
6
|
+
buildModulesByChunks,
|
|
7
7
|
buildStyles,
|
|
8
8
|
buildTranslations,
|
|
9
9
|
discoverPackages,
|
|
@@ -19,6 +19,9 @@ import {
|
|
|
19
19
|
updateCache,
|
|
20
20
|
type BuildCache,
|
|
21
21
|
} from "../builder/build-cache";
|
|
22
|
+
import { extractAccessMap } from "../builder/access-extractor";
|
|
23
|
+
import { planChunks } from "../builder/chunk-planner";
|
|
24
|
+
import { collectFrameworkDeps } from "../builder/dependency-collector";
|
|
22
25
|
import {
|
|
23
26
|
mtimeOf,
|
|
24
27
|
readInstalledVersion,
|
|
@@ -28,14 +31,9 @@ import {
|
|
|
28
31
|
sha256OfJson,
|
|
29
32
|
} from "../builder/hash";
|
|
30
33
|
import { pAll } from "../builder/parallel";
|
|
31
|
-
import {
|
|
32
|
-
collectFrameworkDeps,
|
|
33
|
-
collectModuleDeps,
|
|
34
|
-
} from "../builder/dependency-collector";
|
|
35
|
-
import { extractAccessMap } from "../builder/access-extractor";
|
|
36
34
|
|
|
37
35
|
// Re-export for convenience
|
|
38
|
-
export { buildContextPackages,
|
|
36
|
+
export { buildContextPackages, buildModulesByChunks, buildStyles, isContextPackage };
|
|
39
37
|
export type { BuildManifest, ModuleDescriptor, WorkspacePackage };
|
|
40
38
|
|
|
41
39
|
// ---------------------------------------------------------------------------
|
|
@@ -93,16 +91,15 @@ export function resolveWorkspace(): WorkspaceInfo {
|
|
|
93
91
|
|
|
94
92
|
log("Scanning workspaces...");
|
|
95
93
|
const packages = discoverPackages(rootDir);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
process.exit(1);
|
|
94
|
+
if (packages.length > 0) {
|
|
95
|
+
ok(
|
|
96
|
+
`Found ${packages.length} package(s): ${packages.map((p) => p.name).join(", ")}`,
|
|
97
|
+
);
|
|
98
|
+
} else {
|
|
99
|
+
// Zero packages is valid for `platform start` in image runtime mode —
|
|
100
|
+
// the workspace tree isn't shipped, only `.arc/platform/` is. Build
|
|
101
|
+
// commands enforce non-empty packages where they need them.
|
|
102
|
+
log("No workspace packages found — assuming image runtime mode.");
|
|
106
103
|
}
|
|
107
104
|
|
|
108
105
|
// Detect manifest.json or manifest.webmanifest in root dir
|
|
@@ -163,36 +160,55 @@ export async function buildAll(
|
|
|
163
160
|
|
|
164
161
|
log(`Building (concurrency parallel${noCache ? ", no-cache" : ""})...`);
|
|
165
162
|
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
163
|
+
// Phase 1 — context packages must finish FIRST. The access-extractor
|
|
164
|
+
// subprocess imports workspace packages by name, which resolve through
|
|
165
|
+
// node_modules to packages' `main` field (typically `dist/server/main/`).
|
|
166
|
+
await buildContextPackages(ws.rootDir, ws.packages, cache, noCache);
|
|
167
|
+
|
|
168
|
+
// Phase 1b — relocate per-package server bundles into `.arc/platform/server/`
|
|
169
|
+
// so the deploy image can be self-contained (image COPY needs everything
|
|
170
|
+
// server-side under one root). loadServerContext reads from here in prod.
|
|
171
|
+
copyContextServerBundles(ws);
|
|
172
|
+
|
|
173
|
+
// Phase 2 — extract access metadata (token name + hasCheck per module) in
|
|
174
|
+
// an isolated subprocess. This MUST run before chunk planning so we know
|
|
175
|
+
// which token group each module belongs to.
|
|
176
|
+
const accessMap = await extractAccessMap(ws.rootDir, ws.packages);
|
|
177
|
+
|
|
178
|
+
// Persist access map for the runtime host (server.ts reads at startup to
|
|
179
|
+
// wire up moduleAccessMap for filterManifestForTokens / signed URLs).
|
|
180
|
+
mkdirSync(ws.arcDir, { recursive: true });
|
|
181
|
+
writeFileSync(
|
|
182
|
+
join(ws.arcDir, "access.json"),
|
|
183
|
+
JSON.stringify(accessMap, null, 2) + "\n",
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Phase 3 — group modules into chunks (one Bun.build per group).
|
|
187
|
+
const plan = planChunks(ws.packages, accessMap);
|
|
188
|
+
ok(
|
|
189
|
+
`Chunks: ${plan.chunks
|
|
190
|
+
.map((c) => `${c}(${plan.groups.get(c)?.length ?? 0})`)
|
|
191
|
+
.join(", ")}`,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Phase 4 — independent parallel build units. Module chunks are the only
|
|
195
|
+
// unit whose output we need for the manifest; others update the cache.
|
|
196
|
+
const [modulesResult] = await Promise.all([
|
|
197
|
+
buildModulesByChunks(ws.rootDir, ws.modulesDir, plan, cache, noCache),
|
|
171
198
|
buildShell(ws, cache, noCache),
|
|
172
199
|
buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
|
|
173
200
|
copyBrowserAssets(ws, cache, noCache),
|
|
174
201
|
buildTranslations(ws.rootDir, ws.arcDir, cache, noCache),
|
|
175
202
|
]);
|
|
176
203
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
//
|
|
180
|
-
// map. Generated unconditionally — runtime container needs them to bun-install
|
|
181
|
-
// and to enforce protectBy rules. Order matters: deps before access (access
|
|
182
|
-
// extraction subprocess imports server bundles whose own deps may matter).
|
|
204
|
+
// Phase 5 — framework peer manifest at `<arcDir>/package.json`. Used by
|
|
205
|
+
// the deploy image build (single `bun install` for all peers, one copy
|
|
206
|
+
// shared across module bundles).
|
|
183
207
|
collectFrameworkDeps(ws.arcDir, ws.rootDir, ws.packages);
|
|
184
|
-
for (const pkg of ws.packages) {
|
|
185
|
-
collectModuleDeps(ws.arcDir, pkg);
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
await extractAccessMap(ws.arcDir, ws.packages);
|
|
189
|
-
} catch (e) {
|
|
190
|
-
err(`access-extractor failed: ${(e as Error).message}`);
|
|
191
|
-
// Don't fail the build — protection rules will be empty server-side but
|
|
192
|
-
// the rest of the deploy can still proceed.
|
|
193
|
-
}
|
|
194
208
|
|
|
195
|
-
|
|
209
|
+
saveBuildCache(ws.arcDir, cache);
|
|
210
|
+
|
|
211
|
+
const finalManifest = assembleManifest(ws, modulesResult.modules, plan.chunks, cache);
|
|
196
212
|
writeFileSync(
|
|
197
213
|
join(ws.modulesDir, "manifest.json"),
|
|
198
214
|
JSON.stringify(finalManifest, null, 2),
|
|
@@ -206,6 +222,7 @@ export async function buildAll(
|
|
|
206
222
|
function assembleManifest(
|
|
207
223
|
ws: WorkspaceInfo,
|
|
208
224
|
modules: ModuleDescriptor[],
|
|
225
|
+
chunks: readonly string[],
|
|
209
226
|
cache: BuildCache,
|
|
210
227
|
): BuildManifest {
|
|
211
228
|
// Aggregate shellHash from all shell:* unit output hashes.
|
|
@@ -220,12 +237,41 @@ function assembleManifest(
|
|
|
220
237
|
|
|
221
238
|
return {
|
|
222
239
|
modules,
|
|
240
|
+
chunks,
|
|
223
241
|
shellHash,
|
|
224
242
|
stylesHash,
|
|
225
243
|
buildTime: new Date().toISOString(),
|
|
226
244
|
};
|
|
227
245
|
}
|
|
228
246
|
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// Context server bundles — flatten to `<arcDir>/server/<safeName>.js`
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Copy each context package's compiled server bundle from
|
|
253
|
+
* `packages/<pkg>/dist/server/main/index.js` to a flat location at
|
|
254
|
+
* `<arcDir>/server/<safeName>.js`. The flat layout makes the deploy image
|
|
255
|
+
* self-contained — `COPY .arc/platform/` pulls everything server-side, no
|
|
256
|
+
* need to drag the entire `packages/` tree into the image.
|
|
257
|
+
*/
|
|
258
|
+
function copyContextServerBundles(ws: WorkspaceInfo): void {
|
|
259
|
+
const outDir = join(ws.arcDir, "server");
|
|
260
|
+
mkdirSync(outDir, { recursive: true });
|
|
261
|
+
|
|
262
|
+
for (const pkg of ws.packages) {
|
|
263
|
+
if (!isContextPackage(pkg.packageJson)) continue;
|
|
264
|
+
const src = join(pkg.path, "dist", "server", "main", "index.js");
|
|
265
|
+
if (!existsSync(src)) {
|
|
266
|
+
err(`Server bundle missing for ${pkg.name}: ${src}`);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const safeName = pkg.path.split("/").pop()!;
|
|
270
|
+
const dst = join(outDir, `${safeName}.js`);
|
|
271
|
+
copyFileSync(src, dst);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
229
275
|
// ---------------------------------------------------------------------------
|
|
230
276
|
// Browser assets — @arcote.tech/* deps deklarują w `arc.browserAssets` jakie
|
|
231
277
|
// pliki muszą być dostępne w przeglądarce (np. SQLite WASM worker + .wasm).
|
|
@@ -564,47 +610,60 @@ export async function buildShell(
|
|
|
564
610
|
// ---------------------------------------------------------------------------
|
|
565
611
|
|
|
566
612
|
export async function loadServerContext(
|
|
567
|
-
|
|
613
|
+
ws: WorkspaceInfo,
|
|
568
614
|
): Promise<{ context: any | null; moduleAccess: Map<string, any> }> {
|
|
569
|
-
const ctxPackages = packages.filter((p) => isContextPackage(p.packageJson));
|
|
570
|
-
if (ctxPackages.length === 0) return { context: null, moduleAccess: new Map() };
|
|
571
|
-
|
|
572
615
|
// Set globals for server context — framework packages (arc-auth etc.)
|
|
573
616
|
// use these at runtime to tree-shake browser/server code paths.
|
|
574
617
|
(globalThis as any).ONLY_SERVER = true;
|
|
575
618
|
(globalThis as any).ONLY_BROWSER = false;
|
|
576
619
|
(globalThis as any).ONLY_CLIENT = false;
|
|
577
620
|
|
|
578
|
-
// Resolve platform from the project's node_modules
|
|
579
|
-
//
|
|
621
|
+
// Resolve platform from the project's node_modules. Bun picks the `bun`
|
|
622
|
+
// export condition from package.json, which points at `index.server.ts`
|
|
623
|
+
// (server-safe, no React/JSX at top level).
|
|
580
624
|
const platformDir = join(process.cwd(), "node_modules", "@arcote.tech", "platform");
|
|
581
625
|
const platformPkg = JSON.parse(readFileSync(join(platformDir, "package.json"), "utf-8"));
|
|
582
626
|
const platformEntry = join(platformDir, platformPkg.main ?? "src/index.ts");
|
|
583
627
|
|
|
584
628
|
await import(platformEntry);
|
|
585
629
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
630
|
+
// Primary source: flattened server bundles at `<arcDir>/server/<safeName>.js`.
|
|
631
|
+
// The deploy image only has this directory — there's no workspace `packages/`
|
|
632
|
+
// tree. In dev, `copyContextServerBundles` populates this same location, so
|
|
633
|
+
// both modes go through the same code path.
|
|
634
|
+
const serverDir = join(ws.arcDir, "server");
|
|
635
|
+
const bundles = existsSync(serverDir)
|
|
636
|
+
? readdirSync(serverDir).filter((f) => f.endsWith(".js"))
|
|
637
|
+
: [];
|
|
638
|
+
|
|
639
|
+
if (bundles.length > 0) {
|
|
640
|
+
for (const file of bundles) {
|
|
641
|
+
const bundlePath = join(serverDir, file);
|
|
642
|
+
try {
|
|
643
|
+
await import(bundlePath);
|
|
644
|
+
} catch (e) {
|
|
645
|
+
err(`Failed to load server bundle ${file}: ${e}`);
|
|
646
|
+
}
|
|
597
647
|
}
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
648
|
+
} else if (ws.packages.length > 0) {
|
|
649
|
+
// Fallback for the "no .arc/platform/server/ yet" case (e.g. somebody
|
|
650
|
+
// wired up loadServerContext before running the build). This path goes
|
|
651
|
+
// through workspace packages directly — only meaningful in dev.
|
|
652
|
+
const ctxPackages = ws.packages.filter((p) => isContextPackage(p.packageJson));
|
|
653
|
+
for (const ctx of ctxPackages) {
|
|
654
|
+
const serverDist = join(ctx.path, "dist", "server", "main", "index.js");
|
|
655
|
+
if (!existsSync(serverDist)) {
|
|
656
|
+
err(`Context server dist not found: ${serverDist}`);
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
try {
|
|
660
|
+
await import(serverDist);
|
|
661
|
+
} catch (e) {
|
|
662
|
+
err(`Failed to load server context from ${ctx.name}: ${e}`);
|
|
663
|
+
}
|
|
607
664
|
}
|
|
665
|
+
} else {
|
|
666
|
+
return { context: null, moduleAccess: new Map() };
|
|
608
667
|
}
|
|
609
668
|
|
|
610
669
|
const { getContext, getAllModuleAccess } = await import(platformEntry);
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { existsSync, readFileSync, watch } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { startPlatformServer, type PlatformServer } from "./server";
|
|
4
|
+
import type { BuildManifest } from "./shared";
|
|
5
|
+
import {
|
|
6
|
+
buildAll,
|
|
7
|
+
collectArcPeerDeps,
|
|
8
|
+
err,
|
|
9
|
+
loadServerContext,
|
|
10
|
+
log,
|
|
11
|
+
ok,
|
|
12
|
+
type WorkspaceInfo,
|
|
13
|
+
} from "./shared";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// startup — single entry point for `arc platform start` and `arc platform dev`.
|
|
17
|
+
//
|
|
18
|
+
// The runtime host code path is IDENTICAL in both modes. Only two things
|
|
19
|
+
// differ:
|
|
20
|
+
// - dev mode runs `buildAll` on startup (and rebuilds on file changes)
|
|
21
|
+
// - dev mode wires up the file watcher + SSE notify on rebuild
|
|
22
|
+
//
|
|
23
|
+
// Production mode reads a pre-built manifest from disk (in the deploy image,
|
|
24
|
+
// that manifest was baked in at image build time) and never rebuilds.
|
|
25
|
+
//
|
|
26
|
+
// Module access map and signed URL semantics, chunk-aware static serving,
|
|
27
|
+
// /api/modules filtering — all identical. This is the "dev↔prod parity"
|
|
28
|
+
// promise that the v0.7 refactor was built around.
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
export interface StartPlatformOptions {
|
|
32
|
+
ws: WorkspaceInfo;
|
|
33
|
+
/** Listening port. Defaults to PORT env var or 5005. */
|
|
34
|
+
port?: number;
|
|
35
|
+
/** SQLite database path. Defaults to `<ws.rootDir>/.arc/data/<mode>.db`. */
|
|
36
|
+
dbPath?: string;
|
|
37
|
+
/** Dev mode: rebuild on startup, watch for changes, SSE-notify clients on rebuild. */
|
|
38
|
+
devMode: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function startPlatform(
|
|
42
|
+
opts: StartPlatformOptions,
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
const { ws, devMode } = opts;
|
|
45
|
+
const port = opts.port ?? parseInt(process.env.PORT || "5005", 10);
|
|
46
|
+
const dbPath =
|
|
47
|
+
opts.dbPath ??
|
|
48
|
+
join(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
|
|
49
|
+
|
|
50
|
+
// 1. Acquire the manifest. In dev we always rebuild (fast — cache makes
|
|
51
|
+
// subsequent passes incremental). In prod we read the pre-built one;
|
|
52
|
+
// missing manifest is a hard error.
|
|
53
|
+
let manifest: BuildManifest;
|
|
54
|
+
if (devMode) {
|
|
55
|
+
manifest = await buildAll(ws);
|
|
56
|
+
} else {
|
|
57
|
+
const manifestPath = join(ws.modulesDir, "manifest.json");
|
|
58
|
+
if (!existsSync(manifestPath)) {
|
|
59
|
+
err("No build found. Run `arc platform build` first.");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 2. Server context — same code path in both modes; loadServerContext
|
|
66
|
+
// prefers .arc/platform/server/*.js (the canonical location after the
|
|
67
|
+
// copyContextServerBundles step in buildAll).
|
|
68
|
+
log("Loading server context...");
|
|
69
|
+
const { context, moduleAccess } = await loadServerContext(ws);
|
|
70
|
+
if (context) {
|
|
71
|
+
ok("Context loaded");
|
|
72
|
+
} else {
|
|
73
|
+
log("No context — server endpoints skipped");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 3. Start the platform server. Cache headers + SSE behaviour are
|
|
77
|
+
// controlled inside the server by `devMode`; we just pass the flag.
|
|
78
|
+
const arcEntries = collectArcPeerDeps(ws.packages);
|
|
79
|
+
const platform = await startPlatformServer({
|
|
80
|
+
ws,
|
|
81
|
+
port,
|
|
82
|
+
manifest,
|
|
83
|
+
context,
|
|
84
|
+
moduleAccess,
|
|
85
|
+
dbPath,
|
|
86
|
+
devMode,
|
|
87
|
+
arcEntries,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
ok(`Server on http://localhost:${port}`);
|
|
91
|
+
if (platform.contextHandler) {
|
|
92
|
+
ok("Commands, queries, WebSocket — all on same port");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 4. Dev-only: file watcher + debounced rebuild + SSE notify.
|
|
96
|
+
if (devMode) {
|
|
97
|
+
attachDevWatcher(ws, platform);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 5. Graceful shutdown for both modes.
|
|
101
|
+
const cleanup = () => {
|
|
102
|
+
platform.stop();
|
|
103
|
+
process.exit(0);
|
|
104
|
+
};
|
|
105
|
+
process.on("SIGTERM", cleanup);
|
|
106
|
+
process.on("SIGINT", cleanup);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function attachDevWatcher(ws: WorkspaceInfo, platform: PlatformServer): void {
|
|
110
|
+
log("Watching for changes...");
|
|
111
|
+
let rebuildTimer: ReturnType<typeof setTimeout> | null = null;
|
|
112
|
+
let isRebuilding = false;
|
|
113
|
+
|
|
114
|
+
const triggerRebuild = () => {
|
|
115
|
+
if (rebuildTimer) clearTimeout(rebuildTimer);
|
|
116
|
+
rebuildTimer = setTimeout(async () => {
|
|
117
|
+
if (isRebuilding) return;
|
|
118
|
+
isRebuilding = true;
|
|
119
|
+
log("Rebuilding...");
|
|
120
|
+
try {
|
|
121
|
+
const next = await buildAll(ws);
|
|
122
|
+
platform.setManifest(next);
|
|
123
|
+
platform.notifyReload(next);
|
|
124
|
+
ok(`Rebuilt — ${next.modules.length} module(s)`);
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error(`Rebuild failed: ${e}`);
|
|
127
|
+
} finally {
|
|
128
|
+
isRebuilding = false;
|
|
129
|
+
}
|
|
130
|
+
}, 300);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
for (const pkg of ws.packages) {
|
|
134
|
+
const srcDir = join(pkg.path, "src");
|
|
135
|
+
if (!existsSync(srcDir)) continue;
|
|
136
|
+
|
|
137
|
+
watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
138
|
+
if (
|
|
139
|
+
!filename ||
|
|
140
|
+
filename.includes(".arc") ||
|
|
141
|
+
filename.endsWith(".d.ts") ||
|
|
142
|
+
filename.includes("node_modules") ||
|
|
143
|
+
filename.includes("dist")
|
|
144
|
+
)
|
|
145
|
+
return;
|
|
146
|
+
if (!filename.endsWith(".ts") && !filename.endsWith(".tsx")) return;
|
|
147
|
+
triggerRebuild();
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// .po files — translations are a separate cache unit, but buildAll picks
|
|
152
|
+
// them up via finalizeTranslations after extraction. Treat the same as src.
|
|
153
|
+
const localesDir = join(ws.rootDir, "locales");
|
|
154
|
+
if (existsSync(localesDir)) {
|
|
155
|
+
watch(localesDir, { recursive: false }, (_event, filename) => {
|
|
156
|
+
if (!filename?.endsWith(".po")) return;
|
|
157
|
+
triggerRebuild();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
package/runtime/Dockerfile
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# arcote/runtime — generic Arc platform runtime container.
|
|
2
|
-
#
|
|
3
|
-
# Image is intentionally version-agnostic. CLI version comes from the
|
|
4
|
-
# ARC_CLI_VERSION env var set by docker-compose (generated per-deploy by
|
|
5
|
-
# `arc platform deploy`), so a single `arcote/runtime:1` tag serves every
|
|
6
|
-
# CLI release. Bumping CLI only requires `bun publish` — no image rebuild.
|
|
7
|
-
#
|
|
8
|
-
# Layout in container:
|
|
9
|
-
# /app/.arc/cli/<ARC_CLI_VERSION>/ ← arc-cli + direct deps (installed by entrypoint, cached)
|
|
10
|
-
# /app/.arc/platform/ ← user volume: code, framework + per-module deps
|
|
11
|
-
# /app/.arc/data/ ← user volume: sqlite
|
|
12
|
-
# /root/.bun/install/cache/ ← shared bun store (volume)
|
|
13
|
-
|
|
14
|
-
FROM oven/bun:1-alpine
|
|
15
|
-
|
|
16
|
-
# tini for proper signal handling. build-base/python3/git in case any user dep
|
|
17
|
-
# has a native postinstall step (e.g. better-sqlite3). curl for healthchecks.
|
|
18
|
-
RUN apk add --no-cache tini ca-certificates build-base python3 git curl
|
|
19
|
-
|
|
20
|
-
WORKDIR /app
|
|
21
|
-
|
|
22
|
-
COPY entrypoint.sh /entrypoint.sh
|
|
23
|
-
RUN chmod +x /entrypoint.sh
|
|
24
|
-
|
|
25
|
-
EXPOSE 5005
|
|
26
|
-
ENV PORT=5005 \
|
|
27
|
-
ARC_DEPLOY_API=1
|
|
28
|
-
|
|
29
|
-
ENTRYPOINT ["tini", "--", "/entrypoint.sh"]
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# build-and-push.sh — manual multi-arch publish of pkrasinski/arc-runtime to Docker Hub.
|
|
3
|
-
#
|
|
4
|
-
# Image is generic (no CLI version baked in) — one tag covers every CLI release.
|
|
5
|
-
# Re-run only when the entrypoint script or base image needs to change.
|
|
6
|
-
#
|
|
7
|
-
# Prereqs: docker buildx + multi-arch builder configured, `docker login` done.
|
|
8
|
-
#
|
|
9
|
-
# Usage: bash build-and-push.sh [tag=1]
|
|
10
|
-
|
|
11
|
-
set -euo pipefail
|
|
12
|
-
|
|
13
|
-
TAG="${1:-1}"
|
|
14
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
-
|
|
16
|
-
docker buildx build \
|
|
17
|
-
--platform linux/amd64,linux/arm64 \
|
|
18
|
-
--tag "pkrasinski/arc-runtime:${TAG}" \
|
|
19
|
-
--tag "pkrasinski/arc-runtime:latest" \
|
|
20
|
-
--push \
|
|
21
|
-
"${SCRIPT_DIR}"
|
|
22
|
-
|
|
23
|
-
echo "✓ Published pkrasinski/arc-runtime:${TAG} (multi-arch)"
|
package/runtime/entrypoint.sh
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# entrypoint.sh — installs arc-cli at ARC_CLI_VERSION (cached per version),
|
|
3
|
-
# then hands off to `arc platform start`. The CLI itself decides between
|
|
4
|
-
# pre-deploy mode (no manifest yet) and full mode based on volume state —
|
|
5
|
-
# no logic for that lives here.
|
|
6
|
-
#
|
|
7
|
-
# bun install of framework peers and per-module deps is done by the CLI in
|
|
8
|
-
# response to /api/deploy/framework and /api/deploy/modules/:name — this
|
|
9
|
-
# script only ensures the CLI binary itself is present.
|
|
10
|
-
|
|
11
|
-
set -e
|
|
12
|
-
|
|
13
|
-
: "${ARC_CLI_VERSION:?ARC_CLI_VERSION env var required (set by docker-compose)}"
|
|
14
|
-
|
|
15
|
-
CLI_DIR="/app/.arc/cli/${ARC_CLI_VERSION}"
|
|
16
|
-
CLI_BIN="${CLI_DIR}/node_modules/@arcote.tech/arc-cli/dist/index.js"
|
|
17
|
-
|
|
18
|
-
if [ ! -f "$CLI_BIN" ]; then
|
|
19
|
-
echo "[entrypoint] installing @arcote.tech/arc-cli@${ARC_CLI_VERSION}..."
|
|
20
|
-
mkdir -p "$CLI_DIR"
|
|
21
|
-
cd "$CLI_DIR"
|
|
22
|
-
echo '{"name":"arc-runtime-cli","private":true,"type":"module"}' > package.json
|
|
23
|
-
bun add "@arcote.tech/arc-cli@${ARC_CLI_VERSION}"
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
# resolveWorkspace() in arc platform start exits hard if /app has no
|
|
27
|
-
# package.json. In runtime mode the workspace lives in /app/.arc/platform/
|
|
28
|
-
# but the CLI still walks up from cwd. Drop a stub manifest here so the
|
|
29
|
-
# walk-up resolves to /app/.arc/platform (or to a stable "no workspace"
|
|
30
|
-
# state in pre-deploy).
|
|
31
|
-
if [ ! -f /app/package.json ]; then
|
|
32
|
-
cat > /app/package.json <<'EOF'
|
|
33
|
-
{
|
|
34
|
-
"name": "arc-runtime",
|
|
35
|
-
"private": true,
|
|
36
|
-
"type": "module",
|
|
37
|
-
"workspaces": []
|
|
38
|
-
}
|
|
39
|
-
EOF
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# Make /app/.arc/platform the working directory — that's where deployed user
|
|
43
|
-
# code, deps and node_modules live (volume mount).
|
|
44
|
-
mkdir -p /app/.arc/platform
|
|
45
|
-
cd /app/.arc/platform
|
|
46
|
-
if [ ! -f package.json ]; then
|
|
47
|
-
cat > package.json <<'EOF'
|
|
48
|
-
{
|
|
49
|
-
"name": "arc-platform-runtime",
|
|
50
|
-
"private": true,
|
|
51
|
-
"type": "module",
|
|
52
|
-
"workspaces": []
|
|
53
|
-
}
|
|
54
|
-
EOF
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
echo "[entrypoint] starting arc platform (cli=${ARC_CLI_VERSION})"
|
|
58
|
-
exec bun run "$CLI_BIN" platform start
|