ic-mops 2.8.1 → 2.10.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/CHANGELOG.md +10 -0
- package/bundle/cli.tgz +0 -0
- package/cli.ts +21 -11
- package/commands/build.ts +134 -114
- package/commands/check-stable.ts +110 -17
- package/commands/check.ts +183 -102
- package/commands/info.ts +103 -0
- package/commands/lint.ts +132 -42
- package/commands/toolchain/moc.ts +5 -5
- package/dist/cli.js +19 -10
- package/dist/commands/build.js +105 -82
- package/dist/commands/check-stable.d.ts +7 -1
- package/dist/commands/check-stable.js +83 -19
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +127 -78
- package/dist/commands/info.d.ts +4 -0
- package/dist/commands/info.js +75 -0
- package/dist/commands/lint.js +84 -37
- package/dist/commands/toolchain/moc.js +5 -5
- package/dist/helpers/resolve-canisters.d.ts +3 -1
- package/dist/helpers/resolve-canisters.js +20 -5
- package/dist/package.json +3 -1
- package/dist/templates/mops-publish.yml +1 -1
- package/dist/templates/mops-test.yml +1 -1
- package/dist/tests/build.test.js +17 -0
- package/dist/tests/check-stable.test.js +18 -0
- package/dist/tests/check.test.js +23 -5
- package/dist/tests/lint.test.js +33 -0
- package/dist/types.d.ts +1 -0
- package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
- package/helpers/resolve-canisters.ts +36 -5
- package/package.json +3 -1
- package/templates/mops-publish.yml +1 -1
- package/templates/mops-test.yml +1 -1
- package/tests/__snapshots__/check.test.ts.snap +2 -2
- package/tests/__snapshots__/lint.test.ts.snap +163 -5
- package/tests/build.test.ts +17 -0
- package/tests/check/canisters-canister-args/Warning.mo +5 -0
- package/tests/check/canisters-canister-args/mops.toml +9 -0
- package/tests/check-stable/canister-args/migrations/20250101_000000_Init.mo +8 -0
- package/tests/check-stable/canister-args/migrations/20250201_000000_AddField.mo +9 -0
- package/tests/check-stable/canister-args/mops.toml +9 -0
- package/tests/check-stable/canister-args/old.most +8 -0
- package/tests/check-stable/canister-args/src/main.mo +11 -0
- package/tests/check-stable.test.ts +21 -0
- package/tests/check.test.ts +26 -5
- package/tests/lint-extra/mops.toml +5 -0
- package/tests/lint-extra/src/Ok.mo +5 -0
- package/tests/lint-extra/src/restricted/B.mo +8 -0
- package/tests/lint-extra/src/restricted/Restricted.mo +8 -0
- package/tests/lint-extra-edge-cases/mops.toml +8 -0
- package/tests/lint-extra-edge-cases/src/Clean.mo +5 -0
- package/tests/lint-extra-example-rules/lint/migration-only/migration-only.toml +9 -0
- package/tests/lint-extra-example-rules/lint/no-types/no-types.toml +5 -0
- package/tests/lint-extra-example-rules/lint/types-only/types-only.toml +6 -0
- package/tests/lint-extra-example-rules/mops.toml +7 -0
- package/tests/lint-extra-example-rules/src/Main.mo +10 -0
- package/tests/lint-extra-example-rules/src/Migration.mo +9 -0
- package/tests/lint-extra-example-rules/src/Types.mo +10 -0
- package/tests/lint-extra-with-base/mops.toml +8 -0
- package/tests/lint-extra-with-base/src/BadBase.mo +8 -0
- package/tests/lint-extra-with-base/src/Ok.mo +5 -0
- package/tests/lint-extra-with-base/src/Restricted.mo +5 -0
- package/tests/lint-extra-with-cli-rules/empty-rules/.gitkeep +0 -0
- package/tests/lint-extra-with-cli-rules/mops.toml +5 -0
- package/tests/lint-extra-with-cli-rules/rules-b/no-bool-switch-2.toml +9 -0
- package/tests/lint-extra-with-cli-rules/src/Ok.mo +5 -0
- package/tests/lint-extra-with-cli-rules/src/Restricted.mo +8 -0
- package/tests/lint.test.ts +42 -0
- package/types.ts +1 -0
- package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/wasm/pkg/web/wasm_bg.wasm +0 -0
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { checkStable } from "./commands/check-stable.js";
|
|
|
15
15
|
import { docsCoverage } from "./commands/docs-coverage.js";
|
|
16
16
|
import { docs } from "./commands/docs.js";
|
|
17
17
|
import { format } from "./commands/format.js";
|
|
18
|
+
import { info } from "./commands/info.js";
|
|
18
19
|
import { init } from "./commands/init.js";
|
|
19
20
|
import { lint } from "./commands/lint.js";
|
|
20
21
|
import { installAll } from "./commands/install/install-all.js";
|
|
@@ -213,6 +214,14 @@ program
|
|
|
213
214
|
.action(async (text) => {
|
|
214
215
|
await search(text);
|
|
215
216
|
});
|
|
217
|
+
// info
|
|
218
|
+
program
|
|
219
|
+
.command("info <pkg>")
|
|
220
|
+
.description("Show detailed information about a package from the registry")
|
|
221
|
+
.option("--versions", "List all published versions, one per line")
|
|
222
|
+
.action(async (pkg, options) => {
|
|
223
|
+
await info(pkg, options);
|
|
224
|
+
});
|
|
216
225
|
// cache
|
|
217
226
|
program
|
|
218
227
|
.command("cache")
|
|
@@ -254,20 +263,20 @@ program
|
|
|
254
263
|
});
|
|
255
264
|
// check
|
|
256
265
|
program
|
|
257
|
-
.command("check [
|
|
258
|
-
.description("Check Motoko files for syntax errors and type issues. If no
|
|
266
|
+
.command("check [args...]")
|
|
267
|
+
.description("Check Motoko canisters or files for syntax errors and type issues. Arguments can be canister names or file paths. If no arguments are given, checks all canisters from mops.toml. Also runs stable compatibility checks for canisters with [check-stable] configured, and runs linting if lintoko is configured in [toolchain]")
|
|
259
268
|
.option("--verbose", "Verbose console output")
|
|
260
269
|
.addOption(new Option("--fix", "Apply autofixes to all files, including transitively imported ones"))
|
|
261
270
|
.allowUnknownOption(true)
|
|
262
|
-
.action(async (
|
|
271
|
+
.action(async (args, options) => {
|
|
263
272
|
checkConfigFile(true);
|
|
264
|
-
const { extraArgs, args:
|
|
273
|
+
const { extraArgs, args: argList } = parseExtraArgs(args);
|
|
265
274
|
await installAll({
|
|
266
275
|
silent: true,
|
|
267
276
|
lock: "ignore",
|
|
268
277
|
installFromLockFile: true,
|
|
269
278
|
});
|
|
270
|
-
await check(
|
|
279
|
+
await check(argList, {
|
|
271
280
|
...options,
|
|
272
281
|
extraArgs,
|
|
273
282
|
});
|
|
@@ -287,19 +296,19 @@ program
|
|
|
287
296
|
});
|
|
288
297
|
// check-stable
|
|
289
298
|
program
|
|
290
|
-
.command("check-stable
|
|
291
|
-
.description("Check stable variable compatibility
|
|
299
|
+
.command("check-stable [args...]")
|
|
300
|
+
.description("Check stable variable compatibility. With no arguments, checks all canisters with [check-stable] configured. Arguments can be canister names or an old file path followed by an optional canister name")
|
|
292
301
|
.option("--verbose", "Verbose console output")
|
|
293
302
|
.allowUnknownOption(true)
|
|
294
|
-
.action(async (
|
|
303
|
+
.action(async (args, options) => {
|
|
295
304
|
checkConfigFile(true);
|
|
296
|
-
const { extraArgs } = parseExtraArgs();
|
|
305
|
+
const { extraArgs, args: argList } = parseExtraArgs(args);
|
|
297
306
|
await installAll({
|
|
298
307
|
silent: true,
|
|
299
308
|
lock: "ignore",
|
|
300
309
|
installFromLockFile: true,
|
|
301
310
|
});
|
|
302
|
-
await checkStable(
|
|
311
|
+
await checkStable(argList, {
|
|
303
312
|
...options,
|
|
304
313
|
extraArgs,
|
|
305
314
|
});
|
package/dist/commands/build.js
CHANGED
|
@@ -3,9 +3,10 @@ import { execa } from "execa";
|
|
|
3
3
|
import { exists } from "fs-extra";
|
|
4
4
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
+
import { lock, unlockSync } from "proper-lockfile";
|
|
6
7
|
import { cliError } from "../error.js";
|
|
7
8
|
import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
|
|
8
|
-
import { resolveCanisterConfigs } from "../helpers/resolve-canisters.js";
|
|
9
|
+
import { filterCanisters, resolveCanisterConfigs, validateCanisterArgs, } from "../helpers/resolve-canisters.js";
|
|
9
10
|
import { getWasmBindings } from "../wasm.js";
|
|
10
11
|
import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
|
|
11
12
|
import { sourcesArgs } from "./sources.js";
|
|
@@ -25,18 +26,10 @@ export async function build(canisterNames, options) {
|
|
|
25
26
|
if (!Object.keys(canisters).length) {
|
|
26
27
|
cliError(`No Motoko canisters found in mops.toml configuration`);
|
|
27
28
|
}
|
|
28
|
-
if (canisterNames) {
|
|
29
|
-
let invalidNames = canisterNames.filter((name) => !(name in canisters));
|
|
30
|
-
if (invalidNames.length) {
|
|
31
|
-
cliError(`Motoko canister(s) not found in mops.toml configuration: ${invalidNames.join(", ")}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
29
|
if (!(await exists(outputDir))) {
|
|
35
30
|
await mkdir(outputDir, { recursive: true });
|
|
36
31
|
}
|
|
37
|
-
const filteredCanisters = canisterNames
|
|
38
|
-
? Object.fromEntries(Object.entries(canisters).filter(([name]) => canisterNames.includes(name)))
|
|
39
|
-
: canisters;
|
|
32
|
+
const filteredCanisters = filterCanisters(canisters, canisterNames);
|
|
40
33
|
for (let [canisterName, canister] of Object.entries(filteredCanisters)) {
|
|
41
34
|
console.log(chalk.blue("build canister"), chalk.bold(canisterName));
|
|
42
35
|
let motokoPath = canister.main;
|
|
@@ -46,88 +39,120 @@ export async function build(canisterNames, options) {
|
|
|
46
39
|
motokoPath = resolveConfigPath(motokoPath);
|
|
47
40
|
const wasmPath = join(outputDir, `${canisterName}.wasm`);
|
|
48
41
|
const mostPath = join(outputDir, `${canisterName}.most`);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
|
|
62
|
-
if (isPublicCandid) {
|
|
63
|
-
args.push("--public-metadata", "candid:service");
|
|
64
|
-
args.push("--public-metadata", "candid:args");
|
|
42
|
+
// per-canister lock to prevent parallel builds of the same canister from clobbering output files
|
|
43
|
+
const lockTarget = join(outputDir, `.${canisterName}.buildlock`);
|
|
44
|
+
await writeFile(lockTarget, "", { flag: "a" });
|
|
45
|
+
let release;
|
|
46
|
+
try {
|
|
47
|
+
release = await lock(lockTarget, {
|
|
48
|
+
stale: 300_000,
|
|
49
|
+
retries: { retries: 60, minTimeout: 500, maxTimeout: 5_000 },
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
cliError(`Failed to acquire build lock for canister ${canisterName} — another build may be stuck`);
|
|
65
54
|
}
|
|
55
|
+
// proper-lockfile registers its own signal-exit handler, but it doesn't reliably
|
|
56
|
+
// fire on process.exit(). This manual handler covers that gap. Double-unlock is
|
|
57
|
+
// harmless (the second call throws and is caught).
|
|
58
|
+
const exitCleanup = () => {
|
|
59
|
+
try {
|
|
60
|
+
unlockSync(lockTarget);
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
};
|
|
64
|
+
process.on("exit", exitCleanup);
|
|
66
65
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
let args = [
|
|
67
|
+
"-c",
|
|
68
|
+
"--idl",
|
|
69
|
+
"--stable-types",
|
|
70
|
+
"-o",
|
|
71
|
+
wasmPath,
|
|
72
|
+
motokoPath,
|
|
73
|
+
...(await sourcesArgs()).flat(),
|
|
74
|
+
...getGlobalMocArgs(config),
|
|
75
|
+
];
|
|
76
|
+
args.push(...collectExtraArgs(config, canister, canisterName, options.extraArgs));
|
|
77
|
+
const isPublicCandid = true; // always true for now to reduce corner cases
|
|
78
|
+
const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
|
|
79
|
+
if (isPublicCandid) {
|
|
80
|
+
args.push("--public-metadata", "candid:service");
|
|
81
|
+
args.push("--public-metadata", "candid:args");
|
|
69
82
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
try {
|
|
84
|
+
if (options.verbose) {
|
|
85
|
+
console.log(chalk.gray(mocPath, JSON.stringify(args)));
|
|
86
|
+
}
|
|
87
|
+
const result = await execa(mocPath, args, {
|
|
88
|
+
stdio: options.verbose ? "inherit" : "pipe",
|
|
89
|
+
reject: false,
|
|
90
|
+
});
|
|
91
|
+
if (result.exitCode !== 0) {
|
|
92
|
+
if (!options.verbose) {
|
|
93
|
+
if (result.stderr) {
|
|
94
|
+
console.error(chalk.red(result.stderr));
|
|
95
|
+
}
|
|
96
|
+
if (result.stdout?.trim()) {
|
|
97
|
+
console.error(chalk.yellow("Build output:"));
|
|
98
|
+
console.error(result.stdout);
|
|
99
|
+
}
|
|
82
100
|
}
|
|
101
|
+
cliError(`Build failed for canister ${canisterName} (exit code: ${result.exitCode})`);
|
|
83
102
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
if (options.verbose && result.stdout && result.stdout.trim()) {
|
|
104
|
+
console.log(result.stdout);
|
|
105
|
+
}
|
|
106
|
+
options.verbose &&
|
|
107
|
+
console.log(chalk.gray(`Stable types written to ${mostPath}`));
|
|
108
|
+
const generatedDidPath = join(outputDir, `${canisterName}.did`);
|
|
109
|
+
const resolvedCandidPath = canister.candid
|
|
110
|
+
? resolveConfigPath(canister.candid)
|
|
111
|
+
: null;
|
|
112
|
+
if (resolvedCandidPath) {
|
|
113
|
+
try {
|
|
114
|
+
const compatible = await isCandidCompatible(generatedDidPath, resolvedCandidPath);
|
|
115
|
+
if (!compatible) {
|
|
116
|
+
cliError(`Candid compatibility check failed for canister ${canisterName}`);
|
|
117
|
+
}
|
|
118
|
+
if (options.verbose) {
|
|
119
|
+
console.log(chalk.gray(`Candid compatibility check passed for canister ${canisterName}`));
|
|
120
|
+
}
|
|
100
121
|
}
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
catch (err) {
|
|
123
|
+
cliError(`Error during Candid compatibility check for canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`);
|
|
103
124
|
}
|
|
104
125
|
}
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
options.verbose &&
|
|
127
|
+
console.log(chalk.gray(`Adding metadata to ${wasmPath}`));
|
|
128
|
+
const candidPath = resolvedCandidPath ?? generatedDidPath;
|
|
129
|
+
const candidText = await readFile(candidPath, "utf-8");
|
|
130
|
+
const customSections = [
|
|
131
|
+
{ name: `${candidVisibility} candid:service`, data: candidText },
|
|
132
|
+
];
|
|
133
|
+
if (canister.initArg) {
|
|
134
|
+
customSections.push({
|
|
135
|
+
name: `${candidVisibility} candid:args`,
|
|
136
|
+
data: canister.initArg,
|
|
137
|
+
});
|
|
107
138
|
}
|
|
139
|
+
const wasmBytes = await readFile(wasmPath);
|
|
140
|
+
const newWasm = getWasmBindings().add_custom_sections(wasmBytes, customSections);
|
|
141
|
+
await writeFile(wasmPath, newWasm);
|
|
108
142
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{ name: `${candidVisibility} candid:service`, data: candidText },
|
|
115
|
-
];
|
|
116
|
-
if (canister.initArg) {
|
|
117
|
-
customSections.push({
|
|
118
|
-
name: `${candidVisibility} candid:args`,
|
|
119
|
-
data: canister.initArg,
|
|
120
|
-
});
|
|
143
|
+
catch (err) {
|
|
144
|
+
if (err.message?.includes("Build failed for canister")) {
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
cliError(`Error while compiling canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`);
|
|
121
148
|
}
|
|
122
|
-
const wasmBytes = await readFile(wasmPath);
|
|
123
|
-
const newWasm = getWasmBindings().add_custom_sections(wasmBytes, customSections);
|
|
124
|
-
await writeFile(wasmPath, newWasm);
|
|
125
149
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
150
|
+
finally {
|
|
151
|
+
process.removeListener("exit", exitCleanup);
|
|
152
|
+
try {
|
|
153
|
+
await release?.();
|
|
129
154
|
}
|
|
130
|
-
|
|
155
|
+
catch { }
|
|
131
156
|
}
|
|
132
157
|
}
|
|
133
158
|
console.log(chalk.green(`\n✓ Built ${Object.keys(filteredCanisters).length} canister${Object.keys(filteredCanisters).length === 1 ? "" : "s"} successfully`));
|
|
@@ -148,9 +173,7 @@ function collectExtraArgs(config, canister, canisterName, extraArgs) {
|
|
|
148
173
|
args.push(...config.build.args);
|
|
149
174
|
}
|
|
150
175
|
if (canister.args) {
|
|
151
|
-
|
|
152
|
-
cliError(`Canister config 'args' should be an array of strings for canister ${canisterName}`);
|
|
153
|
-
}
|
|
176
|
+
validateCanisterArgs(canister, canisterName);
|
|
154
177
|
args.push(...canister.args);
|
|
155
178
|
}
|
|
156
179
|
if (extraArgs) {
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import { CanisterConfig } from "../types.js";
|
|
1
2
|
export interface CheckStableOptions {
|
|
2
3
|
verbose: boolean;
|
|
3
4
|
extraArgs: string[];
|
|
4
5
|
}
|
|
5
|
-
export declare function
|
|
6
|
+
export declare function resolveStablePath(canister: CanisterConfig, canisterName: string, options?: {
|
|
7
|
+
required?: boolean;
|
|
8
|
+
}): string | null;
|
|
9
|
+
export declare function checkStable(args: string[], options?: Partial<CheckStableOptions>): Promise<void>;
|
|
6
10
|
export interface RunStableCheckParams {
|
|
7
11
|
oldFile: string;
|
|
8
12
|
canisterMain: string;
|
|
9
13
|
canisterName: string;
|
|
10
14
|
mocPath: string;
|
|
11
15
|
globalMocArgs: string[];
|
|
16
|
+
canisterArgs: string[];
|
|
17
|
+
sources?: string[];
|
|
12
18
|
options?: Partial<CheckStableOptions>;
|
|
13
19
|
}
|
|
14
20
|
export declare function runStableCheck(params: RunStableCheckParams): Promise<void>;
|
|
@@ -5,30 +5,93 @@ import chalk from "chalk";
|
|
|
5
5
|
import { execa } from "execa";
|
|
6
6
|
import { cliError } from "../error.js";
|
|
7
7
|
import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
|
|
8
|
-
import { resolveSingleCanister } from "../helpers/resolve-canisters.js";
|
|
8
|
+
import { filterCanisters, looksLikeFile, resolveCanisterConfigs, resolveSingleCanister, validateCanisterArgs, } from "../helpers/resolve-canisters.js";
|
|
9
9
|
import { sourcesArgs } from "./sources.js";
|
|
10
10
|
import { toolchain } from "./toolchain/index.js";
|
|
11
11
|
const CHECK_STABLE_DIR = ".mops/.check-stable";
|
|
12
|
-
export
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
export function resolveStablePath(canister, canisterName, options) {
|
|
13
|
+
const stableConfig = canister["check-stable"];
|
|
14
|
+
if (!stableConfig) {
|
|
15
|
+
if (options?.required) {
|
|
16
|
+
cliError(`Canister '${canisterName}' has no [canisters.${canisterName}.check-stable] configuration in mops.toml`);
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
17
19
|
}
|
|
20
|
+
const stablePath = resolveConfigPath(stableConfig.path);
|
|
21
|
+
if (!existsSync(stablePath)) {
|
|
22
|
+
if (stableConfig.skipIfMissing) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
cliError(`Deployed file not found: ${stablePath} (canister '${canisterName}')\n` +
|
|
26
|
+
"Set skipIfMissing = true in [canisters." +
|
|
27
|
+
canisterName +
|
|
28
|
+
".check-stable] to skip this check when the file is missing.");
|
|
29
|
+
}
|
|
30
|
+
return stablePath;
|
|
31
|
+
}
|
|
32
|
+
export async function checkStable(args, options = {}) {
|
|
33
|
+
const config = readConfig();
|
|
18
34
|
const mocPath = await toolchain.bin("moc", { fallback: true });
|
|
19
35
|
const globalMocArgs = getGlobalMocArgs(config);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
canisterName
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
const firstArg = args[0];
|
|
37
|
+
if (firstArg && looksLikeFile(firstArg)) {
|
|
38
|
+
const oldFile = firstArg;
|
|
39
|
+
const canisterName = args[1];
|
|
40
|
+
const { name, canister } = resolveSingleCanister(config, canisterName);
|
|
41
|
+
if (!canister.main) {
|
|
42
|
+
cliError(`No main file specified for canister '${name}' in mops.toml`);
|
|
43
|
+
}
|
|
44
|
+
validateCanisterArgs(canister, name);
|
|
45
|
+
await runStableCheck({
|
|
46
|
+
oldFile,
|
|
47
|
+
canisterMain: resolveConfigPath(canister.main),
|
|
48
|
+
canisterName: name,
|
|
49
|
+
mocPath,
|
|
50
|
+
globalMocArgs,
|
|
51
|
+
canisterArgs: canister.args ?? [],
|
|
52
|
+
options,
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const canisters = resolveCanisterConfigs(config);
|
|
57
|
+
const canisterNames = args.length > 0 ? args : undefined;
|
|
58
|
+
const filteredCanisters = filterCanisters(canisters, canisterNames);
|
|
59
|
+
const sources = (await sourcesArgs()).flat();
|
|
60
|
+
let checked = 0;
|
|
61
|
+
for (const [name, canister] of Object.entries(filteredCanisters)) {
|
|
62
|
+
if (!canister.main) {
|
|
63
|
+
cliError(`No main file specified for canister '${name}' in mops.toml`);
|
|
64
|
+
}
|
|
65
|
+
validateCanisterArgs(canister, name);
|
|
66
|
+
const stablePath = resolveStablePath(canister, name, {
|
|
67
|
+
required: !!canisterNames,
|
|
68
|
+
});
|
|
69
|
+
if (!stablePath) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
await runStableCheck({
|
|
73
|
+
oldFile: stablePath,
|
|
74
|
+
canisterMain: resolveConfigPath(canister.main),
|
|
75
|
+
canisterName: name,
|
|
76
|
+
mocPath,
|
|
77
|
+
globalMocArgs,
|
|
78
|
+
canisterArgs: canister.args ?? [],
|
|
79
|
+
sources,
|
|
80
|
+
options,
|
|
81
|
+
});
|
|
82
|
+
checked++;
|
|
83
|
+
}
|
|
84
|
+
if (checked === 0 && !canisterNames) {
|
|
85
|
+
cliError("No canisters with [check-stable] configuration found in mops.toml.\n" +
|
|
86
|
+
"Either pass an old file: mops check-stable <old-file> [canister]\n" +
|
|
87
|
+
"Or configure check-stable for a canister:\n\n" +
|
|
88
|
+
" [canisters.backend.check-stable]\n" +
|
|
89
|
+
' path = "deployed.mo"');
|
|
90
|
+
}
|
|
28
91
|
}
|
|
29
92
|
export async function runStableCheck(params) {
|
|
30
|
-
const { oldFile, canisterMain, canisterName, mocPath, globalMocArgs, options = {}, } = params;
|
|
31
|
-
const sources = (await sourcesArgs()).flat();
|
|
93
|
+
const { oldFile, canisterMain, canisterName, mocPath, globalMocArgs, canisterArgs, options = {}, } = params;
|
|
94
|
+
const sources = params.sources ?? (await sourcesArgs()).flat();
|
|
32
95
|
const isOldMostFile = oldFile.endsWith(".most");
|
|
33
96
|
if (!existsSync(oldFile)) {
|
|
34
97
|
cliError(`File not found: ${oldFile}`);
|
|
@@ -38,8 +101,8 @@ export async function runStableCheck(params) {
|
|
|
38
101
|
try {
|
|
39
102
|
const oldMostPath = isOldMostFile
|
|
40
103
|
? oldFile
|
|
41
|
-
: await generateStableTypes(mocPath, oldFile, join(CHECK_STABLE_DIR, "old.most"), sources, globalMocArgs, options);
|
|
42
|
-
const newMostPath = await generateStableTypes(mocPath, canisterMain, join(CHECK_STABLE_DIR, "new.most"), sources, globalMocArgs, options);
|
|
104
|
+
: await generateStableTypes(mocPath, oldFile, join(CHECK_STABLE_DIR, "old.most"), sources, globalMocArgs, canisterArgs, options);
|
|
105
|
+
const newMostPath = await generateStableTypes(mocPath, canisterMain, join(CHECK_STABLE_DIR, "new.most"), sources, globalMocArgs, canisterArgs, options);
|
|
43
106
|
if (options.verbose) {
|
|
44
107
|
console.log(chalk.blue("check-stable"), chalk.gray(`Comparing ${oldMostPath} ↔ ${newMostPath}`));
|
|
45
108
|
}
|
|
@@ -63,7 +126,7 @@ export async function runStableCheck(params) {
|
|
|
63
126
|
await rm(CHECK_STABLE_DIR, { recursive: true, force: true });
|
|
64
127
|
}
|
|
65
128
|
}
|
|
66
|
-
async function generateStableTypes(mocPath, moFile, outputPath, sources, globalMocArgs, options) {
|
|
129
|
+
async function generateStableTypes(mocPath, moFile, outputPath, sources, globalMocArgs, canisterArgs, options) {
|
|
67
130
|
const base = basename(outputPath, ".most");
|
|
68
131
|
const wasmPath = join(CHECK_STABLE_DIR, base + ".wasm");
|
|
69
132
|
const args = [
|
|
@@ -73,6 +136,7 @@ async function generateStableTypes(mocPath, moFile, outputPath, sources, globalM
|
|
|
73
136
|
moFile,
|
|
74
137
|
...sources,
|
|
75
138
|
...globalMocArgs,
|
|
139
|
+
...canisterArgs,
|
|
76
140
|
...(options.extraArgs ?? []),
|
|
77
141
|
];
|
|
78
142
|
if (options.verbose) {
|
package/dist/commands/check.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export interface CheckOptions {
|
|
|
3
3
|
fix: boolean;
|
|
4
4
|
extraArgs: string[];
|
|
5
5
|
}
|
|
6
|
-
export declare function check(
|
|
6
|
+
export declare function check(args: string[], options?: Partial<CheckOptions>): Promise<void>;
|