Skip to content

Commit 85f40b6

Browse files
Kamirusrvanasa
andauthored
feat(build): generate .most file via --stable-types (#456)
## Summary - `mops build` now always passes `--stable-types` to `moc`, generating a `<canister>.most` (Motoko stable types) file alongside `.wasm` and `.did` for every canister - `--stable-types` is registered in `managedFlags` so users get a clear warning if they try to pass it manually via `[build].args` or `--` extra args ## Test plan - `custom output path via config outputDir` — asserts `main.most` is produced in the configured output dir - `--output CLI option` — asserts `foo.most` is produced alongside `foo.wasm` and `foo.did` - `warns when args contain managed flags` — now also passes `--stable-types` to cover the duplicate-flag warning; snapshot updated - Verbose-mode snapshots (`build ok 1`, `build error 1`) updated to reflect `--stable-types` in the logged moc args ## Notable decisions - No changes to how the `.most` file is consumed — it is left in the output directory for tooling (e.g. upgrade compatibility checks) to pick up - Flag placement mirrors `--idl`: immediately after `-c`, before `-o` Made with [Cursor](https://cursor.com) --------- Co-authored-by: Ryan Vandersmith <ryanvandersmith@gmail.com>
1 parent 2147e41 commit 85f40b6

6 files changed

Lines changed: 41 additions & 7 deletions

File tree

cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Next
44

5+
- `mops build` now generates a `.most` (Motoko stable types) file alongside `.wasm` and `.did` for each canister; the `.most` file can be passed directly to `mops check-stable` to verify upgrade compatibility
56
- `mops.lock` is now created automatically the first time dependencies are installed — no need to run `mops i --lock update` once to opt in. Triggered by `mops install`, `mops add`, `mops remove`, `mops update`, `mops sync`, and `mops init` (when it installs dependencies). Applications should commit `mops.lock`; library authors should add it to `.gitignore`.
67

78
## 2.7.0

cli/commands/build.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,11 @@ export async function build(
6969
}
7070
motokoPath = resolveConfigPath(motokoPath);
7171
const wasmPath = join(outputDir, `${canisterName}.wasm`);
72+
const mostPath = join(outputDir, `${canisterName}.most`);
7273
let args = [
7374
"-c",
7475
"--idl",
76+
"--stable-types",
7577
"-o",
7678
wasmPath,
7779
motokoPath,
@@ -116,6 +118,9 @@ export async function build(
116118
console.log(result.stdout);
117119
}
118120

121+
options.verbose &&
122+
console.log(chalk.gray(`Stable types written to ${mostPath}`));
123+
119124
const generatedDidPath = join(outputDir, `${canisterName}.did`);
120125
const resolvedCandidPath = canister.candid
121126
? resolveConfigPath(canister.candid)
@@ -188,6 +193,7 @@ const managedFlags: Record<string, string> = {
188193
"-o": "use [build].outputDir in mops.toml or --output flag instead",
189194
"-c": "this flag is always set by mops build",
190195
"--idl": "this flag is always set by mops build",
196+
"--stable-types": "this flag is always set by mops build",
191197
"--public-metadata": "this flag is managed by mops build",
192198
};
193199

cli/tests/__snapshots__/build.test.ts.snap

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ exports[`build error 1`] = `
55
"exitCode": 0,
66
"stderr": "",
77
"stdout": "build canister foo
8-
moc-wrapper ["-c","--idl","-o",".mops/.build/foo.wasm","src/Foo.mo","--package","core",".mops/core@1.0.0/src","--release","--public-metadata","candid:service","--public-metadata","candid:args"]
8+
moc-wrapper ["-c","--idl","--stable-types","-o",".mops/.build/foo.wasm","src/Foo.mo","--package","core",".mops/core@1.0.0/src","--release","--public-metadata","candid:service","--public-metadata","candid:args"]
9+
Stable types written to .mops/.build/foo.most
910
Adding metadata to .mops/.build/foo.wasm
1011
1112
Built 1 canister successfully",
@@ -34,10 +35,12 @@ exports[`build ok 1`] = `
3435
"exitCode": 0,
3536
"stderr": "",
3637
"stdout": "build canister foo
37-
moc-wrapper ["-c","--idl","-o",".mops/.build/foo.wasm","src/Foo.mo","--package","core",".mops/core@1.0.0/src","--release","--public-metadata","candid:service","--public-metadata","candid:args"]
38+
moc-wrapper ["-c","--idl","--stable-types","-o",".mops/.build/foo.wasm","src/Foo.mo","--package","core",".mops/core@1.0.0/src","--release","--public-metadata","candid:service","--public-metadata","candid:args"]
39+
Stable types written to .mops/.build/foo.most
3840
Adding metadata to .mops/.build/foo.wasm
3941
build canister bar
40-
moc-wrapper ["-c","--idl","-o",".mops/.build/bar.wasm","src/Bar.mo","--package","core",".mops/core@1.0.0/src","--release","--incremental-gc","--public-metadata","candid:service","--public-metadata","candid:args"]
42+
moc-wrapper ["-c","--idl","--stable-types","-o",".mops/.build/bar.wasm","src/Bar.mo","--package","core",".mops/core@1.0.0/src","--release","--incremental-gc","--public-metadata","candid:service","--public-metadata","candid:args"]
43+
Stable types written to .mops/.build/bar.most
4144
Candid compatibility check passed for canister bar
4245
Adding metadata to .mops/.build/bar.wasm
4346
@@ -82,6 +85,7 @@ exports[`build warns when args contain managed flags 1`] = `
8285
"stderr": "Warning: '-o' in args for canister foo may conflict with mops build — use [build].outputDir in mops.toml or --output flag instead
8386
Warning: '-c' in args for canister foo may conflict with mops buildthis flag is always set by mops build
8487
Warning: '--idl' in args for canister foo may conflict with mops buildthis flag is always set by mops build
88+
Warning: '--stable-types' in args for canister foo may conflict with mops buildthis flag is always set by mops build
8589
Error while compiling canister foo
8690
ENOENT: no such file or directory, open '.mops/.build/foo.did'",
8791
"stdout": "build canister foo",

cli/tests/build.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,15 @@ describe("build", () => {
5050
const customOut = path.join(cwd, "custom-out");
5151
const customWasm = path.join(customOut, "main.wasm");
5252
const customDid = path.join(customOut, "main.did");
53+
const customMost = path.join(customOut, "main.most");
5354
const defaultDid = path.join(cwd, ".mops/.build/main.did");
5455

5556
try {
5657
const result = await cli(["build"], { cwd });
5758
expect(result.exitCode).toBe(0);
5859
expect(existsSync(customWasm)).toBe(true);
5960
expect(existsSync(customDid)).toBe(true);
61+
expect(existsSync(customMost)).toBe(true);
6062
expect(existsSync(defaultDid)).toBe(false);
6163
} finally {
6264
cleanFixture(cwd, customOut);
@@ -76,6 +78,7 @@ describe("build", () => {
7678
expect(result.exitCode).toBe(0);
7779
expect(existsSync(path.join(outputDir, "foo.wasm"))).toBe(true);
7880
expect(existsSync(path.join(outputDir, "foo.did"))).toBe(true);
81+
expect(existsSync(path.join(outputDir, "foo.most"))).toBe(true);
7982
} finally {
8083
cleanFixture(cwd, outputDir);
8184
}
@@ -88,7 +91,7 @@ describe("build", () => {
8891

8992
try {
9093
await cliSnapshot(
91-
["build", "foo", "--", "-o", "x", "-c", "--idl"],
94+
["build", "foo", "--", "-o", "x", "-c", "--idl", "--stable-types"],
9295
{ cwd },
9396
1,
9497
);

docs/docs/cli/4-dev/03-mops-build.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ Build Motoko canisters defined in `mops.toml`
1111
mops build
1212
```
1313

14-
Compiles Motoko canisters to WebAssembly (Wasm) modules and generates Candid interface files.
14+
Compiles Motoko canisters to WebAssembly (Wasm) modules and generates Candid interface and Motoko stable types files.
1515

1616
Canisters must be defined in the `[canisters]` section of your `mops.toml` file.
1717

1818
The build command will automatically:
1919
- Add Candid metadata to the Wasm modules
20+
- Generate a Motoko stable types file (`.most`) for each canister
2021
- Validate Candid compatibility (if a candid file is specified in canister config)
2122

2223
### Examples
@@ -54,7 +55,7 @@ Show detailed build information including compiler commands and build times.
5455

5556
### `--output`, `-o`
5657

57-
Specify the output directory for compiled Wasm and Candid files. Overrides `[build].outputDir` from `mops.toml`.
58+
Specify the output directory for compiled Wasm, Candid, and stable types files. Overrides `[build].outputDir` from `mops.toml`.
5859

5960
Default `.mops/.build`
6061

@@ -87,7 +88,7 @@ args = ["--release", "--ai-errors"]
8788

8889
### `[build].outputDir`
8990

90-
Custom output directory for compiled Wasm and Candid files. The path is relative to the `mops.toml` location.
91+
Custom output directory for compiled Wasm, Candid, and stable types files. The path is relative to the `mops.toml` location.
9192

9293
Default `.mops/.build`
9394

@@ -100,3 +101,18 @@ If a `candid` field is specified in the canister configuration, the build comman
100101
If the compatibility check fails, the build will fail with an error message.
101102

102103
For manual compatibility checking, see [`mops check-candid`](/cli/mops-check-candid).
104+
105+
## Stable Types
106+
107+
Each build produces a `<canister>.most` file in the output directory alongside the `.wasm` and `.did` files. This file captures the stable variable type signatures of the current canister version.
108+
109+
To use it for upgrade safety checking, save the `.most` file before deploying a new version (e.g. copy it to a committed path), then point `mops check` to it via `mops.toml`:
110+
111+
```toml
112+
[canisters.backend.check-stable]
113+
path = ".deployed/backend.most"
114+
```
115+
116+
With this in place, `mops check` automatically verifies upgrade compatibility on every run.
117+
118+
See [`mops check`](/cli/mops-check#stable-compatibility-checking) for full configuration details.

docs/docs/cli/4-dev/05-mops-check-stable.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ Path to the old (deployed) version of the actor. Accepts two formats:
4646
- **`.mo` file** — the old Motoko source file. The command generates the `.most` stable type signature automatically.
4747
- **`.most` file** — a pre-generated stable type signature. Used directly without compilation.
4848

49+
:::tip
50+
`mops build` generates a `.most` file for each canister alongside `.wasm` and `.did`. Save it before deploying an upgrade, then configure `[canisters.<name>.check-stable]` in `mops.toml` so `mops check` verifies upgrade safety automatically on every run.
51+
:::
52+
4953
### `[canister]`
5054

5155
Name of the canister to check against (as defined in `mops.toml`). The current entrypoint is resolved from `[canisters.<name>].main`.

0 commit comments

Comments
 (0)