ic-mops 2.3.1 → 2.3.2

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 CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## Next
4
4
 
5
+ ## 2.3.2
6
+ - Fix `mops check`, `mops build`, and `mops check-stable` failing to find canister entrypoints when run from a subdirectory
7
+
5
8
  ## 2.3.1
6
9
  - Fix `mops build` and `mops check-candid` failing with "Wasm bindings have not been set" when installed via `npm i -g ic-mops`
7
10
 
package/RELEASE.md CHANGED
@@ -2,20 +2,9 @@
2
2
 
3
3
  ## Prerequisites
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
5
  ### Docker
17
6
 
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.
7
+ Docker (or OrbStack) must be installed and running. The on-chain release step builds the CLI inside a Docker container (`zenvoich/mops-builder:1.1.0`) for reproducibility.
19
8
 
20
9
  ### dfx
21
10
 
@@ -23,13 +12,7 @@ Docker (or OrbStack) must be installed and running. The CLI build happens inside
23
12
 
24
13
  ## Release Steps
25
14
 
26
- ### 1. Install dependencies
27
-
28
- ```bash
29
- cd cli && bun install
30
- ```
31
-
32
- ### 2. Update changelog
15
+ ### 1. Update changelog
33
16
 
34
17
  Move items from the `## Next` section in `CHANGELOG.md` into a new version heading:
35
18
 
@@ -41,28 +24,19 @@ Move items from the `## Next` section in `CHANGELOG.md` into a new version headi
41
24
  - Change 2
42
25
  ```
43
26
 
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
27
+ The heading must contain the exact version string — the release workflow parses it to extract release notes for the GitHub Release.
55
28
 
56
- Use `--no-git-tag-version` since the version bump will be committed as part of the PR (not directly on `main`):
29
+ ### 2. Bump version
57
30
 
58
31
  ```bash
59
32
  cd cli
60
33
  npm version minor --no-git-tag-version # or: patch / major
61
34
  ```
62
35
 
63
- ### 5. Commit, push, and open PR
36
+ ### 3. Create a release branch and PR
64
37
 
65
38
  ```bash
39
+ git checkout -b <username>/release-X.Y.Z
66
40
  git add cli/CHANGELOG.md cli/package.json cli/package-lock.json
67
41
  git commit -m "release: CLI vX.Y.Z"
68
42
  git push -u origin <username>/release-X.Y.Z
@@ -71,41 +45,26 @@ gh pr create --title "release: CLI vX.Y.Z" --body "..."
71
45
 
72
46
  Wait for CI to pass, then merge the PR.
73
47
 
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.
48
+ ### 4. Tag and push
77
49
 
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.
50
+ After the PR is merged to `main`:
79
51
 
80
- Build the same version locally:
81
52
  ```bash
82
- cd cli
83
- MOPS_VERSION=0.0.0 ./build.sh
53
+ git checkout main && git pull
54
+ git tag cli-vX.Y.Z
55
+ git push origin cli-vX.Y.Z
84
56
  ```
85
57
 
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`
58
+ This triggers the [`release.yml`](../.github/workflows/release.yml) workflow which automatically:
59
+ 1. Validates the tag is on `main` and version matches `package.json`
60
+ 2. Builds the CLI tarball in Docker (reproducible build)
61
+ 3. Computes and reports the SHA256 hash (visible in the workflow Step Summary)
62
+ 4. Publishes to npm via OIDC trusted publishing
63
+ 5. Creates a GitHub Release with the tarball attached and changelog as release notes
92
64
 
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.
97
-
98
- ### 7. Publish to npm
99
-
100
- After the PR is merged and the build hash is verified:
101
-
102
- ```bash
103
- git checkout main && git pull
104
- cd cli
105
- npm publish
106
- ```
65
+ Monitor the workflow run at [Actions Release CLI](https://github.com/caffeinelabs/mops/actions/workflows/release.yml).
107
66
 
108
- ### 8. Prepare on-chain release
67
+ ### 5. Prepare on-chain release
109
68
 
110
69
  Run from the **repo root** (not `cli/`), with Docker running:
111
70
 
@@ -122,7 +81,7 @@ This runs `cli/release-cli.ts`, which:
122
81
  6. Updates `cli-releases/tags/latest` to the new version
123
82
  7. Updates `cli-releases/releases.json` with metadata (timestamp, size, hash, commit hash, download URL, release notes)
124
83
 
125
- ### 9. Deploy the canister
84
+ ### 6. Deploy the canister
126
85
 
127
86
  ```bash
