dokku-compose 0.4.0 → 0.5.1
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 +139 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -156,12 +156,10 @@ function createRunner(opts = {}) {
|
|
|
156
156
|
await execDokku(args);
|
|
157
157
|
},
|
|
158
158
|
async query(...args) {
|
|
159
|
-
if (opts.dryRun) return "";
|
|
160
159
|
const { stdout } = await execDokku(args);
|
|
161
160
|
return stdout.trim();
|
|
162
161
|
},
|
|
163
162
|
async check(...args) {
|
|
164
|
-
if (opts.dryRun) return false;
|
|
165
163
|
const { ok } = await execDokku(args);
|
|
166
164
|
return ok;
|
|
167
165
|
},
|
|
@@ -284,6 +282,40 @@ var Apps = {
|
|
|
284
282
|
}
|
|
285
283
|
};
|
|
286
284
|
|
|
285
|
+
// src/resources/parsers.ts
|
|
286
|
+
function parseReport(raw, namespace) {
|
|
287
|
+
const result = {};
|
|
288
|
+
const prefix = new RegExp(`^${namespace}\\s+`, "i");
|
|
289
|
+
for (const line of raw.split("\n")) {
|
|
290
|
+
if (line.trimStart().startsWith("=====>")) continue;
|
|
291
|
+
const colonIdx = line.indexOf(":");
|
|
292
|
+
if (colonIdx === -1) continue;
|
|
293
|
+
const rawKey = line.slice(0, colonIdx).trim();
|
|
294
|
+
if (!rawKey) continue;
|
|
295
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
296
|
+
const stripped = rawKey.replace(prefix, "");
|
|
297
|
+
const key = stripped.toLowerCase().replace(/\s+/g, "-");
|
|
298
|
+
if (key.startsWith("computed-") || key.startsWith("global-") || key === "last-visited-at") continue;
|
|
299
|
+
if (!value) continue;
|
|
300
|
+
result[key] = value;
|
|
301
|
+
}
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
function parseBulkReport(raw, namespace) {
|
|
305
|
+
const result = /* @__PURE__ */ new Map();
|
|
306
|
+
const sections = raw.split(/(?=^=====> )/m).filter((s) => s.trim());
|
|
307
|
+
for (const section of sections) {
|
|
308
|
+
const headerEnd = section.indexOf("\n");
|
|
309
|
+
if (headerEnd === -1) continue;
|
|
310
|
+
const header = section.slice(0, headerEnd);
|
|
311
|
+
const match = header.match(/^=====> (.+?)\s+\S+\s+information/);
|
|
312
|
+
if (!match) continue;
|
|
313
|
+
const app = match[1];
|
|
314
|
+
result.set(app, parseReport(section, namespace));
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
287
319
|
// src/resources/lists.ts
|
|
288
320
|
function splitWords(raw) {
|
|
289
321
|
return raw.split(/\s+/).map((s) => s.trim()).filter(Boolean);
|
|
@@ -297,6 +329,15 @@ var Ports = {
|
|
|
297
329
|
const raw = await ctx.query("ports:report", target, "--ports-map");
|
|
298
330
|
return splitWords(raw);
|
|
299
331
|
},
|
|
332
|
+
readAll: async (ctx) => {
|
|
333
|
+
const raw = await ctx.query("ports:report");
|
|
334
|
+
const bulk = parseBulkReport(raw, "ports");
|
|
335
|
+
const result = /* @__PURE__ */ new Map();
|
|
336
|
+
for (const [app, report] of bulk) {
|
|
337
|
+
result.set(app, report["map"] ? splitWords(report["map"]) : []);
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
},
|
|
300
341
|
onChange: async (ctx, target, change) => {
|
|
301
342
|
await ctx.run("ports:set", target, ...change.after);
|
|
302
343
|
}
|
|
@@ -307,6 +348,15 @@ var Domains = {
|
|
|
307
348
|
const raw = await ctx.query("domains:report", target, "--domains-app-vhosts");
|
|
308
349
|
return splitLines(raw);
|
|
309
350
|
},
|
|
351
|
+
readAll: async (ctx) => {
|
|
352
|
+
const raw = await ctx.query("domains:report");
|
|
353
|
+
const bulk = parseBulkReport(raw, "domains");
|
|
354
|
+
const result = /* @__PURE__ */ new Map();
|
|
355
|
+
for (const [app, report] of bulk) {
|
|
356
|
+
result.set(app, report["app-vhosts"] ? splitWords(report["app-vhosts"]) : []);
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
359
|
+
},
|
|
310
360
|
onChange: async (ctx, target, { added, removed }) => {
|
|
311
361
|
for (const d of removed) await ctx.run("domains:remove", target, d);
|
|
312
362
|
for (const d of added) await ctx.run("domains:add", target, d);
|
|
@@ -318,32 +368,21 @@ var Storage = {
|
|
|
318
368
|
const raw = await ctx.query("storage:report", target, "--storage-mounts");
|
|
319
369
|
return splitLines(raw);
|
|
320
370
|
},
|
|
371
|
+
readAll: async (ctx) => {
|
|
372
|
+
const raw = await ctx.query("storage:report");
|
|
373
|
+
const bulk = parseBulkReport(raw, "storage");
|
|
374
|
+
const result = /* @__PURE__ */ new Map();
|
|
375
|
+
for (const [app, report] of bulk) {
|
|
376
|
+
result.set(app, report["mounts"] ? splitLines(report["mounts"]) : []);
|
|
377
|
+
}
|
|
378
|
+
return result;
|
|
379
|
+
},
|
|
321
380
|
onChange: async (ctx, target, { added, removed }) => {
|
|
322
381
|
for (const m of removed) await ctx.run("storage:unmount", target, m);
|
|
323
382
|
for (const m of added) await ctx.run("storage:mount", target, m);
|
|
324
383
|
}
|
|
325
384
|
};
|
|
326
385
|
|
|
327
|
-
// src/resources/parsers.ts
|
|
328
|
-
function parseReport(raw, namespace) {
|
|
329
|
-
const result = {};
|
|
330
|
-
const prefix = new RegExp(`^${namespace}\\s+`, "i");
|
|
331
|
-
for (const line of raw.split("\n")) {
|
|
332
|
-
if (line.trimStart().startsWith("=====>")) continue;
|
|
333
|
-
const colonIdx = line.indexOf(":");
|
|
334
|
-
if (colonIdx === -1) continue;
|
|
335
|
-
const rawKey = line.slice(0, colonIdx).trim();
|
|
336
|
-
if (!rawKey) continue;
|
|
337
|
-
const value = line.slice(colonIdx + 1).trim();
|
|
338
|
-
const stripped = rawKey.replace(prefix, "");
|
|
339
|
-
const key = stripped.toLowerCase().replace(/\s+/g, "-");
|
|
340
|
-
if (key.startsWith("computed-") || key.startsWith("global-") || key === "last-visited-at") continue;
|
|
341
|
-
if (!value) continue;
|
|
342
|
-
result[key] = value;
|
|
343
|
-
}
|
|
344
|
-
return result;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
386
|
// src/resources/properties.ts
|
|
348
387
|
function propertyResource(opts) {
|
|
349
388
|
return {
|
|
@@ -352,6 +391,10 @@ function propertyResource(opts) {
|
|
|
352
391
|
const raw = await ctx.query(`${opts.namespace}:report`, target);
|
|
353
392
|
return parseReport(raw, opts.namespace);
|
|
354
393
|
},
|
|
394
|
+
async readAll(ctx) {
|
|
395
|
+
const raw = await ctx.query(`${opts.namespace}:report`);
|
|
396
|
+
return parseBulkReport(raw, opts.namespace);
|
|
397
|
+
},
|
|
355
398
|
async onChange(ctx, target, change) {
|
|
356
399
|
for (const [key, value] of Object.entries({ ...change.added, ...change.modified })) {
|
|
357
400
|
await ctx.run(opts.setCmd, target, key, String(value));
|
|
@@ -387,6 +430,15 @@ var Scheduler = {
|
|
|
387
430
|
const report = parseReport(raw, "scheduler");
|
|
388
431
|
return report["selected"] ?? "";
|
|
389
432
|
},
|
|
433
|
+
async readAll(ctx) {
|
|
434
|
+
const raw = await ctx.query("scheduler:report");
|
|
435
|
+
const bulk = parseBulkReport(raw, "scheduler");
|
|
436
|
+
const result = /* @__PURE__ */ new Map();
|
|
437
|
+
for (const [app, report] of bulk) {
|
|
438
|
+
result.set(app, report["selected"] ?? "");
|
|
439
|
+
}
|
|
440
|
+
return result;
|
|
441
|
+
},
|
|
390
442
|
async onChange(ctx, target, change) {
|
|
391
443
|
await ctx.run("scheduler:set", target, "selected", change.after);
|
|
392
444
|
}
|
|
@@ -399,6 +451,15 @@ var Proxy = {
|
|
|
399
451
|
const raw = await ctx.query("proxy:report", target, "--proxy-enabled");
|
|
400
452
|
return raw.trim() === "true";
|
|
401
453
|
},
|
|
454
|
+
readAll: async (ctx) => {
|
|
455
|
+
const raw = await ctx.query("proxy:report");
|
|
456
|
+
const bulk = parseBulkReport(raw, "proxy");
|
|
457
|
+
const result = /* @__PURE__ */ new Map();
|
|
458
|
+
for (const [app, report] of bulk) {
|
|
459
|
+
result.set(app, report["enabled"] === "true");
|
|
460
|
+
}
|
|
461
|
+
return result;
|
|
462
|
+
},
|
|
402
463
|
onChange: async (ctx, target, { after }) => {
|
|
403
464
|
await ctx.run(after ? "proxy:enable" : "proxy:disable", target);
|
|
404
465
|
}
|
|
@@ -451,6 +512,15 @@ var Certs = {
|
|
|
451
512
|
const raw = await ctx.query("certs:report", target, "--ssl-enabled");
|
|
452
513
|
return raw.trim() === "true";
|
|
453
514
|
},
|
|
515
|
+
readAll: async (ctx) => {
|
|
516
|
+
const raw = await ctx.query("certs:report");
|
|
517
|
+
const bulk = parseBulkReport(raw, "ssl");
|
|
518
|
+
const result = /* @__PURE__ */ new Map();
|
|
519
|
+
for (const [app, report] of bulk) {
|
|
520
|
+
result.set(app, report["enabled"] === "true");
|
|
521
|
+
}
|
|
522
|
+
return result;
|
|
523
|
+
},
|
|
454
524
|
onChange: async (ctx, target, { before, after }) => {
|
|
455
525
|
if (after === false && before) {
|
|
456
526
|
await ctx.run("certs:remove", target);
|
|
@@ -505,6 +575,15 @@ var Git = {
|
|
|
505
575
|
const report = await ctx.query("git:report", target, "--git-deploy-branch");
|
|
506
576
|
return { deploy_branch: report.trim() || void 0 };
|
|
507
577
|
},
|
|
578
|
+
readAll: async (ctx) => {
|
|
579
|
+
const raw = await ctx.query("git:report");
|
|
580
|
+
const bulk = parseBulkReport(raw, "git");
|
|
581
|
+
const result = /* @__PURE__ */ new Map();
|
|
582
|
+
for (const [app, report] of bulk) {
|
|
583
|
+
result.set(app, { deploy_branch: report["deploy-branch"] || void 0 });
|
|
584
|
+
}
|
|
585
|
+
return result;
|
|
586
|
+
},
|
|
508
587
|
onChange: async (ctx, target, { after }) => {
|
|
509
588
|
if (after.deploy_branch) {
|
|
510
589
|
await ctx.run("git:set", target, "deploy-branch", after.deploy_branch);
|
|
@@ -542,6 +621,15 @@ var Networks = {
|
|
|
542
621
|
const raw = await ctx.query("network:report", target, "--network-attach-post-deploy");
|
|
543
622
|
return raw.trim() ? raw.trim().split(/\s+/) : [];
|
|
544
623
|
},
|
|
624
|
+
readAll: async (ctx) => {
|
|
625
|
+
const raw = await ctx.query("network:report");
|
|
626
|
+
const bulk = parseBulkReport(raw, "network");
|
|
627
|
+
const result = /* @__PURE__ */ new Map();
|
|
628
|
+
for (const [app, report] of bulk) {
|
|
629
|
+
result.set(app, report["attach-post-deploy"] ? report["attach-post-deploy"].split(/\s+/) : []);
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
},
|
|
545
633
|
onChange: async (ctx, target, { after }) => {
|
|
546
634
|
await ctx.run("network:set", target, "attach-post-deploy", ...after);
|
|
547
635
|
}
|
|
@@ -858,12 +946,19 @@ async function runExport(ctx, opts) {
|
|
|
858
946
|
if (networks.length > 0) config.networks = networks;
|
|
859
947
|
const services = await exportServices(ctx);
|
|
860
948
|
if (Object.keys(services).length > 0) config.services = services;
|
|
949
|
+
const prefetched = /* @__PURE__ */ new Map();
|
|
950
|
+
await Promise.all(
|
|
951
|
+
ALL_APP_RESOURCES.filter((r) => !r.forceApply && !r.key.startsWith("_") && r.readAll).map(async (r) => {
|
|
952
|
+
prefetched.set(r.key, await r.readAll(ctx));
|
|
953
|
+
})
|
|
954
|
+
);
|
|
861
955
|
for (const app of apps) {
|
|
862
956
|
const appConfig = {};
|
|
863
957
|
for (const resource of ALL_APP_RESOURCES) {
|
|
864
958
|
if (resource.key.startsWith("_")) continue;
|
|
865
959
|
if (resource.forceApply) continue;
|
|
866
|
-
const
|
|
960
|
+
const bulk = prefetched.get(resource.key);
|
|
961
|
+
const value = bulk ? bulk.get(app) : await resource.read(ctx, app);
|
|
867
962
|
if (value === void 0 || value === null || value === "") continue;
|
|
868
963
|
if (Array.isArray(value) && value.length === 0) continue;
|
|
869
964
|
if (typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0) continue;
|
|
@@ -886,6 +981,12 @@ async function runExport(ctx, opts) {
|
|
|
886
981
|
import chalk2 from "chalk";
|
|
887
982
|
async function computeDiff(ctx, config) {
|
|
888
983
|
const result = { apps: {}, services: {}, inSync: true };
|
|
984
|
+
const prefetched = /* @__PURE__ */ new Map();
|
|
985
|
+
await Promise.all(
|
|
986
|
+
ALL_APP_RESOURCES.filter((r) => !r.forceApply && !r.key.startsWith("_") && r.readAll).map(async (r) => {
|
|
987
|
+
prefetched.set(r.key, await r.readAll(ctx));
|
|
988
|
+
})
|
|
989
|
+
);
|
|
889
990
|
for (const [app, appConfig] of Object.entries(config.apps)) {
|
|
890
991
|
const appDiff = {};
|
|
891
992
|
for (const resource of ALL_APP_RESOURCES) {
|
|
@@ -898,7 +999,8 @@ async function computeDiff(ctx, config) {
|
|
|
898
999
|
desired = appConfig[resource.key];
|
|
899
1000
|
}
|
|
900
1001
|
if (desired === void 0) continue;
|
|
901
|
-
const
|
|
1002
|
+
const bulk = prefetched.get(resource.key);
|
|
1003
|
+
const current = bulk ? bulk.get(app) : await resource.read(ctx, app);
|
|
902
1004
|
const change = computeChange(current, desired);
|
|
903
1005
|
if (!change.changed) {
|
|
904
1006
|
appDiff[resource.key] = { status: "in-sync", desired, current };
|
|
@@ -1027,6 +1129,14 @@ function validate(filePath) {
|
|
|
1027
1129
|
return { errors, warnings };
|
|
1028
1130
|
}
|
|
1029
1131
|
|
|
1132
|
+
// src/core/mask.ts
|
|
1133
|
+
function maskSensitiveArgs(cmd) {
|
|
1134
|
+
return cmd.replace(/(\S*(?:TOKEN|SECRET|PASSWORD|KEY|AUTH|CREDENTIAL)\S*)=(\S+)/gi, (_, key, value) => {
|
|
1135
|
+
if (value.length <= 4) return `${key}=****`;
|
|
1136
|
+
return `${key}=****${value.slice(-4)}`;
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1030
1140
|
// src/index.ts
|
|
1031
1141
|
var require2 = createRequire(import.meta.url);
|
|
1032
1142
|
var { version } = require2("../package.json");
|
|
@@ -1037,7 +1147,7 @@ function makeRunner(opts) {
|
|
|
1037
1147
|
dryRun: opts.dryRun ?? false
|
|
1038
1148
|
});
|
|
1039
1149
|
}
|
|
1040
|
-
program.command("up [apps...]").description("Create/update apps and services to match config").option("-f, --file <path>", "Config file", "dokku-compose.yml").option("--dry-run", "Print commands without executing").option("--fail-fast", "Stop on first error").action(async (apps, opts) => {
|
|
1150
|
+
program.command("up [apps...]").description("Create/update apps and services to match config").option("-f, --file <path>", "Config file", "dokku-compose.yml").option("--dry-run", "Print commands without executing").option("--sensitive", "Show sensitive values in dry-run output (masked by default)").option("--fail-fast", "Stop on first error").action(async (apps, opts) => {
|
|
1041
1151
|
const config = loadConfig(opts.file);
|
|
1042
1152
|
const runner = makeRunner(opts);
|
|
1043
1153
|
const ctx = createContext(runner);
|
|
@@ -1045,7 +1155,10 @@ program.command("up [apps...]").description("Create/update apps and services to
|
|
|
1045
1155
|
await runUp(ctx, config, apps);
|
|
1046
1156
|
if (opts.dryRun) {
|
|
1047
1157
|
console.log("\n# Commands that would run:");
|
|
1048
|
-
for (const cmd of runner.dryRunLog)
|
|
1158
|
+
for (const cmd of runner.dryRunLog) {
|
|
1159
|
+
const line = opts.sensitive ? cmd : maskSensitiveArgs(cmd);
|
|
1160
|
+
console.log(`dokku ${line}`);
|
|
1161
|
+
}
|
|
1049
1162
|
}
|
|
1050
1163
|
} finally {
|
|
1051
1164
|
await ctx.close();
|