@flisk-sh/flisk-import 0.4.0 → 0.4.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/flisk.js +49 -0
- package/dist/tiltfile.js +39 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ flisk import tiltfile dev/Tiltfile --yes # a named file, or a folder that holds
|
|
|
32
32
|
|
|
33
33
|
- **Path:** `[path]` defaults to `Tiltfile`; a folder means `<folder>/Tiltfile`. It is resolved from the current directory. The project root is the nearest folder with `.flisk/project.json`, else the current directory.
|
|
34
34
|
- **Writes:** only `.flisk/project.json`, with `"orchestration": { "source": "tiltfile", "path": "<path from the project root>" }`. Every other key is kept. Without a `project.json`, it creates the same minimal one as the Compose import, with only the proxy stack. It never edits the Tiltfile.
|
|
35
|
-
- **Refuses (exit 1):** a missing Tiltfile, a path inside `.flisk
|
|
35
|
+
- **Refuses (exit 1):** a missing Tiltfile, a path inside `.flisk/` (also through a symlink), a path with a quote or control character, a `project.json` that is not a JSON object or lacks the fields `flisk up` needs, and a `project.json` that already sets `orchestration` (pass `--force` to replace it).
|
|
36
36
|
- **Exit codes:** the same as the Compose import: `0` for `--dry-run` or a write, `1` for a refusal, `2` when the prompt is declined or closed (`Nothing written.`).
|
|
37
37
|
- **Needs** a Flisk core that runs an imported Tiltfile ([flisk#2849](https://github.com/flisk-sh/flisk/pull/2849)). The importer does not check the core version.
|
|
38
38
|
|
package/dist/flisk.js
CHANGED
|
@@ -70,3 +70,52 @@ export function defaultProjectConfig(projectRoot, preAlpha) {
|
|
|
70
70
|
overrides: {},
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
+
const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
74
|
+
const PHASES = ["pre_alpha", "alpha", "beta", "out_of_scope"];
|
|
75
|
+
const OPTIONAL_INFRA = ["monitoring", "elk", "debezium", "golden_image", "verdaccio"];
|
|
76
|
+
/**
|
|
77
|
+
* The fields of an existing `.flisk/project.json` that Flisk core would refuse, missing or of the wrong type, as dotted
|
|
78
|
+
* names such as `project.name`. A whole object that is absent is named once (`optional_infra`), not field by field.
|
|
79
|
+
* Empty when `flisk up` can read the file. A copy of core's `normalizeProjectConfig` (flisk
|
|
80
|
+
* `cli/src/generator/template-engine.ts`), so it can drift from flisk.
|
|
81
|
+
*/
|
|
82
|
+
export function missingProjectFields(config) {
|
|
83
|
+
const missing = [];
|
|
84
|
+
if (typeof config.version !== "string")
|
|
85
|
+
missing.push("version");
|
|
86
|
+
if (!isObject(config.project))
|
|
87
|
+
missing.push("project");
|
|
88
|
+
else
|
|
89
|
+
for (const key of ["name", "version"])
|
|
90
|
+
if (typeof config.project[key] !== "string")
|
|
91
|
+
missing.push(`project.${key}`);
|
|
92
|
+
// Core also reads the older `stacks` shape, with `services` in place of `enabledStacks`.
|
|
93
|
+
const phaseGaps = (value, label, field) => {
|
|
94
|
+
if (!isObject(value))
|
|
95
|
+
return [label];
|
|
96
|
+
return PHASES.filter((phase) => {
|
|
97
|
+
const entry = value[phase];
|
|
98
|
+
return !isObject(entry) || !Array.isArray(entry[field]);
|
|
99
|
+
}).map((phase) => `${label}.${phase}.${field}`);
|
|
100
|
+
};
|
|
101
|
+
const phases = phaseGaps(config.phases, "phases", "enabledStacks");
|
|
102
|
+
if (phases.length && phaseGaps(config.stacks, "stacks", "services").length)
|
|
103
|
+
missing.push(...phases);
|
|
104
|
+
if (!isObject(config.optional_infra))
|
|
105
|
+
missing.push("optional_infra");
|
|
106
|
+
else {
|
|
107
|
+
const infra = config.optional_infra;
|
|
108
|
+
for (const key of OPTIONAL_INFRA)
|
|
109
|
+
if (typeof infra[key] !== "boolean")
|
|
110
|
+
missing.push(`optional_infra.${key}`);
|
|
111
|
+
}
|
|
112
|
+
if (!isObject(config.discovery))
|
|
113
|
+
missing.push("discovery");
|
|
114
|
+
else if (!Array.isArray(config.discovery.paths))
|
|
115
|
+
missing.push("discovery.paths");
|
|
116
|
+
if (config.capabilities !== undefined &&
|
|
117
|
+
!(isObject(config.capabilities) &&
|
|
118
|
+
Object.values(config.capabilities).every((flag) => typeof flag === "boolean")))
|
|
119
|
+
missing.push("capabilities");
|
|
120
|
+
return missing;
|
|
121
|
+
}
|
package/dist/tiltfile.js
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { ImportError } from "./errors.js";
|
|
4
|
-
import { defaultProjectConfig, findProjectRoot } from "./flisk.js";
|
|
4
|
+
import { defaultProjectConfig, findProjectRoot, missingProjectFields } from "./flisk.js";
|
|
5
5
|
const RERUN = "flisk import tiltfile <path>";
|
|
6
|
+
const RECREATE = "Or recreate it with `flisk project --yes --force` (this resets .flisk/project.json), then run the import again";
|
|
7
|
+
/** The refusal for a Tiltfile inside `.flisk/`; `resolved` names where a symlink leads. */
|
|
8
|
+
function insideFliskError(path, resolved) {
|
|
9
|
+
const via = resolved ? ` (it resolves to ${resolved})` : "";
|
|
10
|
+
return new ImportError(`${path} is inside .flisk/${via}, where Flisk writes its generated files; import your own Tiltfile instead`, [`Point it at the Tiltfile you maintain: ${RERUN}`]);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Where `tiltfile` really is, relative to the real `<projectRoot>/.flisk`, when a symlink on the way leads into it.
|
|
14
|
+
* The lexical check misses `infra/Tiltfile -> ../.flisk/.flisk-out/Tiltfile`, and flisk up would then include its own
|
|
15
|
+
* generated Tiltfile. Undefined when it does not, or when there is no `.flisk/` yet.
|
|
16
|
+
*/
|
|
17
|
+
function linkedIntoFlisk(tiltfile, projectRoot) {
|
|
18
|
+
const fliskDir = join(projectRoot, ".flisk");
|
|
19
|
+
if (!existsSync(fliskDir))
|
|
20
|
+
return undefined;
|
|
21
|
+
const realFlisk = realpathSync(fliskDir);
|
|
22
|
+
const inside = relative(realFlisk, realpathSync(tiltfile));
|
|
23
|
+
if (inside === ".." || inside.startsWith(`..${sep}`) || isAbsolute(inside))
|
|
24
|
+
return undefined;
|
|
25
|
+
return [".flisk", ...inside.split(sep)].join("/");
|
|
26
|
+
}
|
|
6
27
|
/** Describes an `orchestration` value for a message, for example `tiltfile: Tiltfile`. */
|
|
7
28
|
export function describeOrchestration(value) {
|
|
8
29
|
if (value && typeof value === "object" && "source" in value) {
|
|
@@ -30,14 +51,16 @@ export function planTiltfileImport(pathArg = "Tiltfile", options = {}) {
|
|
|
30
51
|
if (hasUnsafeChar(path)) {
|
|
31
52
|
throw new ImportError(`Tiltfile path ${JSON.stringify(path)} has a quote or control character`, ["Rename the file or folder, then run: flisk import tiltfile <path>"]);
|
|
32
53
|
}
|
|
33
|
-
if (path === ".flisk" || path.startsWith(".flisk/"))
|
|
34
|
-
throw
|
|
35
|
-
}
|
|
54
|
+
if (path === ".flisk" || path.startsWith(".flisk/"))
|
|
55
|
+
throw insideFliskError(path);
|
|
36
56
|
if (!statSync(tiltfile, { throwIfNoEntry: false })?.isFile()) {
|
|
37
57
|
throw new ImportError(`No Tiltfile found at ${pathArg}`, [
|
|
38
58
|
`Pass the path of an existing Tiltfile, or a folder that holds one: ${RERUN}`,
|
|
39
59
|
]);
|
|
40
60
|
}
|
|
61
|
+
const resolved = linkedIntoFlisk(tiltfile, projectRoot);
|
|
62
|
+
if (resolved)
|
|
63
|
+
throw insideFliskError(path, resolved);
|
|
41
64
|
const projectJson = join(projectRoot, ".flisk", "project.json");
|
|
42
65
|
const orchestration = { source: "tiltfile", path };
|
|
43
66
|
if (!existsSync(projectJson)) {
|
|
@@ -62,14 +85,23 @@ export function planTiltfileImport(pathArg = "Tiltfile", options = {}) {
|
|
|
62
85
|
existing = JSON.parse(readFileSync(projectJson, "utf8"));
|
|
63
86
|
}
|
|
64
87
|
catch (err) {
|
|
65
|
-
throw new ImportError(`Cannot read ${projectJson}: ${err instanceof Error ? err.message : String(err)}`, ["Fix the JSON in .flisk/project.json, then run the import again"]);
|
|
88
|
+
throw new ImportError(`Cannot read ${projectJson}: ${err instanceof Error ? err.message : String(err)}`, ["Fix the JSON in .flisk/project.json, then run the import again", RECREATE]);
|
|
66
89
|
}
|
|
67
90
|
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
68
91
|
throw new ImportError(`${projectJson} is not a JSON object`, [
|
|
69
92
|
"Fix .flisk/project.json, then run the import again",
|
|
93
|
+
RECREATE,
|
|
70
94
|
]);
|
|
71
95
|
}
|
|
72
96
|
const config = existing;
|
|
97
|
+
// Writing orchestration into a file flisk up cannot read would report success and then fail at flisk up.
|
|
98
|
+
const missing = missingProjectFields(config);
|
|
99
|
+
if (missing.length) {
|
|
100
|
+
throw new ImportError(`.flisk/project.json is missing fields flisk up needs, or has them with the wrong type: ${missing.join(", ")}`, [
|
|
101
|
+
`Add ${missing.length === 1 ? "it" : "them"} to .flisk/project.json, then run the import again`,
|
|
102
|
+
RECREATE,
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
73
105
|
const replaced = config.orchestration;
|
|
74
106
|
if (replaced !== undefined && !options.force) {
|
|
75
107
|
throw new ImportError(`.flisk/project.json already sets orchestration (${describeOrchestration(replaced)})`, ["Pass --force to replace it; every other key is kept"]);
|
package/package.json
CHANGED