ic-mops 2.3.1 → 2.4.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/RELEASE.md +16 -162
  3. package/bundle/cli.tgz +0 -0
  4. package/cli.ts +2 -5
  5. package/commands/bench-replica.ts +13 -34
  6. package/commands/build.ts +70 -24
  7. package/commands/check-stable.ts +2 -2
  8. package/commands/check.ts +9 -8
  9. package/commands/replica.ts +17 -42
  10. package/dist/cli.js +3 -2
  11. package/dist/commands/bench-replica.d.ts +3 -4
  12. package/dist/commands/bench-replica.js +5 -23
  13. package/dist/commands/build.js +47 -20
  14. package/dist/commands/check-stable.js +2 -2
  15. package/dist/commands/check.js +9 -8
  16. package/dist/commands/replica.d.ts +3 -4
  17. package/dist/commands/replica.js +9 -29
  18. package/dist/helpers/pocket-ic-client.d.ts +9 -0
  19. package/dist/helpers/pocket-ic-client.js +18 -0
  20. package/dist/mops.d.ts +5 -0
  21. package/dist/mops.js +7 -0
  22. package/dist/package.json +1 -1
  23. package/dist/release-cli.js +6 -2
  24. package/dist/tests/build.test.js +83 -16
  25. package/dist/tests/check.test.js +7 -0
  26. package/dist/tests/pocket-ic.test.d.ts +1 -0
  27. package/dist/tests/pocket-ic.test.js +12 -0
  28. package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  29. package/dist/wasm/pkg/nodejs/wasm_bg.wasm.d.ts +1 -1
  30. package/dist/wasm/pkg/web/wasm.d.ts +1 -1
  31. package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
  32. package/dist/wasm/pkg/web/wasm_bg.wasm.d.ts +1 -1
  33. package/helpers/pocket-ic-client.ts +32 -0
  34. package/mops.ts +8 -0
  35. package/package.json +1 -1
  36. package/release-cli.ts +7 -2
  37. package/tests/__snapshots__/build.test.ts.snap +12 -0
  38. package/tests/build/custom-output/dfx.json +8 -0
  39. package/tests/build/custom-output/mops.toml +8 -0
  40. package/tests/build/custom-output/src/Main.mo +5 -0
  41. package/tests/build.test.ts +92 -20
  42. package/tests/check/canisters-subdir/mops.toml +8 -0
  43. package/tests/check/canisters-subdir/src/backend/main.mo +5 -0
  44. package/tests/check.test.ts +11 -0
  45. package/tests/pocket-ic/mops.toml +3 -0
  46. package/tests/pocket-ic/test/hello.test.mo +3 -0
  47. package/tests/pocket-ic.test.ts +19 -0
  48. package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  49. package/wasm/pkg/nodejs/wasm_bg.wasm.d.ts +1 -1
  50. package/wasm/pkg/web/wasm.d.ts +1 -1
  51. package/wasm/pkg/web/wasm_bg.wasm +0 -0
  52. package/wasm/pkg/web/wasm_bg.wasm.d.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Next
4
4
 
