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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Next
|
|
4
4
|
|
|
5
|
+
## 2.10.0
|
|
6
|
+
- `mops check` and `mops check-stable` now apply per-canister `[canisters.<name>].args` (previously only `mops build` applied them)
|
|
7
|
+
- `mops check` now accepts canister names as arguments (e.g. `mops check backend`) to check a specific canister
|
|
8
|
+
- `mops check-stable` now works without arguments, checking all canisters with `[check-stable]` configured
|
|
9
|
+
- `mops check-stable` now accepts canister names as arguments (e.g. `mops check-stable backend`)
|
|
10
|
+
|
|
11
|
+
## 2.9.0
|
|
12
|
+
- Add `mops info <pkg>` command to show detailed package metadata from the registry
|
|
13
|
+
- Add `[lint.extra]` config for applying additional lint rules to specific files via glob patterns
|
|
14
|
+
|
|
5
15
|
## 2.8.1
|
|
6
16
|
|
|
7
17
|
- Fix `mops check-stable` failing when `[moc] args` contains flags with relative paths (e.g. `--actor-idl=system-idl`)
|
package/bundle/cli.tgz
CHANGED
|
Binary file
|
package/cli.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { getNetwork } from "./api/network.js";
|
|
|
8
8
|
import { cacheSize, cleanCache, show } from "./cache.js";
|
|
9
9
|
import { add } from "./commands/add.js";
|
|
10
10
|
import { bench } from "./commands/bench.js";
|
|
11
|
-
import { build
|
|
11
|
+
import { build } from "./commands/build.js";
|
|
12
12
|
import { bump } from "./commands/bump.js";
|
|
13
13
|
import { check } from "./commands/check.js";
|
|
14
14
|
import { checkCandid } from "./commands/check-candid.js";
|
|
@@ -16,6 +16,7 @@ import { checkStable } from "./commands/check-stable.js";
|
|
|
16
16
|
import { docsCoverage } from "./commands/docs-coverage.js";
|
|
17
17
|
import { docs } from "./commands/docs.js";
|
|
18
18
|
import { format } from "./commands/format.js";
|
|
19
|
+
import { info } from "./commands/info.js";
|
|
19
20
|
import { init } from "./commands/init.js";
|
|
20
21
|
import { lint } from "./commands/lint.js";
|
|
21
22
|
import { installAll } from "./commands/install/install-all.js";
|
|
@@ -279,6 +280,15 @@ program
|
|
|
279
280
|
await search(text);
|
|
280
281
|
});
|
|
281
282
|
|
|
283
|
+
// info
|
|
284
|
+
program
|
|
285
|
+
.command("info <pkg>")
|
|
286
|
+
.description("Show detailed information about a package from the registry")
|
|
287
|
+
.option("--versions", "List all published versions, one per line")
|
|
288
|
+
.action(async (pkg: string, options) => {
|
|
289
|
+
await info(pkg, options);
|
|
290
|
+
});
|
|
291
|
+
|
|
282
292
|
// cache
|
|
283
293
|
program
|
|
284
294
|
.command("cache")
|
|
@@ -320,9 +330,9 @@ program
|
|
|
320
330
|
|
|
321
331
|
// check
|
|
322
332
|
program
|
|
323
|
-
.command("check [
|
|
333
|
+
.command("check [args...]")
|
|
324
334
|
.description(
|
|
325
|
-
"Check Motoko files for syntax errors and type issues. If no
|
|
335
|
+
"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]",
|
|
326
336
|
)
|
|
327
337
|
.option("--verbose", "Verbose console output")
|
|
328
338
|
.addOption(
|
|
@@ -332,15 +342,15 @@ program
|
|
|
332
342
|
),
|
|
333
343
|
)
|
|
334
344
|
.allowUnknownOption(true)
|
|
335
|
-
.action(async (
|
|
345
|
+
.action(async (args, options) => {
|
|
336
346
|
checkConfigFile(true);
|
|
337
|
-
const { extraArgs, args:
|
|
347
|
+
const { extraArgs, args: argList } = parseExtraArgs(args);
|
|
338
348
|
await installAll({
|
|
339
349
|
silent: true,
|
|
340
350
|
lock: "ignore",
|
|
341
351
|
installFromLockFile: true,
|
|
342
352
|
});
|
|
343
|
-
await check(
|
|
353
|
+
await check(argList, {
|
|
344
354
|
...options,
|
|
345
355
|
extraArgs,
|
|
346
356
|
});
|
|
@@ -362,21 +372,21 @@ program
|
|
|
362
372
|
|
|
363
373
|
// check-stable
|
|
364
374
|
program
|
|
365
|
-
.command("check-stable
|
|
375
|
+
.command("check-stable [args...]")
|
|
366
376
|
.description(
|
|
367
|
-
"Check stable variable compatibility
|
|
377
|
+
"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",
|
|
368
378
|
)
|
|
369
379
|
.option("--verbose", "Verbose console output")
|
|
370
380
|
.allowUnknownOption(true)
|
|
371
|
-
.action(async (
|
|
381
|
+
.action(async (args, options) => {
|
|
372
382
|
checkConfigFile(true);
|
|
373
|
-
const { extraArgs } = parseExtraArgs();
|
|
383
|
+
const { extraArgs, args: argList } = parseExtraArgs(args);
|
|
374
384
|
await installAll({
|
|
375
385
|
silent: true,
|
|
376
386
|
lock: "ignore",
|
|
377
387
|
installFromLockFile: true,
|
|
378
388
|
});
|
|
379
|
-
await checkStable(
|
|
389
|
+
await checkStable(argList, {
|
|
380
390
|
...options,
|
|
381
391
|
extraArgs,
|
|
382
392
|
});
|
package/commands/build.ts
CHANGED
|
@@ -3,9 +3,14 @@ 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 {
|
|
9
|
+
import {
|
|
10
|
+
filterCanisters,
|
|
11
|
+
resolveCanisterConfigs,
|
|
12
|
+
validateCanisterArgs,
|
|
13
|
+
} from "../helpers/resolve-canisters.js";
|
|
9
14
|
import { CanisterConfig, Config } from "../types.js";
|
|
10
15
|
import { CustomSection, getWasmBindings } from "../wasm.js";
|
|
11
16
|
import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
|
|
@@ -40,26 +45,11 @@ export async function build(
|
|
|
40
45
|
cliError(`No Motoko canisters found in mops.toml configuration`);
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
if (canisterNames) {
|
|
44
|
-
let invalidNames = canisterNames.filter((name) => !(name in canisters));
|
|
45
|
-
if (invalidNames.length) {
|
|
46
|
-
cliError(
|
|
47
|
-
`Motoko canister(s) not found in mops.toml configuration: ${invalidNames.join(", ")}`,
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
48
|
if (!(await exists(outputDir))) {
|
|
53
49
|
await mkdir(outputDir, { recursive: true });
|
|
54
50
|
}
|
|
55
51
|
|
|
56
|
-
const filteredCanisters = canisterNames
|
|
57
|
-
? Object.fromEntries(
|
|
58
|
-
Object.entries(canisters).filter(([name]) =>
|
|
59
|
-
canisterNames.includes(name),
|
|
60
|
-
),
|
|
61
|
-
)
|
|
62
|
-
: canisters;
|
|
52
|
+
const filteredCanisters = filterCanisters(canisters, canisterNames);
|
|
63
53
|
|
|
64
54
|
for (let [canisterName, canister] of Object.entries(filteredCanisters)) {
|
|
65
55
|
console.log(chalk.blue("build canister"), chalk.bold(canisterName));
|
|
@@ -70,115 +60,149 @@ export async function build(
|
|
|
70
60
|
motokoPath = resolveConfigPath(motokoPath);
|
|
71
61
|
const wasmPath = join(outputDir, `${canisterName}.wasm`);
|
|
72
62
|
const mostPath = join(outputDir, `${canisterName}.most`);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
|
|
89
|
-
if (isPublicCandid) {
|
|
90
|
-
args.push("--public-metadata", "candid:service");
|
|
91
|
-
args.push("--public-metadata", "candid:args");
|
|
63
|
+
|
|
64
|
+
// per-canister lock to prevent parallel builds of the same canister from clobbering output files
|
|
65
|
+
const lockTarget = join(outputDir, `.${canisterName}.buildlock`);
|
|
66
|
+
await writeFile(lockTarget, "", { flag: "a" });
|
|
67
|
+
|
|
68
|
+
let release: (() => Promise<void>) | undefined;
|
|
69
|
+
try {
|
|
70
|
+
release = await lock(lockTarget, {
|
|
71
|
+
stale: 300_000,
|
|
72
|
+
retries: { retries: 60, minTimeout: 500, maxTimeout: 5_000 },
|
|
73
|
+
});
|
|
74
|
+
} catch {
|
|
75
|
+
cliError(
|
|
76
|
+
`Failed to acquire build lock for canister ${canisterName} — another build may be stuck`,
|
|
77
|
+
);
|
|
92
78
|
}
|
|
79
|
+
|
|
80
|
+
// proper-lockfile registers its own signal-exit handler, but it doesn't reliably
|
|
81
|
+
// fire on process.exit(). This manual handler covers that gap. Double-unlock is
|
|
82
|
+
// harmless (the second call throws and is caught).
|
|
83
|
+
const exitCleanup = () => {
|
|
84
|
+
try {
|
|
85
|
+
unlockSync(lockTarget);
|
|
86
|
+
} catch {}
|
|
87
|
+
};
|
|
88
|
+
process.on("exit", exitCleanup);
|
|
89
|
+
|
|
93
90
|
try {
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
let args = [
|
|
92
|
+
"-c",
|
|
93
|
+
"--idl",
|
|
94
|
+
"--stable-types",
|
|
95
|
+
"-o",
|
|
96
|
+
wasmPath,
|
|
97
|
+
motokoPath,
|
|
98
|
+
...(await sourcesArgs()).flat(),
|
|
99
|
+
...getGlobalMocArgs(config),
|
|
100
|
+
];
|
|
101
|
+
args.push(
|
|
102
|
+
...collectExtraArgs(config, canister, canisterName, options.extraArgs),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const isPublicCandid = true; // always true for now to reduce corner cases
|
|
106
|
+
const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
|
|
107
|
+
if (isPublicCandid) {
|
|
108
|
+
args.push("--public-metadata", "candid:service");
|
|
109
|
+
args.push("--public-metadata", "candid:args");
|
|
96
110
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
try {
|
|
112
|
+
if (options.verbose) {
|
|
113
|
+
console.log(chalk.gray(mocPath, JSON.stringify(args)));
|
|
114
|
+
}
|
|
115
|
+
const result = await execa(mocPath, args, {
|
|
116
|
+
stdio: options.verbose ? "inherit" : "pipe",
|
|
117
|
+
reject: false,
|
|
118
|
+
});
|
|
101
119
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
if (result.exitCode !== 0) {
|
|
121
|
+
if (!options.verbose) {
|
|
122
|
+
if (result.stderr) {
|
|
123
|
+
console.error(chalk.red(result.stderr));
|
|
124
|
+
}
|
|
125
|
+
if (result.stdout?.trim()) {
|
|
126
|
+
console.error(chalk.yellow("Build output:"));
|
|
127
|
+
console.error(result.stdout);
|
|
128
|
+
}
|
|
110
129
|
}
|
|
130
|
+
cliError(
|
|
131
|
+
`Build failed for canister ${canisterName} (exit code: ${result.exitCode})`,
|
|
132
|
+
);
|
|
111
133
|
}
|
|
112
|
-
cliError(
|
|
113
|
-
`Build failed for canister ${canisterName} (exit code: ${result.exitCode})`,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
134
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
options.verbose &&
|
|
122
|
-
console.log(chalk.gray(`Stable types written to ${mostPath}`));
|
|
135
|
+
if (options.verbose && result.stdout && result.stdout.trim()) {
|
|
136
|
+
console.log(result.stdout);
|
|
137
|
+
}
|
|
123
138
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
? resolveConfigPath(canister.candid)
|
|
127
|
-
: null;
|
|
139
|
+
options.verbose &&
|
|
140
|
+
console.log(chalk.gray(`Stable types written to ${mostPath}`));
|
|
128
141
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
resolvedCandidPath,
|
|
134
|
-
);
|
|
142
|
+
const generatedDidPath = join(outputDir, `${canisterName}.did`);
|
|
143
|
+
const resolvedCandidPath = canister.candid
|
|
144
|
+
? resolveConfigPath(canister.candid)
|
|
145
|
+
: null;
|
|
135
146
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
if (resolvedCandidPath) {
|
|
148
|
+
try {
|
|
149
|
+
const compatible = await isCandidCompatible(
|
|
150
|
+
generatedDidPath,
|
|
151
|
+
resolvedCandidPath,
|
|
139
152
|
);
|
|
140
|
-
}
|
|
141
153
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
154
|
+
if (!compatible) {
|
|
155
|
+
cliError(
|
|
156
|
+
`Candid compatibility check failed for canister ${canisterName}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (options.verbose) {
|
|
161
|
+
console.log(
|
|
162
|
+
chalk.gray(
|
|
163
|
+
`Candid compatibility check passed for canister ${canisterName}`,
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
} catch (err: any) {
|
|
168
|
+
cliError(
|
|
169
|
+
`Error during Candid compatibility check for canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`,
|
|
147
170
|
);
|
|
148
171
|
}
|
|
149
|
-
} catch (err: any) {
|
|
150
|
-
cliError(
|
|
151
|
-
`Error during Candid compatibility check for canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`,
|
|
152
|
-
);
|
|
153
172
|
}
|
|
154
|
-
}
|
|
155
173
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
options.verbose &&
|
|
175
|
+
console.log(chalk.gray(`Adding metadata to ${wasmPath}`));
|
|
176
|
+
const candidPath = resolvedCandidPath ?? generatedDidPath;
|
|
177
|
+
const candidText = await readFile(candidPath, "utf-8");
|
|
178
|
+
const customSections: CustomSection[] = [
|
|
179
|
+
{ name: `${candidVisibility} candid:service`, data: candidText },
|
|
180
|
+
];
|
|
181
|
+
if (canister.initArg) {
|
|
182
|
+
customSections.push({
|
|
183
|
+
name: `${candidVisibility} candid:args`,
|
|
184
|
+
data: canister.initArg,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const wasmBytes = await readFile(wasmPath);
|
|
188
|
+
const newWasm = getWasmBindings().add_custom_sections(
|
|
189
|
+
wasmBytes,
|
|
190
|
+
customSections,
|
|
191
|
+
);
|
|
192
|
+
await writeFile(wasmPath, newWasm);
|
|
193
|
+
} catch (err: any) {
|
|
194
|
+
if (err.message?.includes("Build failed for canister")) {
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
cliError(
|
|
198
|
+
`Error while compiling canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`,
|
|
199
|
+
);
|
|
178
200
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
201
|
+
} finally {
|
|
202
|
+
process.removeListener("exit", exitCleanup);
|
|
203
|
+
try {
|
|
204
|
+
await release?.();
|
|
205
|
+
} catch {}
|
|
182
206
|
}
|
|
183
207
|
}
|
|
184
208
|
|
|
@@ -214,11 +238,7 @@ function collectExtraArgs(
|
|
|
214
238
|
args.push(...config.build.args);
|
|
215
239
|
}
|
|
216
240
|
if (canister.args) {
|
|
217
|
-
|
|
218
|
-
cliError(
|
|
219
|
-
`Canister config 'args' should be an array of strings for canister ${canisterName}`,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
241
|
+
validateCanisterArgs(canister, canisterName);
|
|
222
242
|
args.push(...canister.args);
|
|
223
243
|
}
|
|
224
244
|
if (extraArgs) {
|
package/commands/check-stable.ts
CHANGED
|
@@ -5,7 +5,14 @@ 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 {
|
|
8
|
+
import { CanisterConfig } from "../types.js";
|
|
9
|
+
import {
|
|
10
|
+
filterCanisters,
|
|
11
|
+
looksLikeFile,
|
|
12
|
+
resolveCanisterConfigs,
|
|
13
|
+
resolveSingleCanister,
|
|
14
|
+
validateCanisterArgs,
|
|
15
|
+
} from "../helpers/resolve-canisters.js";
|
|
9
16
|
import { sourcesArgs } from "./sources.js";
|
|
10
17
|
import { toolchain } from "./toolchain/index.js";
|
|
11
18
|
|
|
@@ -16,29 +23,108 @@ export interface CheckStableOptions {
|
|
|
16
23
|
extraArgs: string[];
|
|
17
24
|
}
|
|
18
25
|
|
|
26
|
+
export function resolveStablePath(
|
|
27
|
+
canister: CanisterConfig,
|
|
28
|
+
canisterName: string,
|
|
29
|
+
options?: { required?: boolean },
|
|
30
|
+
): string | null {
|
|
31
|
+
const stableConfig = canister["check-stable"];
|
|
32
|
+
if (!stableConfig) {
|
|
33
|
+
if (options?.required) {
|
|
34
|
+
cliError(
|
|
35
|
+
`Canister '${canisterName}' has no [canisters.${canisterName}.check-stable] configuration in mops.toml`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const stablePath = resolveConfigPath(stableConfig.path);
|
|
41
|
+
if (!existsSync(stablePath)) {
|
|
42
|
+
if (stableConfig.skipIfMissing) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
cliError(
|
|
46
|
+
`Deployed file not found: ${stablePath} (canister '${canisterName}')\n` +
|
|
47
|
+
"Set skipIfMissing = true in [canisters." +
|
|
48
|
+
canisterName +
|
|
49
|
+
".check-stable] to skip this check when the file is missing.",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return stablePath;
|
|
53
|
+
}
|
|
54
|
+
|
|
19
55
|
export async function checkStable(
|
|
20
|
-
|
|
21
|
-
canisterName: string | undefined,
|
|
56
|
+
args: string[],
|
|
22
57
|
options: Partial<CheckStableOptions> = {},
|
|
23
58
|
): Promise<void> {
|
|
24
59
|
const config = readConfig();
|
|
25
|
-
const
|
|
60
|
+
const mocPath = await toolchain.bin("moc", { fallback: true });
|
|
61
|
+
const globalMocArgs = getGlobalMocArgs(config);
|
|
62
|
+
|
|
63
|
+
const firstArg = args[0];
|
|
64
|
+
if (firstArg && looksLikeFile(firstArg)) {
|
|
65
|
+
const oldFile = firstArg;
|
|
66
|
+
const canisterName = args[1];
|
|
67
|
+
const { name, canister } = resolveSingleCanister(config, canisterName);
|
|
68
|
+
|
|
69
|
+
if (!canister.main) {
|
|
70
|
+
cliError(`No main file specified for canister '${name}' in mops.toml`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
validateCanisterArgs(canister, name);
|
|
26
74
|
|
|
27
|
-
|
|
28
|
-
|
|
75
|
+
await runStableCheck({
|
|
76
|
+
oldFile,
|
|
77
|
+
canisterMain: resolveConfigPath(canister.main),
|
|
78
|
+
canisterName: name,
|
|
79
|
+
mocPath,
|
|
80
|
+
globalMocArgs,
|
|
81
|
+
canisterArgs: canister.args ?? [],
|
|
82
|
+
options,
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
29
85
|
}
|
|
30
86
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
87
|
+
const canisters = resolveCanisterConfigs(config);
|
|
88
|
+
const canisterNames = args.length > 0 ? args : undefined;
|
|
89
|
+
const filteredCanisters = filterCanisters(canisters, canisterNames);
|
|
90
|
+
const sources = (await sourcesArgs()).flat();
|
|
33
91
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
92
|
+
let checked = 0;
|
|
93
|
+
for (const [name, canister] of Object.entries(filteredCanisters)) {
|
|
94
|
+
if (!canister.main) {
|
|
95
|
+
cliError(`No main file specified for canister '${name}' in mops.toml`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
validateCanisterArgs(canister, name);
|
|
99
|
+
const stablePath = resolveStablePath(canister, name, {
|
|
100
|
+
required: !!canisterNames,
|
|
101
|
+
});
|
|
102
|
+
if (!stablePath) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
await runStableCheck({
|
|
107
|
+
oldFile: stablePath,
|
|
108
|
+
canisterMain: resolveConfigPath(canister.main),
|
|
109
|
+
canisterName: name,
|
|
110
|
+
mocPath,
|
|
111
|
+
globalMocArgs,
|
|
112
|
+
canisterArgs: canister.args ?? [],
|
|
113
|
+
sources,
|
|
114
|
+
options,
|
|
115
|
+
});
|
|
116
|
+
checked++;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (checked === 0 && !canisterNames) {
|
|
120
|
+
cliError(
|
|
121
|
+
"No canisters with [check-stable] configuration found in mops.toml.\n" +
|
|
122
|
+
"Either pass an old file: mops check-stable <old-file> [canister]\n" +
|
|
123
|
+
"Or configure check-stable for a canister:\n\n" +
|
|
124
|
+
" [canisters.backend.check-stable]\n" +
|
|
125
|
+
' path = "deployed.mo"',
|
|
126
|
+
);
|
|
127
|
+
}
|
|
42
128
|
}
|
|
43
129
|
|
|
44
130
|
export interface RunStableCheckParams {
|
|
@@ -47,6 +133,8 @@ export interface RunStableCheckParams {
|
|
|
47
133
|
canisterName: string;
|
|
48
134
|
mocPath: string;
|
|
49
135
|
globalMocArgs: string[];
|
|
136
|
+
canisterArgs: string[];
|
|
137
|
+
sources?: string[];
|
|
50
138
|
options?: Partial<CheckStableOptions>;
|
|
51
139
|
}
|
|
52
140
|
|
|
@@ -59,10 +147,11 @@ export async function runStableCheck(
|
|
|
59
147
|
canisterName,
|
|
60
148
|
mocPath,
|
|
61
149
|
globalMocArgs,
|
|
150
|
+
canisterArgs,
|
|
62
151
|
options = {},
|
|
63
152
|
} = params;
|
|
64
153
|
|
|
65
|
-
const sources = (await sourcesArgs()).flat();
|
|
154
|
+
const sources = params.sources ?? (await sourcesArgs()).flat();
|
|
66
155
|
const isOldMostFile = oldFile.endsWith(".most");
|
|
67
156
|
|
|
68
157
|
if (!existsSync(oldFile)) {
|
|
@@ -80,6 +169,7 @@ export async function runStableCheck(
|
|
|
80
169
|
join(CHECK_STABLE_DIR, "old.most"),
|
|
81
170
|
sources,
|
|
82
171
|
globalMocArgs,
|
|
172
|
+
canisterArgs,
|
|
83
173
|
options,
|
|
84
174
|
);
|
|
85
175
|
|
|
@@ -89,6 +179,7 @@ export async function runStableCheck(
|
|
|
89
179
|
join(CHECK_STABLE_DIR, "new.most"),
|
|
90
180
|
sources,
|
|
91
181
|
globalMocArgs,
|
|
182
|
+
canisterArgs,
|
|
92
183
|
options,
|
|
93
184
|
);
|
|
94
185
|
|
|
@@ -134,6 +225,7 @@ async function generateStableTypes(
|
|
|
134
225
|
outputPath: string,
|
|
135
226
|
sources: string[],
|
|
136
227
|
globalMocArgs: string[],
|
|
228
|
+
canisterArgs: string[],
|
|
137
229
|
options: Partial<CheckStableOptions>,
|
|
138
230
|
): Promise<string> {
|
|
139
231
|
const base = basename(outputPath, ".most");
|
|
@@ -145,6 +237,7 @@ async function generateStableTypes(
|
|
|
145
237
|
moFile,
|
|
146
238
|
...sources,
|
|
147
239
|
...globalMocArgs,
|
|
240
|
+
...canisterArgs,
|
|
148
241
|
...(options.extraArgs ?? []),
|
|
149
242
|
];
|
|
150
243
|
|