128
87
  dfx deploy --network ic --no-wallet cli --identity mops
@@ -130,7 +89,7 @@ dfx deploy --network ic --no-wallet cli --identity mops
130
89
 
131
90
  This deploys the `cli-releases` canister (serving `cli.mops.one`) to the Internet Computer mainnet.
132
91
 
133
- ### 10. Deploy the docs canister
92
+ ### 7. Deploy the docs canister
134
93
 
135
94
  ```bash
136
95
  dfx deploy --network ic --no-wallet docs --identity mops
@@ -138,9 +97,9 @@ dfx deploy --network ic --no-wallet docs --identity mops
138
97
 
139
98
  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
99
 
141
- ### 11. Commit and push release artifacts
100
+ ### 8. Commit and push release artifacts
142
101
 
143
- Step 8 generates files in `cli-releases/` that must be committed and pushed:
102
+ Step 5 generates files in `cli-releases/` that must be committed and pushed:
144
103
 
145
104
  ```bash
146
105
  git add cli-releases/
@@ -159,7 +118,7 @@ Merge this PR after approval.
159
118
 
160
119
  ## Verify build
161
120
 
162
- Anyone can verify a released version by rebuilding from source:
121
+ Anyone can verify a released version by rebuilding from source. The SHA256 hash and verification instructions are included in each [GitHub Release](https://github.com/caffeinelabs/mops/releases).
163
122
 
164
123
  ```bash
165
124
  cd cli
package/bundle/cli.tgz ADDED
Binary file
package/commands/build.ts CHANGED
@@ -7,7 +7,7 @@ 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
9
  import { CustomSection, getWasmBindings } from "../wasm.js";
10
- import { getGlobalMocArgs, readConfig } from "../mops.js";
10
+ import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
11
11
  import { sourcesArgs } from "./sources.js";
12
12
  import { toolchain } from "./toolchain/index.js";
13
13
 
@@ -67,6 +67,7 @@ export async function build(
67
67
  if (!motokoPath) {
68
68
  cliError(`No main file is specified for canister ${canisterName}`);
69
69
  }
70
+ motokoPath = resolveConfigPath(motokoPath);
70
71
  const wasmPath = join(outputDir, `${canisterName}.wasm`);
71
72
  let args = [
72
73
  "-c",
@@ -129,13 +130,15 @@ export async function build(
129
130
  }
130
131
 
131
132
  const generatedDidPath = join(outputDir, `${canisterName}.did`);
132
- if (canister.candid) {
133
- const originalCandidPath = canister.candid;
133
+ const resolvedCandidPath = canister.candid
134
+ ? resolveConfigPath(canister.candid)
135
+ : null;
134
136
 
137
+ if (resolvedCandidPath) {
135
138
  try {
136
139
  const compatible = await isCandidCompatible(
137
140
  generatedDidPath,
138
- originalCandidPath,
141
+ resolvedCandidPath,
139
142
  );
140
143
 
141
144
  if (!compatible) {
@@ -160,7 +163,7 @@ export async function build(
160
163
 
161
164
  options.verbose &&
162
165
  console.log(chalk.gray(`Adding metadata to ${wasmPath}`));
163
- const candidPath = canister.candid ?? generatedDidPath;
166
+ const candidPath = resolvedCandidPath ?? generatedDidPath;
164
167
  const candidText = await readFile(candidPath, "utf-8");
165
168
  const customSections: CustomSection[] = [
166
169
  { name: `${candidVisibility} candid:service`, data: candidText },
@@ -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,
@@ -7,7 +7,7 @@ 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
9
  import { getWasmBindings } from "../wasm.js";
10
- import { getGlobalMocArgs, readConfig } from "../mops.js";
10
+ import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
11
11
  import { sourcesArgs } from "./sources.js";
12
12
  import { toolchain } from "./toolchain/index.js";
13
13
  export const DEFAULT_BUILD_OUTPUT_DIR = ".mops/.build";
@@ -45,6 +45,7 @@ export async function build(canisterNames, options) {
45
45
  if (!motokoPath) {
46
46
  cliError(`No main file is specified for canister ${canisterName}`);
47
47
  }
48
+ motokoPath = resolveConfigPath(motokoPath);
48
49
  const wasmPath = join(outputDir, `${canisterName}.wasm`);
49
50
  let args = [
50
51
  "-c",
@@ -98,10 +99,12 @@ export async function build(canisterNames, options) {
98
99
  console.log(result.stdout);
99
100
  }
100
101
  const generatedDidPath = join(outputDir, `${canisterName}.did`);
101
- if (canister.candid) {
102
- const originalCandidPath = canister.candid;
102
+ const resolvedCandidPath = canister.candid
103
+ ? resolveConfigPath(canister.candid)
104
+ : null;
105
+ if (resolvedCandidPath) {
103
106
  try {
104
- const compatible = await isCandidCompatible(generatedDidPath, originalCandidPath);
107
+ const compatible = await isCandidCompatible(generatedDidPath, resolvedCandidPath);
105
108
  if (!compatible) {
106
109
  cliError(`Candid compatibility check failed for canister ${canisterName}`);
107
110
  }
@@ -115,7 +118,7 @@ export async function build(canisterNames, options) {
115
118
  }
116
119
  options.verbose &&
117
120
  console.log(chalk.gray(`Adding metadata to ${wasmPath}`));
118
- const candidPath = canister.candid ?? generatedDidPath;
121
+ const candidPath = resolvedCandidPath ?? generatedDidPath;
119
122
  const candidText = await readFile(candidPath, "utf-8");
120
123
  const customSections = [
121
124
  { name: `${candidVisibility} candid:service`, data: candidText },
@@ -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";
@@ -19,7 +19,7 @@ export async function checkStable(oldFile, canisterName, options = {}) {
19
19
  const globalMocArgs = getGlobalMocArgs(config);
20
20
  await runStableCheck({
21
21
  oldFile,
22
- canisterMain: canister.main,
22
+ canisterMain: resolveConfigPath(canister.main),
23
23
  canisterName: name,
24
24
  mocPath,
25
25
  globalMocArgs,
@@ -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 { resolveCanisterConfigs, resolveCanisterEntrypoints, } from "../helpers/resolve-canisters.js";
@@ -19,7 +19,7 @@ export async function check(files, options = {}) {
19
19
  let fileList = Array.isArray(files) ? files : files ? [files] : [];
20
20
  const config = readConfig();
21
21
  if (fileList.length === 0) {
22
- fileList = resolveCanisterEntrypoints(config);
22
+ fileList = resolveCanisterEntrypoints(config).map(resolveConfigPath);
23
23
  }
24
24
  if (fileList.length === 0) {
25
25
  cliError("No Motoko files specified and no canisters defined in mops.toml.\n" +
@@ -55,7 +55,7 @@ export async function check(files, options = {}) {
55
55
  for (const [file, codes] of fixResult.fixedFiles) {
56
56
  const unique = [...new Set(codes)].sort();
57
57
  const n = codes.length;
58
- const rel = relative(process.cwd(), file);
58
+ const rel = path.relative(process.cwd(), file);
59
59
  console.log(chalk.green(`Fixed ${rel} (${n} ${n === 1 ? "fix" : "fixes"}: ${unique.join(", ")})`));
60
60
  }
61
61
  const fileCount = fixResult.fixedFiles.size;
@@ -96,18 +96,19 @@ export async function check(files, options = {}) {
96
96
  if (!canister.main) {
97
97
  cliError(`No main file specified for canister '${name}' in mops.toml`);
98
98
  }
99
- if (!existsSync(stableConfig.path)) {
99
+ const stablePath = resolveConfigPath(stableConfig.path);
100
+ if (!existsSync(stablePath)) {
100
101
  if (stableConfig.skipIfMissing) {
101
102
  continue;
102
103
  }
103
- cliError(`Deployed file not found: ${stableConfig.path} (canister '${name}')\n` +
104
+ cliError(`Deployed file not found: ${stablePath} (canister '${name}')\n` +
104
105
  "Set skipIfMissing = true in [canisters." +
105
106
  name +
106
107
  ".check-stable] to skip this check when the file is missing.");
107
108
  }
108
109
  await runStableCheck({
109
- oldFile: stableConfig.path,
110
- canisterMain: canister.main,
110
+ oldFile: stablePath,
111
+ canisterMain: resolveConfigPath(canister.main),
111
112
  canisterName: name,
112
113
  mocPath,
113
114
  globalMocArgs,
package/dist/mops.d.ts CHANGED
@@ -11,6 +11,11 @@ export declare function setNetwork(network: string): void;
11
11
  export declare let getIdentity: () => Promise<Identity | undefined>;
12
12
  export declare function getClosestConfigFile(dir?: string): string;
13
13
  export declare function getRootDir(): string;
14
+ /**
15
+ * Resolve a path from mops.toml config (relative to project root)
16
+ * into a path relative to the current working directory.
17
+ */
18
+ export declare function resolveConfigPath(configPath: string): string;
14
19
  export declare function checkConfigFile(exit?: boolean): boolean;
15
20
  export declare function progressBar(step: number, total: number): string;
16
21
  export declare function parseGithubURL(href: string): {
package/dist/mops.js CHANGED
@@ -87,6 +87,13 @@ export function getRootDir() {
87
87
  }
88
88
  return path.dirname(configFile);
89
89
  }
90
+ /**
91
+ * Resolve a path from mops.toml config (relative to project root)
92
+ * into a path relative to the current working directory.
93
+ */
94
+ export function resolveConfigPath(configPath) {
95
+ return path.relative(process.cwd(), path.resolve(getRootDir(), configPath));
96
+ }
90
97
  export function checkConfigFile(exit = false) {
91
98
  let configFile = getClosestConfigFile();
92
99
  if (!configFile) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "bin/mops.js",
@@ -38,6 +38,13 @@ describe("check", () => {
38
38
  const cwd = path.join(import.meta.dirname, "check/canisters");
39
39
  await cliSnapshot(["check"], { cwd }, 0);
40
40
  });
41
+ test("canister entrypoint resolved relative to config root when run from subdirectory", async () => {
42
+ const fixtureRoot = path.join(import.meta.dirname, "check/canisters-subdir");
43
+ const subdir = path.join(fixtureRoot, "src/backend");
44
+ const result = await cli(["check"], { cwd: subdir });
45
+ expect(result.exitCode).toBe(0);
46
+ expect(result.stdout).toMatch(/✓/);
47
+ });
41
48
  test("[moc] args applied when using canister fallback", async () => {
42
49
  const cwd = path.join(import.meta.dirname, "check/canisters-moc-args");
43
50
  const result = await cli(["check"], { cwd });
Binary file
@@ -1,8 +1,8 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
- export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
5
4
  export const add_custom_sections: (a: number, b: number, c: any) => [number, number, number, number];
5
+ export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
6
6
  export const __wbindgen_malloc: (a: number, b: number) => number;
7
7
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
8
8
  export const __wbindgen_exn_store: (a: number) => void;
@@ -9,8 +9,8 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
9
9
 
10
10
  export interface InitOutput {
11
11
  readonly memory: WebAssembly.Memory;
12
- readonly is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
13
12
  readonly add_custom_sections: (a: number, b: number, c: any) => [number, number, number, number];
13
+ readonly is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
14
14
  readonly __wbindgen_malloc: (a: number, b: number) => number;
15
15
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
16
16
  readonly __wbindgen_exn_store: (a: number) => void;
Binary file
@@ -1,8 +1,8 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
- export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
5
4
  export const add_custom_sections: (a: number, b: number, c: any) => [number, number, number, number];
5
+ export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
6
6
  export const __wbindgen_malloc: (a: number, b: number) => number;
7
7
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
8
8
  export const __wbindgen_exn_store: (a: number) => void;
package/mops.ts CHANGED
@@ -100,6 +100,14 @@ export function getRootDir() {
100
100
  return path.dirname(configFile);
101
101
  }
102
102
 
103
+ /**
104
+ * Resolve a path from mops.toml config (relative to project root)
105
+ * into a path relative to the current working directory.
106
+ */
107
+ export function resolveConfigPath(configPath: string): string {
108
+ return path.relative(process.cwd(), path.resolve(getRootDir(), configPath));
109
+ }
110
+
103
111
  export function checkConfigFile(exit = false) {
104
112
  let configFile = getClosestConfigFile();
105
113
  if (!configFile) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/bin/mops.js",
@@ -0,0 +1,8 @@
1
+ [toolchain]
2
+ moc = "1.3.0"
3
+
4
+ [moc]
5
+ args = ["--default-persistent-actors"]
6
+
7
+ [canisters.backend]
8
+ main = "src/backend/main.mo"
@@ -0,0 +1,5 @@
1
+ actor {
2
+ public func hello() : async Text {
3
+ "Hello, World!";
4
+ };
5
+ };
@@ -54,6 +54,17 @@ describe("check", () => {
54
54
  await cliSnapshot(["check"], { cwd }, 0);
55
55
  });
56
56
 
57
+ test("canister entrypoint resolved relative to config root when run from subdirectory", async () => {
58
+ const fixtureRoot = path.join(
59
+ import.meta.dirname,
60
+ "check/canisters-subdir",
61
+ );
62
+ const subdir = path.join(fixtureRoot, "src/backend");
63
+ const result = await cli(["check"], { cwd: subdir });
64
+ expect(result.exitCode).toBe(0);
65
+ expect(result.stdout).toMatch(/✓/);
66
+ });
67
+
57
68
  test("[moc] args applied when using canister fallback", async () => {
58
69
  const cwd = path.join(import.meta.dirname, "check/canisters-moc-args");
59
70
  const result = await cli(["check"], { cwd });
Binary file
@@ -1,8 +1,8 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
- export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
5
4
  export const add_custom_sections: (a: number, b: number, c: any) => [number, number, number, number];
5
+ export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
6
6
  export const __wbindgen_malloc: (a: number, b: number) => number;
7
7
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
8
8
  export const __wbindgen_exn_store: (a: number) => void;
@@ -9,8 +9,8 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
9
9
 
10
10
  export interface InitOutput {
11
11
  readonly memory: WebAssembly.Memory;
12
- readonly is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
13
12
  readonly add_custom_sections: (a: number, b: number, c: any) => [number, number, number, number];
13
+ readonly is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
14
14
  readonly __wbindgen_malloc: (a: number, b: number) => number;
15
15
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
16
16
  readonly __wbindgen_exn_store: (a: number) => void;
Binary file
@@ -1,8 +1,8 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
- export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
5
4
  export const add_custom_sections: (a: number, b: number, c: any) => [number, number, number, number];
5
+ export const is_candid_compatible: (a: number, b: number, c: number, d: number) => number;
6
6
  export const __wbindgen_malloc: (a: number, b: number) => number;
7
7
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
8
8
  export const __wbindgen_exn_store: (a: number) => void;