@multiplatform.one/cli 7.34.0 → 7.34.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/lib/bin/multiplatformOne.mjs +5 -9
- package/lib/commands/ruff.mjs +52 -0
- package/lib/source.json +2 -2
- package/package.json +2 -2
- package/src/bin/multiplatformOne.ts +5 -9
- package/src/commands/ruff.spec.ts +46 -0
- package/src/commands/ruff.ts +74 -0
- package/types/bin/multiplatformOne.d.ts.map +1 -1
- package/types/commands/ruff.d.ts +15 -0
- package/types/commands/ruff.d.ts.map +1 -0
|
@@ -4,6 +4,7 @@ import { adoptApp } from "../commands/adoptApp.mjs";
|
|
|
4
4
|
import { generateDoctypeTypes, typesDrift } from "../commands/doctypeTypes.mjs";
|
|
5
5
|
import { discoverE2EApp, runE2ESession } from "../commands/e2e.mjs";
|
|
6
6
|
import { SINGLETON_APPS, init } from "../commands/init.mjs";
|
|
7
|
+
import { runRuff } from "../commands/ruff.mjs";
|
|
7
8
|
import { updateApp } from "../commands/updateApp.mjs";
|
|
8
9
|
import fsSync from "node:fs";
|
|
9
10
|
import path from "node:path";
|
|
@@ -466,15 +467,10 @@ async function ruffStep(mode) {
|
|
|
466
467
|
const frappeApps = path.join(projectRoot, "apps", "frappe", "apps");
|
|
467
468
|
if (!fsSync.existsSync(frappeApps)) return false;
|
|
468
469
|
console.log(`\n── ${mode === "format" ? "ruff format" : "ruff check"} (Python) ──`);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
});
|
|
474
|
-
return false;
|
|
475
|
-
} catch {
|
|
476
|
-
return true;
|
|
477
|
-
}
|
|
470
|
+
const reason = await runRuff(mode, path.relative(projectRoot, frappeApps), projectRoot);
|
|
471
|
+
if (reason === void 0) return false;
|
|
472
|
+
console.error(reason);
|
|
473
|
+
return true;
|
|
478
474
|
}
|
|
479
475
|
program.command("format").description("format all supported files (TS, Python, etc.) via turbo").action(async () => {
|
|
480
476
|
await spawn("turbo", [
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import spawn from "nano-spawn";
|
|
2
|
+
|
|
3
|
+
//#region src/commands/ruff.ts
|
|
4
|
+
const pinHint = "ruff is pinned in .tool-versions; `make configure` installs it through mise or asdf";
|
|
5
|
+
function errorDetail(error) {
|
|
6
|
+
const e = error ?? {};
|
|
7
|
+
return {
|
|
8
|
+
code: e.cause?.code,
|
|
9
|
+
exitCode: e.exitCode,
|
|
10
|
+
signalName: e.signalName,
|
|
11
|
+
stderr: e.stderr?.trim() || void 0,
|
|
12
|
+
message: e.cause?.message ?? e.message ?? String(error)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Why `ruff --version` did not answer, as one line for the lint output. */
|
|
16
|
+
function ruffStartFailure(error) {
|
|
17
|
+
const d = errorDetail(error);
|
|
18
|
+
if (d.code === "ENOENT") return `ruff is not on PATH. ${pinHint}.`;
|
|
19
|
+
return `ruff did not start: ${d.stderr ?? d.message}. ${pinHint}.`;
|
|
20
|
+
}
|
|
21
|
+
/** Why `ruff <mode>` failed once it had started, as one line for the lint output. */
|
|
22
|
+
function ruffRunFailure(mode, target, error) {
|
|
23
|
+
const d = errorDetail(error);
|
|
24
|
+
if (d.signalName) return `ruff ${mode} was killed by ${d.signalName}.`;
|
|
25
|
+
if (d.exitCode === 1 && mode === "check") return `ruff check found problems in ${target} (listed above).`;
|
|
26
|
+
if (d.exitCode !== void 0) return `ruff ${mode} exited ${d.exitCode}, a configuration or usage error (see above).`;
|
|
27
|
+
return `ruff ${mode} failed: ${d.message}.`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run ruff format or check on `target`, first proving ruff starts so a missing
|
|
31
|
+
* or broken install is reported as that instead of as a lint failure. Returns
|
|
32
|
+
* the reason it failed, or undefined when it passed.
|
|
33
|
+
*/
|
|
34
|
+
async function runRuff(mode, target, cwd, spawn$1 = spawn) {
|
|
35
|
+
try {
|
|
36
|
+
await spawn$1("ruff", ["--version"], { cwd });
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return ruffStartFailure(error);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await spawn$1("ruff", [mode, target], {
|
|
42
|
+
stdio: "inherit",
|
|
43
|
+
cwd
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return ruffRunFailure(mode, target, error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
export { ruffRunFailure, ruffStartFailure, runRuff };
|
package/lib/source.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/cli",
|
|
3
|
-
"version": "7.34.
|
|
3
|
+
"version": "7.34.1",
|
|
4
4
|
"description": "multiplatform.one cli — mpo init clones and prunes the monorepo, mpo update merges it back",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"create-multiplatform-app",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@multiplatform.one/utils": "7.34.
|
|
59
|
+
"@multiplatform.one/utils": "7.34.1",
|
|
60
60
|
"commander": "^14.0.3",
|
|
61
61
|
"dotenv": "^17.4.2",
|
|
62
62
|
"inquirer": "^13.4.2",
|
|
@@ -22,6 +22,7 @@ import { generateDoctypeTypes, typesDrift } from "../commands/doctypeTypes";
|
|
|
22
22
|
import { discoverE2EApp, runE2ESession } from "../commands/e2e";
|
|
23
23
|
import { SINGLETON_APPS, init } from "../commands/init";
|
|
24
24
|
import { INIT_APP_PIECES } from "../commands/pieces";
|
|
25
|
+
import { runRuff } from "../commands/ruff";
|
|
25
26
|
import { updateApp } from "../commands/updateApp";
|
|
26
27
|
|
|
27
28
|
const projectRoot = lookupProjectRoot();
|
|
@@ -715,15 +716,10 @@ async function ruffStep(mode: "format" | "check"): Promise<boolean> {
|
|
|
715
716
|
if (!fsSync.existsSync(frappeApps)) return false;
|
|
716
717
|
const label = mode === "format" ? "ruff format" : "ruff check";
|
|
717
718
|
console.log(`\n── ${label} (Python) ──`);
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
});
|
|
723
|
-
return false;
|
|
724
|
-
} catch {
|
|
725
|
-
return true;
|
|
726
|
-
}
|
|
719
|
+
const reason = await runRuff(mode, path.relative(projectRoot, frappeApps), projectRoot);
|
|
720
|
+
if (reason === undefined) return false;
|
|
721
|
+
console.error(reason);
|
|
722
|
+
return true;
|
|
727
723
|
}
|
|
728
724
|
|
|
729
725
|
program
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import nanoSpawn from "nano-spawn";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { runRuff } from "./ruff";
|
|
4
|
+
|
|
5
|
+
type Spawn = typeof nanoSpawn;
|
|
6
|
+
|
|
7
|
+
// Runs a real subprocess in place of ruff, so the errors are nano-spawn's own.
|
|
8
|
+
function standIn(script: (args: readonly string[]) => string): Spawn {
|
|
9
|
+
return ((_file: string, args: readonly string[] = [], options = {}) =>
|
|
10
|
+
nanoSpawn("sh", ["-c", script(args)], options)) as Spawn;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("runRuff (MPO-497)", () => {
|
|
14
|
+
it("names a missing ruff and the pin that provides it", async () => {
|
|
15
|
+
const missing = ((_file: string, args: readonly string[] = [], options = {}) =>
|
|
16
|
+
nanoSpawn("mpo-497-no-such-ruff", [...args], options)) as Spawn;
|
|
17
|
+
const reason = await runRuff("check", "apps/frappe/apps", process.cwd(), missing);
|
|
18
|
+
expect(reason).toMatch(/^ruff is not on PATH\./);
|
|
19
|
+
expect(reason).toContain(".tool-versions");
|
|
20
|
+
expect(reason).toContain("make configure");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("reports a ruff that will not start with what it printed", async () => {
|
|
24
|
+
const shim = standIn(() => "echo 'mise ERROR No version is set for shim: ruff' >&2; exit 1");
|
|
25
|
+
const reason = await runRuff("check", "apps/frappe/apps", process.cwd(), shim);
|
|
26
|
+
expect(reason).toBe(
|
|
27
|
+
"ruff did not start: mise ERROR No version is set for shim: ruff. ruff is pinned in .tool-versions; `make configure` installs it through mise or asdf.",
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("separates findings from a configuration error", async () => {
|
|
32
|
+
const findings = standIn((args) => (args[0] === "--version" ? "true" : "exit 1"));
|
|
33
|
+
expect(await runRuff("check", "apps/frappe/apps", process.cwd(), findings)).toBe(
|
|
34
|
+
"ruff check found problems in apps/frappe/apps (listed above).",
|
|
35
|
+
);
|
|
36
|
+
const broken = standIn((args) => (args[0] === "--version" ? "true" : "exit 2"));
|
|
37
|
+
expect(await runRuff("check", "apps/frappe/apps", process.cwd(), broken)).toBe(
|
|
38
|
+
"ruff check exited 2, a configuration or usage error (see above).",
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("passes when ruff starts and exits 0", async () => {
|
|
43
|
+
const clean = standIn(() => "true");
|
|
44
|
+
expect(await runRuff("check", "apps/frappe/apps", process.cwd(), clean)).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import nanoSpawn from "nano-spawn";
|
|
2
|
+
|
|
3
|
+
export type RuffMode = "format" | "check";
|
|
4
|
+
|
|
5
|
+
type Spawn = typeof nanoSpawn;
|
|
6
|
+
|
|
7
|
+
const pinHint =
|
|
8
|
+
"ruff is pinned in .tool-versions; `make configure` installs it through mise or asdf";
|
|
9
|
+
|
|
10
|
+
function errorDetail(error: unknown): {
|
|
11
|
+
code?: string;
|
|
12
|
+
exitCode?: number;
|
|
13
|
+
signalName?: string;
|
|
14
|
+
stderr?: string;
|
|
15
|
+
message: string;
|
|
16
|
+
} {
|
|
17
|
+
const e = (error ?? {}) as {
|
|
18
|
+
cause?: { code?: string; message?: string };
|
|
19
|
+
exitCode?: number;
|
|
20
|
+
signalName?: string;
|
|
21
|
+
stderr?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
code: e.cause?.code,
|
|
26
|
+
exitCode: e.exitCode,
|
|
27
|
+
signalName: e.signalName,
|
|
28
|
+
stderr: e.stderr?.trim() || undefined,
|
|
29
|
+
message: e.cause?.message ?? e.message ?? String(error),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Why `ruff --version` did not answer, as one line for the lint output. */
|
|
34
|
+
export function ruffStartFailure(error: unknown): string {
|
|
35
|
+
const d = errorDetail(error);
|
|
36
|
+
if (d.code === "ENOENT") return `ruff is not on PATH. ${pinHint}.`;
|
|
37
|
+
const why = d.stderr ?? d.message;
|
|
38
|
+
return `ruff did not start: ${why}. ${pinHint}.`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Why `ruff <mode>` failed once it had started, as one line for the lint output. */
|
|
42
|
+
export function ruffRunFailure(mode: RuffMode, target: string, error: unknown): string {
|
|
43
|
+
const d = errorDetail(error);
|
|
44
|
+
if (d.signalName) return `ruff ${mode} was killed by ${d.signalName}.`;
|
|
45
|
+
if (d.exitCode === 1 && mode === "check")
|
|
46
|
+
return `ruff check found problems in ${target} (listed above).`;
|
|
47
|
+
if (d.exitCode !== undefined)
|
|
48
|
+
return `ruff ${mode} exited ${d.exitCode}, a configuration or usage error (see above).`;
|
|
49
|
+
return `ruff ${mode} failed: ${d.message}.`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Run ruff format or check on `target`, first proving ruff starts so a missing
|
|
54
|
+
* or broken install is reported as that instead of as a lint failure. Returns
|
|
55
|
+
* the reason it failed, or undefined when it passed.
|
|
56
|
+
*/
|
|
57
|
+
export async function runRuff(
|
|
58
|
+
mode: RuffMode,
|
|
59
|
+
target: string,
|
|
60
|
+
cwd: string,
|
|
61
|
+
spawn: Spawn = nanoSpawn,
|
|
62
|
+
): Promise<string | undefined> {
|
|
63
|
+
try {
|
|
64
|
+
await spawn("ruff", ["--version"], { cwd });
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return ruffStartFailure(error);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
await spawn("ruff", [mode, target], { stdio: "inherit", cwd });
|
|
70
|
+
return undefined;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return ruffRunFailure(mode, target, error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multiplatformOne.d.ts","sourceRoot":"","sources":["../../src/bin/multiplatformOne.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"multiplatformOne.d.ts","sourceRoot":"","sources":["../../src/bin/multiplatformOne.ts"],"names":[],"mappings":"AA8FA,gHAAgH;AAChH,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import nanoSpawn from "nano-spawn";
|
|
2
|
+
export type RuffMode = "format" | "check";
|
|
3
|
+
type Spawn = typeof nanoSpawn;
|
|
4
|
+
/** Why `ruff --version` did not answer, as one line for the lint output. */
|
|
5
|
+
export declare function ruffStartFailure(error: unknown): string;
|
|
6
|
+
/** Why `ruff <mode>` failed once it had started, as one line for the lint output. */
|
|
7
|
+
export declare function ruffRunFailure(mode: RuffMode, target: string, error: unknown): string;
|
|
8
|
+
/**
|
|
9
|
+
* Run ruff format or check on `target`, first proving ruff starts so a missing
|
|
10
|
+
* or broken install is reported as that instead of as a lint failure. Returns
|
|
11
|
+
* the reason it failed, or undefined when it passed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runRuff(mode: RuffMode, target: string, cwd: string, spawn?: Spawn): Promise<string | undefined>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=ruff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ruff.d.ts","sourceRoot":"","sources":["../../src/commands/ruff.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1C,KAAK,KAAK,GAAG,OAAO,SAAS,CAAC;AA4B9B,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKvD;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAQrF;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,KAAiB,GACvB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAY7B"}
|