5
+ ## 2.4.0
6
+ - Support `[build].outputDir` config in `mops.toml` for custom build output directory
7
+ - Fix `mops build --output` CLI option being silently ignored
8
+ - Warn when canister `args` contain flags managed by `mops build` (e.g. `-o`, `-c`, `--idl`)
9
+ - Support pocket-ic versions beyond 9.x.x (fixes #410)
10
+
11
+ ## 2.3.2
12
+ - Fix `mops check`, `mops build`, and `mops check-stable` failing to find canister entrypoints when run from a subdirectory
13
+
5
14
  ## 2.3.1
6
15
  - Fix `mops build` and `mops check-candid` failing with "Wasm bindings have not been set" when installed via `npm i -g ic-mops`
7
16
 
package/RELEASE.md CHANGED
@@ -1,37 +1,8 @@
1
1
  # Mops CLI Release
2
2
 
3
- ## Prerequisites
3
+ ## 1. Update changelog
4
4
 
5
- ### macOS: GNU tar
6
-
7
- ```bash
8
- brew install gnu-tar
9
- ```
10
-
11
- Add to `~/.zshrc` or `~/.bashrc`:
12
- ```bash
13
- export PATH="$HOMEBREW_PREFIX/opt/gnu-tar/libexec/gnubin:$PATH"
14
- ```
15
-
16
- ### Docker
17
-
18
- Docker (or OrbStack) must be installed and running. The CLI build happens inside a Docker container (`zenvoich/mops-builder:1.1.0`) for reproducibility.
19
-
20
- ### dfx
21
-
22
- `dfx` must be installed with the `mops` identity configured (see [Adding the `mops` identity to dfx](#adding-the-mops-identity-to-dfx)).
23
-
24
- ## Release Steps
25
-
26
- ### 1. Install dependencies
27
-
28
- ```bash
29
- cd cli && bun install
30
- ```
31
-
32
- ### 2. Update changelog
33
-
34
- Move items from the `## Next` section in `CHANGELOG.md` into a new version heading:
5
+ Move items from `## Next` in `CHANGELOG.md` into a new version heading:
35
6
 
36
7
  ```markdown
37
8
  ## Next
@@ -41,166 +12,49 @@ Move items from the `## Next` section in `CHANGELOG.md` into a new version headi
41
12
  - Change 2
42
13
  ```
43
14
 
44
- The heading must contain the exact version string — `release-cli.ts` parses it to extract release notes.
45
-
46
- ### 3. Create a release branch and PR
47
-
48
- Direct pushes to `main` are not allowed. Create a branch and PR:
49
-
50
- ```bash
51
- git checkout -b <username>/release-X.Y.Z
52
- ```
53
-
54
- ### 4. Bump version
15
+ The heading must match the exact version string — the release workflow parses it to extract release notes.
55
16
 
56
- Use `--no-git-tag-version` since the version bump will be committed as part of the PR (not directly on `main`):
17
+ ## 2. Bump version
57
18
 
58
19
  ```bash
59
20
  cd cli
60
- npm version minor --no-git-tag-version # or: patch / major
21
+ npm version patch --no-git-tag-version # or: minor / major
61
22
  ```
62
23
 
63
- ### 5. Commit, push, and open PR
24
+ ## 3. Create a release PR
64
25
 
65
26
  ```bash
27
+ git checkout -b <username>/release-X.Y.Z
66
28
  git add cli/CHANGELOG.md cli/package.json cli/package-lock.json
67
29
  git commit -m "release: CLI vX.Y.Z"
68
30
  git push -u origin <username>/release-X.Y.Z
69
31
  gh pr create --title "release: CLI vX.Y.Z" --body "..."
70
32
  ```
71
33
 
72
- Wait for CI to pass, then merge the PR.
73
-
74
- ### 6. Check reproducibility of the build
75
-
76
- After the PR is merged to `main`, check the SHA256 hash from the latest [build-hash workflow](https://github.com/caffeinelabs/mops/actions/workflows/build-hash.yml) run.
77
-
78
- **Important**: The CI hash is written to the GitHub Actions **Step Summary** — it is only visible on the **Summary tab** of the workflow run page. It does **not** appear in the downloadable logs (`gh run view --log` will not find it). You must open the run URL in a browser to see it.
79
-
80
- Build the same version locally:
81
- ```bash
82
- cd cli
83
- MOPS_VERSION=0.0.0 ./build.sh
84
- ```
85
-
86
- `build.sh` does the following:
87
- 1. Reads `COMMIT_HASH` (defaults to `git rev-parse HEAD`) and `MOPS_VERSION`
88
- 2. Runs `docker build` using `cli/Dockerfile` with those as build args
89
- 3. The Dockerfile clones the repo at that commit, runs `bun install`, sets the version, and runs `npm run build`
90
- 4. Prints the SHA256 hash of `cli.tgz`
91
- 5. Copies `cli.tgz` out of the container into `cli/bundle/cli.tgz`
92
-
93
- Compare the locally printed hash with the one from the CI Step Summary. If they don't match, do not proceed.
94
-
95
- **Notes on the local build output:**
96
- - The "Verification failed" message at the end is **expected** — it happens because no `SHASUM` env var is passed for comparison. The important output is the `Actual shasum: <hash>` line.
34
+ Wait for CI to pass, then merge.
97
35
 
98
- ### 7. Publish to npm
99
-
100
- After the PR is merged and the build hash is verified:
36
+ ## 4. Tag and push
101
37
 
102
38
  ```bash
103
39
  git checkout main && git pull
104
- cd cli
105
- npm publish
40
+ git tag cli-vX.Y.Z
41
+ git push origin cli-vX.Y.Z
106
42
  ```
107
43
 
108
- ### 8. Prepare on-chain release
109
-
110
- Run from the **repo root** (not `cli/`), with Docker running:
44
+ This triggers the [`release.yml`](../.github/workflows/release.yml) workflow which builds, publishes to npm, creates a GitHub Release, deploys canisters (`cli.mops.one` and `docs.mops.one`), and opens a PR with on-chain release artifacts.
111
45
 
112
- ```bash
113
- npm run release-cli
114
- ```
46
+ Monitor at [Actions → Release CLI](https://github.com/caffeinelabs/mops/actions/workflows/release.yml).
115
47
 
116
- This runs `cli/release-cli.ts`, which:
117
- 1. Calls `./build.sh` — full Docker build with the real version from `cli/package.json`
118
- 2. Extracts release notes from `CHANGELOG.md` for that version
119
- 3. Computes SHA256 of `bundle/cli.tgz`
120
- 4. Copies the tarball to `cli-releases/versions/<version>.tgz`
121
- 5. Creates tag copies: `latest.tgz` and `<major>.tgz`
122
- 6. Updates `cli-releases/tags/latest` to the new version
123
- 7. Updates `cli-releases/releases.json` with metadata (timestamp, size, hash, commit hash, download URL, release notes)
48
+ ## 5. Merge artifacts PR
124
49
 
125
- ### 9. Deploy the canister
126
-
127
- ```bash
128
- dfx deploy --network ic --no-wallet cli --identity mops
129
- ```
130
-
131
- This deploys the `cli-releases` canister (serving `cli.mops.one`) to the Internet Computer mainnet.
132
-
133
- ### 10. Deploy the docs canister
134
-
135
- ```bash
136
- dfx deploy --network ic --no-wallet docs --identity mops
137
- ```
138
-
139
- This builds the Docusaurus site (`docs/`) and deploys the `docs` assets canister (serving `docs.mops.one`). Docs are not auto-deployed, so this step ensures any documentation changes from the release are published.
140
-
141
- ### 11. Commit and push release artifacts
142
-
143
- Step 8 generates files in `cli-releases/` that must be committed and pushed:
144
-
145
- ```bash
146
- git add cli-releases/
147
- git commit -m "cli-releases: v<version> artifacts"
148
- ```
149
-
150
- Since direct pushes to `main` are not allowed, create a branch and PR:
151
-
152
- ```bash
153
- git checkout -b <username>/release-X.Y.Z-artifacts
154
- git push -u origin <username>/release-X.Y.Z-artifacts
155
- gh pr create --title "cli-releases: vX.Y.Z artifacts" --body "Release artifacts generated by \`npm run release-cli\` for CLI vX.Y.Z."
156
- ```
157
-
158
- Merge this PR after approval.
50
+ After the workflow completes, merge the `cli-releases: vX.Y.Z artifacts` PR.
159
51
 
160
52
  ## Verify build
161
53
 
162
- Anyone can verify a released version by rebuilding from source:
54
+ Anyone can verify a released version by rebuilding from source. Instructions are included in each [GitHub Release](https://github.com/caffeinelabs/mops/releases).
163
55
 
164
56
  ```bash
165
57
  cd cli
166
58
  docker build . --build-arg COMMIT_HASH=<commit_hash> --build-arg MOPS_VERSION=<mops_version> -t mops
167
59
  docker run --rm --env SHASUM=<build_hash> mops
168
60
  ```
169
-
170
- ## Adding the `mops` identity to dfx
171
-
172
- Check if the identity already exists:
173
- ```bash
174
- dfx identity list
175
- ```
176
-
177
- If `mops` appears, verify it's the correct one:
178
- ```bash
179
- dfx identity get-principal --identity mops
180
- ```
181
-
182
- If it doesn't exist, import it from the PEM file stored in Bitwarden (`Mops identity (canisters and packages)`):
183
-
184
- 1. Save the PEM key to a temporary file:
185
- ```bash
186
- cat > /tmp/mops-identity.pem << 'EOF'
187
- -----BEGIN EC PRIVATE KEY-----
188
- <paste key from Bitwarden>
189
- -----END EC PRIVATE KEY-----
190
- EOF
191
- ```
192
-
193
- 2. Import into dfx:
194
- ```bash
195
- dfx identity import mops /tmp/mops-identity.pem
196
- ```
197
-
198
- 3. Delete the temporary file:
199
- ```bash
200
- rm /tmp/mops-identity.pem
201
- ```
202
-
203
- 4. Verify:
204
- ```bash
205
- dfx identity get-principal --identity mops
206
- ```
package/bundle/cli.tgz ADDED
Binary file
package/cli.ts CHANGED
@@ -301,11 +301,7 @@ program
301
301
  .command("build [canisters...]")
302
302
  .description("Build a canister")
303
303
  .addOption(new Option("--verbose", "Verbose console output"))
304
- .addOption(
305
- new Option("--output, -o <output>", "Output directory").default(
306
- DEFAULT_BUILD_OUTPUT_DIR,
307
- ),
308
- )
304
+ .addOption(new Option("--output, -o <output>", "Output directory"))
309
305
  .allowUnknownOption(true) // TODO: restrict unknown before "--"
310
306
  .action(async (canisters, options) => {
311
307
  checkConfigFile(true);
@@ -317,6 +313,7 @@ program
317
313
  });
318
314
  await build(args.length ? args : undefined, {
319
315
  ...options,
316
+ outputDir: options.output,
320
317
  extraArgs,
321
318
  });
322
319
  });
@@ -3,12 +3,13 @@ import { execSync } from "node:child_process";
3
3
  import path from "node:path";
4
4
  import fs from "node:fs";
5
5
  import { execaCommand } from "execa";
6
- import { PocketIc, PocketIcServer } from "pic-ic";
6
+ import { getRootDir } from "../mops.js";
7
7
  import {
8
- PocketIc as PocketIcMops,
9
- PocketIcServer as PocketIcServerMops,
10
- } from "pic-js-mops";
11
- import { getRootDir, readConfig } from "../mops.js";
8
+ type AnyPocketIcServer,
9
+ type AnyPocketIc,
10
+ type AnySetupCanister,
11
+ startPocketIc,
12
+ } from "../helpers/pocket-ic-client.js";
12
13
  import { createActor, idlFactory } from "../declarations/bench/index.js";
13
14
  import { toolchain } from "./toolchain/index.js";
14
15
  import { getDfxVersion } from "../helpers/get-dfx-version.js";
@@ -18,8 +19,8 @@ export class BenchReplica {
18
19
  verbose = false;
19
20
  canisters: Record<string, { cwd: string; canisterId: string; actor: any }> =
20
21
  {};
21
- pocketIcServer?: PocketIcServer | PocketIcServerMops;
22
- pocketIc?: PocketIc | PocketIcMops;
22
+ pocketIcServer?: AnyPocketIcServer;
23
+ pocketIc?: AnyPocketIc;
23
24
 
24
25
  constructor(type: "dfx" | "pocket-ic" | "dfx-pocket-ic", verbose = false) {
25
26
  this.type = type;
@@ -49,30 +50,10 @@ export class BenchReplica {
49
50
  );
50
51
  } else {
51
52
  let pocketIcBin = await toolchain.bin("pocket-ic");
52
- let config = readConfig();
53
- if (
54
- config.toolchain?.["pocket-ic"] !== "4.0.0" &&
55
- !config.toolchain?.["pocket-ic"]?.startsWith("9.")
56
- ) {
57
- console.error(
58
- "Current Mops CLI only supports pocket-ic 9.x.x and 4.0.0",
59
- );
60
- process.exit(1);
61
- }
62
- // pocket-ic 9.x.x
63
- if (config.toolchain?.["pocket-ic"]?.startsWith("9.")) {
64
- this.pocketIcServer = await PocketIcServerMops.start({
65
- binPath: pocketIcBin,
66
- });
67
- this.pocketIc = await PocketIcMops.create(this.pocketIcServer.getUrl());
68
- }
69
- // pocket-ic 4.0.0
70
- else {
71
- this.pocketIcServer = await PocketIcServer.start({
72
- binPath: pocketIcBin,
73
- });
74
- this.pocketIc = await PocketIc.create(this.pocketIcServer.getUrl());
75
- }
53
+
54
+ let pic = await startPocketIc({ binPath: pocketIcBin });
55
+ this.pocketIcServer = pic.server;
56
+ this.pocketIc = pic.client;
76
57
  }
77
58
  }
78
59
 
@@ -105,10 +86,8 @@ export class BenchReplica {
105
86
  });
106
87
  this.canisters[name] = { cwd, canisterId, actor };
107
88
  } else if (this.pocketIc) {
108
- type PocketIcSetupCanister = PocketIcMops["setupCanister"] &
109
- PocketIc["setupCanister"];
110
89
  let { canisterId, actor } = await (
111
- this.pocketIc.setupCanister as PocketIcSetupCanister
90
+ this.pocketIc.setupCanister as AnySetupCanister
112
91
  )({ idlFactory, wasm });
113
92
  this.canisters[name] = {
114
93
  cwd,
package/commands/build.ts CHANGED
@@ -6,8 +6,9 @@ import { join } from "node:path";
6
6
  import { cliError } from "../error.js";
7
7
  import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
8
8
  import { resolveCanisterConfigs } from "../helpers/resolve-canisters.js";
9
+ import { CanisterConfig, Config } from "../types.js";
9
10
  import { CustomSection, getWasmBindings } from "../wasm.js";
10
- import { getGlobalMocArgs, readConfig } from "../mops.js";
11
+ import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
11
12
  import { sourcesArgs } from "./sources.js";
12
13
  import { toolchain } from "./toolchain/index.js";
13
14
 
@@ -27,9 +28,13 @@ export async function build(
27
28
  cliError("No canisters specified to build");
28
29
  }
29
30
 
30
- let outputDir = options.outputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
31
- let mocPath = await toolchain.bin("moc", { fallback: true });
32
31
  let config = readConfig();
32
+ let configOutputDir = config.build?.outputDir
33
+ ? resolveConfigPath(config.build.outputDir)
34
+ : undefined;
35
+ let outputDir =
36
+ options.outputDir ?? configOutputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
37
+ let mocPath = await toolchain.bin("moc", { fallback: true });
33
38
  let canisters = resolveCanisterConfigs(config);
34
39
  if (!Object.keys(canisters).length) {
35
40
  cliError(`No Motoko canisters found in mops.toml configuration`);
@@ -67,6 +72,7 @@ export async function build(
67
72
  if (!motokoPath) {
68
73
  cliError(`No main file is specified for canister ${canisterName}`);
69
74
  }
75
+ motokoPath = resolveConfigPath(motokoPath);
70
76
  const wasmPath = join(outputDir, `${canisterName}.wasm`);
71
77
  let args = [
72
78
  "-c",
@@ -77,23 +83,10 @@ export async function build(
77
83
  ...(await sourcesArgs()).flat(),
78
84
  ...getGlobalMocArgs(config),
79
85
  ];
80
- if (config.build?.args) {
81
- if (typeof config.build.args === "string") {
82
- cliError(
83
- `[build] config 'args' should be an array of strings in mops.toml config file`,
84
- );
85
- }
86
- args.push(...config.build.args);
87
- }
88
- if (canister.args) {
89
- if (typeof canister.args === "string") {
90
- cliError(
91
- `Canister config 'args' should be an array of strings for canister ${canisterName}`,
92
- );
93
- }
94
- args.push(...canister.args);
95
- }
96
- args.push(...(options.extraArgs ?? []));
86
+ args.push(
87
+ ...collectExtraArgs(config, canister, canisterName, options.extraArgs),
88
+ );
89
+
97
90
  const isPublicCandid = true; // always true for now to reduce corner cases
98
91
  const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
99
92
  if (isPublicCandid) {
@@ -129,13 +122,15 @@ export async function build(
129
122
  }
130
123
 
131
124
  const generatedDidPath = join(outputDir, `${canisterName}.did`);
132
- if (canister.candid) {
133
- const originalCandidPath = canister.candid;
125
+ const resolvedCandidPath = canister.candid
126
+ ? resolveConfigPath(canister.candid)
127
+ : null;
134
128
 
129
+ if (resolvedCandidPath) {
135
130
  try {
136
131
  const compatible = await isCandidCompatible(
137
132
  generatedDidPath,
138
- originalCandidPath,
133
+ resolvedCandidPath,
139
134
  );
140
135
 
141
136
  if (!compatible) {
@@ -160,7 +155,7 @@ export async function build(
160
155
 
161
156
  options.verbose &&
162
157
  console.log(chalk.gray(`Adding metadata to ${wasmPath}`));
163
- const candidPath = canister.candid ?? generatedDidPath;
158
+ const candidPath = resolvedCandidPath ?? generatedDidPath;
164
159
  const candidText = await readFile(candidPath, "utf-8");
165
160
  const customSections: CustomSection[] = [
166
161
  { name: `${candidVisibility} candid:service`, data: candidText },
@@ -193,3 +188,54 @@ export async function build(
193
188
  ),
194
189
  );
195
190
  }
191
+
192
+ const managedFlags: Record<string, string> = {
193
+ "-o": "use [build].outputDir in mops.toml or --output flag instead",
194
+ "-c": "this flag is always set by mops build",
195
+ "--idl": "this flag is always set by mops build",
196
+ "--public-metadata": "this flag is managed by mops build",
197
+ };
198
+
199
+ function collectExtraArgs(
200
+ config: Config,
201
+ canister: CanisterConfig,
202
+ canisterName: string,
203
+ extraArgs?: string[],
204
+ ): string[] {
205
+ const args: string[] = [];
206
+
207
+ if (config.build?.args) {
208
+ if (typeof config.build.args === "string") {
209
+ cliError(
210
+ `[build] config 'args' should be an array of strings in mops.toml config file`,
211
+ );
212
+ }
213
+ args.push(...config.build.args);
214
+ }
215
+ if (canister.args) {
216
+ if (typeof canister.args === "string") {
217
+ cliError(
218
+ `Canister config 'args' should be an array of strings for canister ${canisterName}`,
219
+ );
220
+ }
221
+ args.push(...canister.args);
222
+ }
223
+ if (extraArgs) {
224
+ args.push(...extraArgs);
225
+ }
226
+
227
+ const warned = new Set<string>();
228
+ for (const arg of args) {
229
+ const hint = managedFlags[arg];
230
+ if (hint && !warned.has(arg)) {
231
+ warned.add(arg);
232
+ console.warn(
233
+ chalk.yellow(
234
+ `Warning: '${arg}' in args for canister ${canisterName} may conflict with mops build — ${hint}`,
235
+ ),
236
+ );
237
+ }
238
+ }
239
+
240
+ return args;
241
+ }
@@ -4,7 +4,7 @@ import { rename, rm } from "node:fs/promises";
4
4
  import chalk from "chalk";
5
5
  import { execa } from "execa";
6
6
  import { cliError } from "../error.js";
7
- import { getGlobalMocArgs, readConfig } from "../mops.js";
7
+ import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
8
8
  import { resolveSingleCanister } from "../helpers/resolve-canisters.js";
9
9
  import { sourcesArgs } from "./sources.js";
10
10
  import { toolchain } from "./toolchain/index.js";
@@ -33,7 +33,7 @@ export async function checkStable(
33
33
 
34
34
  await runStableCheck({
35
35
  oldFile,
36
- canisterMain: canister.main,
36
+ canisterMain: resolveConfigPath(canister.main),
37
37
  canisterName: name,
38
38
  mocPath,
39
39
  globalMocArgs,
package/commands/check.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { relative } from "node:path";
1
+ import path from "node:path";
2
2
  import { existsSync } from "node:fs";
3
3
  import chalk from "chalk";
4
4
  import { execa } from "execa";
5
5
  import { cliError } from "../error.js";
6
- import { getGlobalMocArgs, readConfig } from "../mops.js";
6
+ import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
7
7
  import { autofixMotoko } from "../helpers/autofix-motoko.js";
8
8
  import { getMocSemVer } from "../helpers/get-moc-version.js";
9
9
  import {
@@ -36,7 +36,7 @@ export async function check(
36
36
  const config = readConfig();
37
37
 
38
38
  if (fileList.length === 0) {
39
- fileList = resolveCanisterEntrypoints(config);
39
+ fileList = resolveCanisterEntrypoints(config).map(resolveConfigPath);
40
40
  }
41
41
 
42
42
  if (fileList.length === 0) {
@@ -86,7 +86,7 @@ export async function check(
86
86
  for (const [file, codes] of fixResult.fixedFiles) {
87
87
  const unique = [...new Set(codes)].sort();
88
88
  const n = codes.length;
89
- const rel = relative(process.cwd(), file);
89
+ const rel = path.relative(process.cwd(), file);
90
90
  console.log(
91
91
  chalk.green(
92
92
  `Fixed ${rel} (${n} ${n === 1 ? "fix" : "fixes"}: ${unique.join(", ")})`,
@@ -144,12 +144,13 @@ export async function check(
144
144
  cliError(`No main file specified for canister '${name}' in mops.toml`);
145
145
  }
146
146
 
147
- if (!existsSync(stableConfig.path)) {
147
+ const stablePath = resolveConfigPath(stableConfig.path);
148
+ if (!existsSync(stablePath)) {
148
149
  if (stableConfig.skipIfMissing) {
149
150
  continue;
150
151
  }
151
152
  cliError(
152
- `Deployed file not found: ${stableConfig.path} (canister '${name}')\n` +
153
+ `Deployed file not found: ${stablePath} (canister '${name}')\n` +
153
154
  "Set skipIfMissing = true in [canisters." +
154
155
  name +
155
156
  ".check-stable] to skip this check when the file is missing.",
@@ -157,8 +158,8 @@ export async function check(
157
158
  }
158
159
 
159
160
  await runStableCheck({
160
- oldFile: stableConfig.path,
161
- canisterMain: canister.main,
161
+ oldFile: stablePath,
162
+ canisterMain: resolveConfigPath(canister.main),
162
163
  canisterName: name,
163
164
  mocPath,
164
165
  globalMocArgs,
@@ -11,14 +11,14 @@ import { spawn as spawnAsync } from "promisify-child-process";
11
11
 
12
12
  import { IDL } from "@icp-sdk/core/candid";
13
13
  import { Actor, HttpAgent } from "@icp-sdk/core/agent";
14
- import { PocketIc, PocketIcServer } from "pic-ic";
15
- import {
16
- PocketIc as PocketIcMops,
17
- PocketIcServer as PocketIcServerMops,
18
- } from "pic-js-mops";
19
14
  import chalk from "chalk";
20
15
 
21
- import { readConfig } from "../mops.js";
16
+ import {
17
+ type AnyPocketIcServer,
18
+ type AnyPocketIc,
19
+ type AnySetupCanister,
20
+ startPocketIc,
21
+ } from "../helpers/pocket-ic-client.js";
22
22
  import { toolchain } from "./toolchain/index.js";
23
23
  import { getDfxVersion } from "../helpers/get-dfx-version.js";
24
24
 
@@ -36,8 +36,8 @@ export class Replica {
36
36
  string,
37
37
  { cwd: string; canisterId: string; actor: any; stream: PassThrough }
38
38
  > = {};
39
- pocketIcServer?: PocketIcServer | PocketIcServerMops;
40
- pocketIc?: PocketIc | PocketIcMops;
39
+ pocketIcServer?: AnyPocketIcServer;
40
+ pocketIc?: AnyPocketIc;
41
41
  dfxProcess?: ChildProcessWithoutNullStreams;
42
42
  dir: string = ""; // absolute path (/.../.mops/.test/)
43
43
  ttl = 60;
@@ -114,37 +114,14 @@ export class Replica {
114
114
  } else {
115
115
  let pocketIcBin = await toolchain.bin("pocket-ic");
116
116
 
117
- let config = readConfig();
118
- if (
119
- config.toolchain?.["pocket-ic"] !== "4.0.0" &&
120
- !config.toolchain?.["pocket-ic"]?.startsWith("9.")
121
- ) {
122
- console.error(
123
- "Current Mops CLI only supports pocket-ic 9.x.x and 4.0.0",
124
- );
125
- process.exit(1);
126
- }
127
-
128
- // pocket-ic 9.x.x
129
- if (config.toolchain?.["pocket-ic"]?.startsWith("9.")) {
130
- this.pocketIcServer = await PocketIcServerMops.start({
131
- showRuntimeLogs: false,
132
- showCanisterLogs: false,
133
- binPath: pocketIcBin,
134
- ttl: this.ttl,
135
- });
136
- this.pocketIc = await PocketIcMops.create(this.pocketIcServer.getUrl());
137
- }
138
- // pocket-ic 4.0.0
139
- else {
140
- this.pocketIcServer = await PocketIcServer.start({
141
- showRuntimeLogs: false,
142
- showCanisterLogs: false,
143
- binPath: pocketIcBin,
144
- ttl: this.ttl,
145
- });
146
- this.pocketIc = await PocketIc.create(this.pocketIcServer.getUrl());
147
- }
117
+ let pic = await startPocketIc({
118
+ binPath: pocketIcBin,
119
+ showRuntimeLogs: false,
120
+ showCanisterLogs: false,
121
+ ttl: this.ttl,
122
+ });
123
+ this.pocketIcServer = pic.server;
124
+ this.pocketIc = pic.client;
148
125
 
149
126
  // process canister logs
150
127
  this._attachCanisterLogHandler(
@@ -305,10 +282,8 @@ export class Replica {
305
282
  stream: new PassThrough(),
306
283
  };
307
284
  } else if (this.pocketIc) {
308
- type PocketIcSetupCanister = PocketIcMops["setupCanister"] &
309
- PocketIc["setupCanister"];
310
285
  let { canisterId, actor } = await (
311
- this.pocketIc.setupCanister as PocketIcSetupCanister
286
+ this.pocketIc.setupCanister as AnySetupCanister
312
287
  )({
313
288
  wasm,
314
289
  idlFactory,
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { getNetwork } from "./api/network.js";
7
7
  import { cacheSize, cleanCache, show } from "./cache.js";
8
8
  import { add } from "./commands/add.js";
9
9
  import { bench } from "./commands/bench.js";
10
- import { build, DEFAULT_BUILD_OUTPUT_DIR } from "./commands/build.js";
10
+ import { build } from "./commands/build.js";
11
11
  import { bump } from "./commands/bump.js";
12
12
  import { check } from "./commands/check.js";
13
13
  import { checkCandid } from "./commands/check-candid.js";
@@ -236,7 +236,7 @@ program
236
236
  .command("build [canisters...]")
237
237
  .description("Build a canister")
238
238
  .addOption(new Option("--verbose", "Verbose console output"))
239
- .addOption(new Option("--output, -o <output>", "Output directory").default(DEFAULT_BUILD_OUTPUT_DIR))
239
+ .addOption(new Option("--output, -o <output>", "Output directory"))
240
240
  .allowUnknownOption(true) // TODO: restrict unknown before "--"
241
241
  .action(async (canisters, options) => {
242
242
  checkConfigFile(true);
@@ -248,6 +248,7 @@ program
248
248
  });
249
249
  await build(args.length ? args : undefined, {
250
250
  ...options,
251
+ outputDir: options.output,
251
252
  extraArgs,
252
253
  });
253
254
  });