flisk-import 0.17.0 → 0.17.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/README.md +1 -1
- package/dist/detectors/compose.js +19 -11
- package/dist/write.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ flisk import . --yes # write service.json files and project setup
|
|
|
17
17
|
- **Keeps (Flisk 1.4.28):** each `depends_on` condition as a `{ name, condition }` entry (the list form as `service_started`), `build.args` as `buildArgs` (and, for a generated Dockerfile on an image with variables, each variable as a build argument, so `ARG POSTGRES_VERSION` gets the project's value), `logging` (options as strings), bind paths from the environment (`${OM_AGENTS_DIR:-./agents}`, the default rewritten relative to `service.json`), SELinux labels (`:z`, `:Z`, `bind.selinux`) as `selinux`, and images with `${NAME:?message}`. A bind path from a variable without a default (`${STORAGE_DIR}/redis`) and `extra_hosts` with a variable are still not carried over.
|
|
18
18
|
- **Keeps:** `stop_grace_period` (`container.stopGracePeriod`, such as `30s`), the `aliases` of every network (`container.networkAliases`), `extra_hosts` in list or map form (`container.extraHosts`, such as `host.docker.internal:host-gateway`) and `ulimits` (`container.ulimits`). A service renamed to kebab-case (`lychee_api` becomes `lychee-api`) keeps its Compose name as a network alias, so `http://lychee_api:8000` in another service still resolves. `container_name` is dropped with a note: Flisk names containers itself. These fields, an image with variables and `on-failure:N` need the Flisk core release that reads them; an older core does not apply them.
|
|
19
19
|
- **Keeps:** the service names (`db` stays `db`, so it stays the hostname), the build context and Dockerfile (or the image), the container port as written (3000, 80, 5432), `depends_on`, and an HTTP health path. Databases and brokers are private, with no proxy route.
|
|
20
|
-
- **Runtime keys:** named volumes and bind mounts inside the project (`volumes`), `cap_add`, `cap_drop`, `init`, `read_only`, `user`, `security_opt` and `shm_size` (`container`), extra published ports (`extraPorts`, on the same host address as in Compose: `"80:80"` stays on every interface, `"127.0.0.1:5432:5432"` on loopback, and another address or a `${VAR:-default}` or `${VAR-default}` host side as `publish`), `network_mode: service:<name>` (`sidecarOf`) and `restart`.
|
|
20
|
+
- **Runtime keys:** named volumes and bind mounts inside the project (`volumes`), `cap_add`, `cap_drop`, `init`, `read_only`, `user`, `security_opt` and `shm_size` (`container`), extra published ports (`extraPorts`, on the same host address as in Compose: `"80:80"` stays on every interface (written as `publish: "80"`, so IPv6 is kept; `"0.0.0.0:80:80"` is `bind`), `"127.0.0.1:5432:5432"` on loopback, and another address or a `${VAR:-default}` or `${VAR-default}` host side as `publish`), `network_mode: service:<name>` (`sidecarOf`) and `restart`.
|
|
21
21
|
- **Environment:** the committed `service.json` `env` gets every pure Compose expression as written, whatever its name (`${DB_PASSWORD:-password}`, `${PORT-8080}`, `${A:-${B:-file}}`), so a fresh clone keeps the defaults, and every literal (`sqlite`, `worker`, `lychee_db`, `http://invidious:3000`) unless its name looks like a credential (`PASSWORD`, `TOKEN`, `KEY`, ...) or the value holds one (a URL with a user or password, a token query parameter, a long random string, a PEM block). Those go into `secrets.required`, with its literal value copied to the project `.env`, which is added to `.gitignore`. Values in `.env` already are kept.
|
|
22
22
|
- **Round trip:** a routed port that Compose also publishes (`"${APP_PORT:-8000}:8000"`) stays in `extraPorts` too, and a `healthcheck` stays as `healthCheck.command` (with `interval`, `timeout`, `retries` and `start_period` as `startPeriod`) even when an HTTP path was read, so `flisk-export compose` writes a file that `docker compose` starts the same way (LycheeOrg/Lychee round-trips with no difference but names and aliases). A service with no `restart` keeps none, so Flisk runs it with its default `unless-stopped` (unlike Compose, Flisk then restarts it after a crash or a reboot; the plan says so). `restart: "no"` is written only when Compose sets it or another service waits for this one with `condition: service_completed_successfully`, because Flisk runs a `restart: "no"` service as a one-shot job and waits for it to exit, `healthcheck: { disable: true }` or `test: ["NONE"]` becomes `healthCheck: false`, a value that mixes `${VAR}` with literal text (`${SERVER_NAME:-localhost}, php:80`) stays in `env` unless it could hold a credential, `*_FILE` paths (`POSTGRES_PASSWORD_FILE=/run/secrets/pg`) stay in `env`, and the Compose project name (`name:` or the folder's, such as `pl_mail`) is recorded as `composeProjectNames.<stack>` in `.flisk/project.json`, so `flisk-export compose` keeps the container names. A generated Dockerfile for an image with variables declares them as `ARG NAME=default` before `FROM`.
|
|
23
23
|
- **A tracked `.env`:** a repo that commits its `.env` (Symfony) keeps it as it is, and it is not added to `.gitignore`; values that must stay local go to `.env.local` when git already ignores it, else to `.flisk/.env.import` (added to `.gitignore`), and each service that needs them reads that file as `envFile`.
|
|
@@ -265,10 +265,19 @@ function hostSideOf(port, hostIp, hostPort) {
|
|
|
265
265
|
const publish = hostIp ? `${hostIp}:${hostPort}` : hostPort;
|
|
266
266
|
return PUBLISH.test(publish) ? { publish } : undefined;
|
|
267
267
|
}
|
|
268
|
-
if (hostIp === undefined || hostIp === ""
|
|
268
|
+
if (hostIp === undefined || hostIp === "") {
|
|
269
269
|
// A bare container port ("3000") gets a random host port from Compose; Flisk keeps it on 127.0.0.1.
|
|
270
|
-
|
|
270
|
+
if (hostPort === undefined)
|
|
271
|
+
return {};
|
|
272
|
+
// "3000:3000" names no host address: Compose listens on every interface, IPv6 too. `publish` writes the same bare mapping
|
|
273
|
+
// ("3000:3000/tcp"); `bind: "0.0.0.0"` would add the IPv4 address and drop IPv6 (flisk#2996).
|
|
274
|
+
return hostNumber !== undefined && hostNumber >= 1 && hostNumber <= 65535
|
|
275
|
+
? { publish: String(hostNumber) }
|
|
276
|
+
: undefined;
|
|
271
277
|
}
|
|
278
|
+
// The file names 0.0.0.0 on purpose, so that is what is kept.
|
|
279
|
+
if (hostIp === "0.0.0.0")
|
|
280
|
+
return { ...keepHostPort, bind: "0.0.0.0" };
|
|
272
281
|
if (hostIp === "127.0.0.1")
|
|
273
282
|
return keepHostPort;
|
|
274
283
|
const publish = `${hostIp}:${hostNumber ?? port}`;
|
|
@@ -864,22 +873,21 @@ function runtimeLimitsOf(raw) {
|
|
|
864
873
|
}
|
|
865
874
|
return { ...out, dropped };
|
|
866
875
|
}
|
|
876
|
+
/** A port in a health URL: digits, or a shell/Compose variable such as `$${PORT:-3000}` that a shell healthcheck uses. */
|
|
877
|
+
const URL_PORT = String.raw `(?:\d+|\$+\{[^}\s]*\}|\$+\w+)`;
|
|
878
|
+
const HEALTH_URL = new RegExp(String.raw `(?:https?:\/\/[^\s"'/]+|(?:^|(?<=[\s"']))(?:[a-z0-9.-]+:${URL_PORT}|(?:localhost|127\.0\.0\.1)(?::${URL_PORT})?))(\/[^\s"']*)`, "i");
|
|
879
|
+
/** The HTTP path a Compose healthcheck command probes (`wget -qO- http://localhost:$${PORT:-3000}/api/health`), if it names one. */
|
|
867
880
|
function healthPathOf(healthcheck) {
|
|
868
881
|
if (!isObj(healthcheck) || !Array.isArray(healthcheck.test))
|
|
869
882
|
return undefined;
|
|
870
883
|
const test = healthcheck.test
|
|
871
884
|
.filter((part) => typeof part === "string")
|
|
872
885
|
.join(" ");
|
|
873
|
-
const
|
|
874
|
-
if (
|
|
875
|
-
return undefined;
|
|
876
|
-
try {
|
|
877
|
-
const raw = match[0];
|
|
878
|
-
return new URL(/^https?:\/\//i.test(raw) ? raw : `http://${raw}`).pathname || "/";
|
|
879
|
-
}
|
|
880
|
-
catch {
|
|
886
|
+
const path = test.match(HEALTH_URL)?.[1];
|
|
887
|
+
if (path === undefined)
|
|
881
888
|
return undefined;
|
|
882
|
-
|
|
889
|
+
// The query and fragment are not part of the route Traefik checks. The port may be a variable, so no URL parsing here.
|
|
890
|
+
return path.split(/[?#]/)[0] || "/";
|
|
883
891
|
}
|
|
884
892
|
registerDetector({
|
|
885
893
|
id: "compose",
|
package/dist/write.js
CHANGED
|
@@ -333,11 +333,15 @@ export function planWrites(plan, force) {
|
|
|
333
333
|
const portText = (p) => `${p.publish ?? p.hostPort ?? p.port}->${p.port}/${p.protocol ?? "tcp"}`;
|
|
334
334
|
const loopback = extraPorts.filter((p) => !p.bind && !p.publish);
|
|
335
335
|
const everywhere = extraPorts.filter((p) => p.bind === "0.0.0.0");
|
|
336
|
-
|
|
336
|
+
// A bare mapping ("3000:3000") names no host address: Compose listens on every interface, IPv4 and IPv6 (flisk#2996).
|
|
337
|
+
const bare = extraPorts.filter((p) => p.publish && /^\d+$/.test(p.publish));
|
|
338
|
+
const fromCompose = extraPorts.filter((p) => p.publish && !/^\d+$/.test(p.publish));
|
|
337
339
|
if (loopback.length)
|
|
338
340
|
notes.push(`published on 127.0.0.1 only: ${loopback.map(portText).join(", ")}`);
|
|
339
341
|
if (everywhere.length)
|
|
340
342
|
notes.push(`published on every interface, as in Compose: ${everywhere.map(portText).join(", ")} (flisk doctor lists these)`);
|
|
343
|
+
if (bare.length)
|
|
344
|
+
notes.push(`published as Compose's bare mapping, on every interface (IPv4 and IPv6): ${bare.map(portText).join(", ")} (flisk doctor lists these)`);
|
|
341
345
|
if (fromCompose.length)
|
|
342
346
|
notes.push(`published as Compose wrote the host side: ${fromCompose.map(portText).join(", ")}`);
|
|
343
347
|
// Bind mounts are relative to the service directory in service.json, and must stay inside the project.
|