@meshxdata/fops 0.1.45 → 0.1.46
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/CHANGELOG.md +16 -18
- package/package.json +1 -1
- package/src/commands/lifecycle.js +81 -5
- package/src/commands/setup.js +45 -4
- package/src/plugins/bundled/fops-plugin-azure/index.js +29 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-core.js +1185 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-flux.js +1180 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-ingress.js +393 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-naming.js +104 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-network.js +296 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-postgres.js +768 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-reconcilers.js +538 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-secrets.js +849 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-stacks.js +643 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-state.js +145 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-storage.js +496 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-terraform.js +1032 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks.js +155 -4245
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-keyvault.js +186 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-results.js +5 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/infra-cmds.js +758 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/registry-cmds.js +250 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/test-cmds.js +2 -1
- package/src/plugins/bundled/fops-plugin-foundation/lib/apply.js +3 -2
- package/src/plugins/bundled/fops-plugin-foundation/lib/helpers.js +21 -0
- package/src/plugins/bundled/fops-plugin-foundation/lib/tools-read.js +3 -5
- package/src/ui/tui/App.js +13 -13
- package/src/web/dist/assets/index-NXC8Hvnp.css +1 -0
- package/src/web/dist/assets/index-QH1N4ejK.js +112 -0
- package/src/web/dist/index.html +2 -2
- package/src/web/server.js +4 -4
- package/src/web/dist/assets/index-BphVaAUd.css +0 -1
- package/src/web/dist/assets/index-CSckLzuG.js +0 -129
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
## [0.1.
|
|
2
|
-
|
|
1
|
+
## [0.1.46] - 2026-03-23
|
|
2
|
+
|
|
3
|
+
- default locust to CLI mode, add --web for UI (ca35bff)
|
|
4
|
+
- add locust command for load testing AKS clusters (1278722)
|
|
5
|
+
- update spot node pool default autoscaling to 1-20 (617c182)
|
|
6
|
+
- module for aks (3dd1a61)
|
|
7
|
+
- add hive to PG_SERVICE_DBS for fops pg-setup (afccb16)
|
|
8
|
+
- feat(azure): enhance aks doctor with ExternalSecrets and PGSSLMODE checks (8b14861)
|
|
9
|
+
- add foundation-postgres ExternalName service to reconciler (ea88e11)
|
|
10
|
+
- new flux templates (0e2e372)
|
|
11
|
+
- feat(azure): add storage-engine secrets to Key Vault (a4f488e)
|
|
12
|
+
- feat(azure-aks): add AUTH0_DOMAIN to template rendering variables (216c37e)
|
|
13
|
+
- feat(azure): add storage account creation per cluster (aa1b138)
|
|
14
|
+
- bump watcher (ab24473)
|
|
15
|
+
- fix: concurrent compute calls (#66) (03e2edf)
|
|
16
|
+
- bump backend version (5058ff5)
|
|
3
17
|
- bump fops to 0.1.44 (8c0ef5d)
|
|
4
18
|
- Mlflow and azure plugin fix (176881f)
|
|
5
19
|
- fix lifecycle (a2cb9e7)
|
|
@@ -166,22 +180,6 @@
|
|
|
166
180
|
- tui fixes (0599779)
|
|
167
181
|
- cleanup (27731f0)
|
|
168
182
|
- train (90bf559)
|
|
169
|
-
- training (f809bf6)
|
|
170
|
-
- training (ba2b836)
|
|
171
|
-
- training (6fc5267)
|
|
172
|
-
- training (4af8ac9)
|
|
173
|
-
- fix build script (bd82836)
|
|
174
|
-
- infra test (5b79815)
|
|
175
|
-
- infra test (3a0ac05)
|
|
176
|
-
- infra test (e5c67b5)
|
|
177
|
-
- tests (ae7b621)
|
|
178
|
-
- tests (c09ae6a)
|
|
179
|
-
- update tui (4784153)
|
|
180
|
-
- training (0a5a330)
|
|
181
|
-
- tui (df4dd4a)
|
|
182
|
-
- pkg builds (4dc9993)
|
|
183
|
-
- also source env for creds (9a17d8f)
|
|
184
|
-
- fcl support (e8a5743)
|
|
185
183
|
|
|
186
184
|
# Changelog
|
|
187
185
|
|
package/package.json
CHANGED
|
@@ -32,6 +32,7 @@ export function registerLifecycleCommands(program, registry) {
|
|
|
32
32
|
.option("--url <url>", "Set the public URL (e.g. https://192.168.1.10) — writes FOUNDATION_PUBLIC_URL to .env and enables traefik if port is 443")
|
|
33
33
|
.option("--pull", "Pull latest images before starting (default: skip pull)")
|
|
34
34
|
.option("--frontend-dev", "Run frontend in development mode (hot reload) instead of production build")
|
|
35
|
+
.option("--local", "Run frontend dev server locally (outside container) with npm run dev — requires --frontend-dev")
|
|
35
36
|
.option("--minio-storage", "Use MinIO as the storage backend instead of foundation-storage-engine")
|
|
36
37
|
.option("--non-interactive", "Skip interactive prompts (auto-grant admin, no browser open)")
|
|
37
38
|
.action(async (component, branch, opts) => {
|
|
@@ -443,6 +444,14 @@ async function runUp(program, registry, opts) {
|
|
|
443
444
|
console.error(chalk.dim(` Available: ${Object.keys(COMPONENT_SUBMODULES).join(", ")}\n`));
|
|
444
445
|
process.exit(1);
|
|
445
446
|
}
|
|
447
|
+
|
|
448
|
+
// --local requires --frontend-dev
|
|
449
|
+
if (opts.local && !opts.frontendDev) {
|
|
450
|
+
console.error(chalk.red(`\n --local requires --frontend-dev flag`));
|
|
451
|
+
console.error(chalk.dim(` Usage: fops up --frontend-dev --local\n`));
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
|
|
446
455
|
if (comp && opts.branch) {
|
|
447
456
|
const componentDir = path.join(root, comp.dir);
|
|
448
457
|
if (!fs.existsSync(path.join(componentDir, ".git"))) {
|
|
@@ -580,7 +589,10 @@ async function runUp(program, registry, opts) {
|
|
|
580
589
|
}
|
|
581
590
|
|
|
582
591
|
// Frontend: either production (frontend-prod) or dev with hot reload (frontend-dev)
|
|
583
|
-
|
|
592
|
+
// --local: skip frontend profile entirely, we'll run npm dev locally
|
|
593
|
+
if (!opts.local) {
|
|
594
|
+
activeProfiles.add(opts.frontendDev ? "frontend-dev" : "frontend-prod");
|
|
595
|
+
}
|
|
584
596
|
|
|
585
597
|
const profileArgs = [
|
|
586
598
|
...(opts.minioStorage ? ["-f", "docker-compose.yaml", "-f", "docker-compose.minio-storage.yaml"] : []),
|
|
@@ -1363,11 +1375,18 @@ async function runUp(program, registry, opts) {
|
|
|
1363
1375
|
clearSpinner();
|
|
1364
1376
|
|
|
1365
1377
|
let result;
|
|
1378
|
+
const skipFrontendContainer = opts.local && opts.frontendDev;
|
|
1366
1379
|
if (componentOnlyUp) {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
:
|
|
1370
|
-
|
|
1380
|
+
if (skipFrontendContainer && opts.component === "frontend") {
|
|
1381
|
+
// --local: skip container, we'll run npm dev locally after
|
|
1382
|
+
result = { exitCode: 0 };
|
|
1383
|
+
console.log(chalk.dim(" Skipping frontend container (--local mode)..."));
|
|
1384
|
+
} else {
|
|
1385
|
+
const serviceList = opts.frontendDev && opts.component === "frontend"
|
|
1386
|
+
? ["foundation-frontend-dev"]
|
|
1387
|
+
: comp.restart;
|
|
1388
|
+
result = await runUpInner(` Starting ${opts.component}...`, null, serviceList);
|
|
1389
|
+
}
|
|
1371
1390
|
} else if (needsSequentialUp) {
|
|
1372
1391
|
const k3sOnlyArgs = profileArgs.filter((a, i, arr) => !(a === "--profile" && arr[i + 1] === "traefik") && !(arr[i - 1] === "--profile" && a === "traefik"));
|
|
1373
1392
|
result = await runUpInner(" Starting services (k3s)...", k3sOnlyArgs);
|
|
@@ -1479,6 +1498,63 @@ async function runUp(program, registry, opts) {
|
|
|
1479
1498
|
}
|
|
1480
1499
|
}
|
|
1481
1500
|
|
|
1501
|
+
// --local: Run frontend dev server locally with compose env vars
|
|
1502
|
+
if (skipFrontendContainer) {
|
|
1503
|
+
console.log(chalk.cyan("\n Starting local frontend dev server..."));
|
|
1504
|
+
const frontendDir = path.join(root, "foundation-frontend");
|
|
1505
|
+
if (!fs.existsSync(path.join(frontendDir, "package.json"))) {
|
|
1506
|
+
console.error(chalk.red(" foundation-frontend not found. Run: git submodule update --init foundation-frontend"));
|
|
1507
|
+
process.exitCode = 1;
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// Extract environment variables from compose config for frontend-dev service
|
|
1512
|
+
let frontendEnv = {};
|
|
1513
|
+
try {
|
|
1514
|
+
const { stdout: configJson } = await execa("docker", [
|
|
1515
|
+
"compose", "--profile", "frontend-dev", "config", "--format", "json",
|
|
1516
|
+
], { cwd: root, reject: false, timeout: 15000 });
|
|
1517
|
+
if (configJson?.trim()) {
|
|
1518
|
+
const config = JSON.parse(configJson);
|
|
1519
|
+
const frontendSvc = config.services?.["foundation-frontend-dev"];
|
|
1520
|
+
if (frontendSvc?.environment) {
|
|
1521
|
+
for (const [key, val] of Object.entries(frontendSvc.environment)) {
|
|
1522
|
+
frontendEnv[key] = val;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
} catch (err) {
|
|
1527
|
+
console.error(chalk.yellow(" Could not extract compose env vars:"), err.message);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// Transform docker-internal URLs to localhost for local dev
|
|
1531
|
+
// Backend is exposed on host port 9001 (container 9000 → host 9001)
|
|
1532
|
+
frontendEnv.INTERNAL_API_URL = "http://localhost:9001";
|
|
1533
|
+
frontendEnv.PORT = "3002";
|
|
1534
|
+
// Remove WATCHPACK_POLLING - not needed outside container
|
|
1535
|
+
delete frontendEnv.WATCHPACK_POLLING;
|
|
1536
|
+
|
|
1537
|
+
// Merge with process.env (compose vars override, then specific overrides)
|
|
1538
|
+
const npmEnv = { ...process.env, ...frontendEnv };
|
|
1539
|
+
|
|
1540
|
+
console.log(chalk.dim(" Environment:"));
|
|
1541
|
+
console.log(chalk.dim(` NEXT_PUBLIC_API_URL=${frontendEnv.NEXT_PUBLIC_API_URL || "not set"}`));
|
|
1542
|
+
console.log(chalk.dim(` INTERNAL_API_URL=${frontendEnv.INTERNAL_API_URL}`));
|
|
1543
|
+
console.log(chalk.dim(` AUTH0_ENABLED=${frontendEnv.AUTH0_ENABLED || "not set"}`));
|
|
1544
|
+
|
|
1545
|
+
// Run npm run dev in foreground (replaces this process)
|
|
1546
|
+
console.log(chalk.cyan("\n Running: npm run dev"));
|
|
1547
|
+
console.log(chalk.dim(" Press Ctrl+C to stop the dev server\n"));
|
|
1548
|
+
const npmProc = execa("npm", ["run", "dev"], {
|
|
1549
|
+
cwd: frontendDir,
|
|
1550
|
+
env: npmEnv,
|
|
1551
|
+
stdio: "inherit",
|
|
1552
|
+
});
|
|
1553
|
+
npmProc.catch(() => {}); // Don't throw on Ctrl+C
|
|
1554
|
+
await npmProc;
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1482
1558
|
// Detect non-interactive environment (no TTY on stdin, e.g. remote VM, CI, cloud-init)
|
|
1483
1559
|
// --non-interactive flag forces non-interactive mode (used by fops azure provisioning)
|
|
1484
1560
|
const isInteractive = !opts.nonInteractive && process.stdin.isTTY === true;
|
package/src/commands/setup.js
CHANGED
|
@@ -246,12 +246,53 @@ export function registerSetupCommands(program, registry) {
|
|
|
246
246
|
const root = rootDir();
|
|
247
247
|
if (root) {
|
|
248
248
|
console.log(DIM(" Updating compose project..."));
|
|
249
|
+
let stashedMain = false;
|
|
249
250
|
try {
|
|
250
251
|
const { stdout: branch } = await execa("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: root, timeout: 5000 });
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
252
|
+
const branchName = branch.trim();
|
|
253
|
+
|
|
254
|
+
const tryPull = async () => {
|
|
255
|
+
return execa("git", ["pull", "--ff-only", "origin", branchName], { cwd: root, stdio: "pipe", timeout: 120_000 });
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
await retry(tryPull, { attempts: 3, delay: 2000, onRetry: (n, max) => console.log(DIM(` Retrying pull (${n}/${max - 1})...`)) });
|
|
260
|
+
} catch (pullErr) {
|
|
261
|
+
const errOut = (pullErr.stderr || pullErr.stdout || pullErr.message || "").toString();
|
|
262
|
+
const wouldOverwrite = /would be overwritten by merge|Your local changes to the following files would be overwritten/i.test(errOut);
|
|
263
|
+
const needsStash = /Please commit your changes or stash them/i.test(errOut);
|
|
264
|
+
|
|
265
|
+
if (wouldOverwrite || needsStash) {
|
|
266
|
+
if (!process.stdin.isTTY) {
|
|
267
|
+
console.log(WARN(" ⚠ Local changes would be overwritten by pull."));
|
|
268
|
+
console.log(DIM(" Run in a TTY to be prompted to stash, or: git stash && fops update"));
|
|
269
|
+
throw pullErr;
|
|
270
|
+
}
|
|
271
|
+
const { getInquirer } = await import("../lazy.js");
|
|
272
|
+
const { stash } = await (await getInquirer()).prompt([
|
|
273
|
+
{
|
|
274
|
+
type: "confirm",
|
|
275
|
+
name: "stash",
|
|
276
|
+
message: "Local changes would be overwritten. Stash them and continue?",
|
|
277
|
+
default: true,
|
|
278
|
+
},
|
|
279
|
+
]);
|
|
280
|
+
if (!stash) {
|
|
281
|
+
console.log(DIM(" Skipping update. Stash or commit changes then run fops update again."));
|
|
282
|
+
throw pullErr;
|
|
283
|
+
}
|
|
284
|
+
await execa("git", ["stash", "push", "-m", "fops update: stashed before pull"], { cwd: root, timeout: 10_000 });
|
|
285
|
+
stashedMain = true;
|
|
286
|
+
await tryPull();
|
|
287
|
+
} else {
|
|
288
|
+
throw pullErr;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (stashedMain) {
|
|
293
|
+
console.log(WARN(" Stashed local changes in compose project"));
|
|
294
|
+
console.log(DIM(" Restore with: git stash pop"));
|
|
295
|
+
}
|
|
255
296
|
console.log(OK(" ✓ Compose project updated"));
|
|
256
297
|
} catch (err) {
|
|
257
298
|
console.log(WARN(` ⚠ git pull failed: ${err.message}`));
|
|
@@ -18,6 +18,7 @@ import { registerVmCommands } from "./lib/commands/vm-cmds.js";
|
|
|
18
18
|
import { registerFleetCommands } from "./lib/commands/fleet-cmds.js";
|
|
19
19
|
import { registerTestCommands } from "./lib/commands/test-cmds.js";
|
|
20
20
|
import { registerInfraCommands } from "./lib/commands/infra-cmds.js";
|
|
21
|
+
import { registerRegistryCommands } from "./lib/commands/registry-cmds.js";
|
|
21
22
|
|
|
22
23
|
export { resolveFoundationCreds, resolveAuth0Config, authenticateVm, vmFetch };
|
|
23
24
|
|
|
@@ -33,6 +34,7 @@ export async function register(api) {
|
|
|
33
34
|
registerFleetCommands(azure);
|
|
34
35
|
registerTestCommands(azure);
|
|
35
36
|
registerInfraCommands(azure);
|
|
37
|
+
registerRegistryCommands(azure);
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
// ── Cost agent tools + agent ───────────────────────────────────────────
|
|
@@ -186,6 +188,22 @@ export async function register(api) {
|
|
|
186
188
|
},
|
|
187
189
|
});
|
|
188
190
|
|
|
191
|
+
api.registerDoctorCheck({
|
|
192
|
+
name: "cloudflared",
|
|
193
|
+
fn: async (ok, warn) => {
|
|
194
|
+
try {
|
|
195
|
+
const { execa } = await import("execa");
|
|
196
|
+
const { stdout } = await execa("cloudflared", ["--version"], { timeout: 10000 });
|
|
197
|
+
ok(stdout.trim().split("\n")[0]);
|
|
198
|
+
} catch {
|
|
199
|
+
const hint = process.platform === "darwin"
|
|
200
|
+
? "optional — run: brew install cloudflared"
|
|
201
|
+
: "optional — see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/";
|
|
202
|
+
warn("cloudflared not installed", hint);
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
189
207
|
// ── Hook: before:up — auto-sync Key Vault secrets ─────────────────────
|
|
190
208
|
|
|
191
209
|
api.registerHook("before:up", async () => {
|
|
@@ -381,6 +399,17 @@ export async function register(api) {
|
|
|
381
399
|
"- `fops azure aks flux reconcile [name]` — Force reconciliation",
|
|
382
400
|
"Flux watches a Git repo and auto-applies manifests to the cluster.",
|
|
383
401
|
"Push to repo → Flux detects → applies. No kubectl needed.",
|
|
402
|
+
"",
|
|
403
|
+
"### Registry (OCI/Docker via Cloudflare Access)",
|
|
404
|
+
"Push images to AKS-hosted Zot registry through Cloudflare Access tunnel.",
|
|
405
|
+
"",
|
|
406
|
+
"- `fops azure registry login [--domain <url>]` — Authenticate via cloudflared (opens browser)",
|
|
407
|
+
"- `fops azure registry push <image> [--tag <tag>]` — Tag and push image to registry",
|
|
408
|
+
"- `fops azure registry pull <image>` — Pull image from registry",
|
|
409
|
+
"- `fops azure registry logout` — Remove stored credentials",
|
|
410
|
+
"",
|
|
411
|
+
"Workflow: login once per session → push branch images → they're available in AKS.",
|
|
412
|
+
"Credentials stored in ~/.fops/registry/ and ~/.docker/config.json.",
|
|
384
413
|
].join("\n"),
|
|
385
414
|
score: 0.9,
|
|
386
415
|
},
|