dokku-compose 0.10.0 → 0.11.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/README.md CHANGED
@@ -111,6 +111,7 @@ All features are idempotent — running `up` twice produces no changes.
111
111
  | Feature | Description | Reference |
112
112
  |---------|-------------|-----------|
113
113
  | Apps | Create and destroy Dokku apps | [apps](docs/reference/apps.md) |
114
+ | Dokku Version | Warn when the server's Dokku version is older than the pinned floor | [dokku](docs/reference/dokku.md) |
114
115
  | Environment Variables | Set config vars per app or globally, with full convergence | [config](docs/reference/config.md) |
115
116
  | Build | Dockerfile path, build context, app.json, build args | [builder](docs/reference/builder.md) |
116
117
  | Docker Options | Custom Docker options per phase (build/deploy/run) | [docker_options](docs/reference/docker_options.md) |
package/dist/index.js CHANGED
@@ -154,6 +154,7 @@ function createRunner(opts = {}) {
154
154
  const controlPath = opts.host ? path.join(os.tmpdir(), `dc-${createHash("sha1").update(opts.host).digest("hex").slice(0, 16)}.sock`) : null;
155
155
  const sshControlFlags = controlPath ? ["-o", "ControlMaster=auto", "-o", `ControlPath=${controlPath}`, "-o", "ControlPersist=60"] : [];
156
156
  function shellQuote(arg) {
157
+ if (/^[A-Za-z0-9_\-:.,/=@]+$/.test(arg)) return arg;
157
158
  return `'${arg.replace(/'/g, "'\\''")}'`;
158
159
  }
159
160
  async function execDokku(args) {
@@ -274,6 +275,9 @@ function logDone() {
274
275
  function logSkip() {
275
276
  console.log(`... ${chalk.yellow("already configured")}`);
276
277
  }
278
+ function logWarn(context, message) {
279
+ console.warn(chalk.yellow(`[${context.padEnd(12)}] WARN: ${message}`));
280
+ }
277
281
 
278
282
  // src/core/reconcile.ts
279
283
  async function reconcile(resource, ctx, target, desired) {
@@ -392,18 +396,21 @@ var Domains = {
392
396
  for (const d of added) await ctx.run("domains:add", target, d);
393
397
  }
394
398
  };
399
+ function parseDeployMounts(raw) {
400
+ return raw.split(/\s+/).filter((s) => s && s !== "-v");
401
+ }
395
402
  var Storage = {
396
403
  key: "storage",
397
404
  read: async (ctx, target) => {
398
- const raw = await ctx.query("storage:report", target, "--storage-mounts");
399
- return splitLines(raw);
405
+ const raw = await ctx.query("storage:report", target, "--storage-deploy-mounts");
406
+ return parseDeployMounts(raw);
400
407
  },
401
408
  readAll: async (ctx) => {
402
409
  const raw = await ctx.query("storage:report");
403
410
  const bulk = parseBulkReport(raw, "storage");
404
411
  const result = /* @__PURE__ */ new Map();
405
412
  for (const [app, report] of bulk) {
406
- result.set(app, report["mounts"] ? splitLines(report["mounts"]) : []);
413
+ result.set(app, report["deploy-mounts"] ? parseDeployMounts(report["deploy-mounts"]) : []);
407
414
  }
408
415
  return result;
409
416
  },
@@ -814,7 +821,7 @@ async function ensurePlugins(ctx, plugins) {
814
821
  async function ensureDockerAuth(ctx, config) {
815
822
  for (const [server, creds] of Object.entries(config)) {
816
823
  logAction("docker-auth", `Logging in to ${server} as ${creds.username}`);
817
- await ctx.run("registry:login", server, creds.username, creds.password);
824
+ await ctx.run("registry:login", "--global", server, creds.username, creds.password);
818
825
  logDone();
819
826
  }
820
827
  }
@@ -1012,6 +1019,40 @@ async function exportRedis(ctx) {
1012
1019
  return services;
1013
1020
  }
1014
1021
 
1022
+ // src/modules/version.ts
1023
+ var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
1024
+ function parseSemver(input) {
1025
+ const m = input.match(SEMVER_RE);
1026
+ if (!m) throw new Error(`Cannot parse version: ${input}`);
1027
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
1028
+ }
1029
+ function extractServerVersion(output) {
1030
+ const match = output.match(/(\d+\.\d+\.\d+)/);
1031
+ return match ? match[1] : null;
1032
+ }
1033
+ function compareSemver(a, b) {
1034
+ const [aMaj, aMin, aPatch] = parseSemver(a);
1035
+ const [bMaj, bMin, bPatch] = parseSemver(b);
1036
+ if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;
1037
+ if (aMin !== bMin) return aMin < bMin ? -1 : 1;
1038
+ if (aPatch !== bPatch) return aPatch < bPatch ? -1 : 1;
1039
+ return 0;
1040
+ }
1041
+ async function ensureDokkuVersion(ctx, pinned) {
1042
+ if (!pinned) return;
1043
+ const output = await ctx.query("version");
1044
+ const server = extractServerVersion(output);
1045
+ if (!server) {
1046
+ throw new Error(`Cannot parse Dokku server version from output: ${output}`);
1047
+ }
1048
+ if (compareSemver(server, pinned) === -1) {
1049
+ logWarn(
1050
+ "dokku",
1051
+ `server is v${server} but dokku-compose.yml pins >= v${pinned}. Some features may be unavailable.`
1052
+ );
1053
+ }
1054
+ }
1055
+
1015
1056
  // src/modules/links.ts
1016
1057
  function resolveServicePlugin(name, config) {
1017
1058
  if (config.postgres?.[name]) return { plugin: "postgres", config: config.postgres[name] };
@@ -1094,6 +1135,7 @@ async function ensureGlobalNginx(ctx, nginx) {
1094
1135
  // src/commands/up.ts
1095
1136
  async function runUp(ctx, config, appFilter) {
1096
1137
  const apps = appFilter.length > 0 ? appFilter : Object.keys(config.apps);
1138
+ await ensureDokkuVersion(ctx, config.dokku?.version);
1097
1139
  if (config.plugins) await ensurePlugins(ctx, config.plugins);
1098
1140
  if (config.docker_auth) await ensureDockerAuth(ctx, config.docker_auth);
1099
1141
  if (config.domains !== void 0) await ensureGlobalDomains(ctx, config.domains);
@@ -1207,9 +1249,8 @@ var ALL_APP_RESOURCES = [
1207
1249
  // src/commands/export.ts
1208
1250
  async function runExport(ctx, opts) {
1209
1251
  const config = { apps: {} };
1210
- const versionOutput = await ctx.query("version");
1211
- const versionMatch = versionOutput.match(/(\d+\.\d+\.\d+)/);
1212
- if (versionMatch) config.dokku = { version: versionMatch[1] };
1252
+ const version2 = extractServerVersion(await ctx.query("version"));
1253
+ if (version2) config.dokku = { version: version2 };
1213
1254
  const apps = opts.appFilter?.length ? opts.appFilter : await exportApps(ctx);
1214
1255
  const networks = await exportNetworks(ctx);
1215
1256
  if (networks.length > 0) config.networks = networks;
@@ -1259,7 +1300,7 @@ function maskSensitiveArgs(cmd) {
1259
1300
  return `${key}=${maskValue(value)}`;
1260
1301
  });
1261
1302
  masked = masked.replace(
1262
- /^(registry:login\s+\S+\s+\S+\s+)(\S+)/,
1303
+ /^(registry:login\s+(?:--global\s+)?\S+\s+\S+\s+)(\S+)/,
1263
1304
  (_, prefix, password) => `${prefix}${maskValue(password)}`
1264
1305
  );
1265
1306
  return masked;
@@ -1288,6 +1329,7 @@ function maskSensitiveData(data) {
1288
1329
  import chalk2 from "chalk";
1289
1330
  async function computeDiff(ctx, config) {
1290
1331
  const result = { apps: {}, services: {}, inSync: true };
1332
+ await ensureDokkuVersion(ctx, config.dokku?.version);
1291
1333
  const prefetched = /* @__PURE__ */ new Map();
1292
1334
  await Promise.all(
1293
1335
  ALL_APP_RESOURCES.filter((r) => !r.forceApply && !r.key.startsWith("_") && r.readAll).map(async (r) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dokku-compose",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Docker Compose for Dokku — declare your entire server in a single YAML file.",
5
5
  "main": "dist/index.js",
6
6
  "exports": "./dist/index.js",