Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
881d55d
Add 'npm run mops' command and set up formatter for VS Code
rvanasa Oct 2, 2025
958971b
Set up boilerplate
rvanasa Oct 2, 2025
70cf479
Remove .vscode from gitignore
rvanasa Oct 2, 2025
a7d9d4d
Merge branch 'ryan/dev-env' into ryan/build-command
rvanasa Oct 2, 2025
b46a0d1
Rename 'ZenVoich/mops' repository to 'dfinity/mops'
rvanasa Oct 2, 2025
f5a690d
Simplify precommit hook
rvanasa Oct 2, 2025
c433f42
Merge branch 'ryan/dev-env' into ryan/build-command
rvanasa Oct 2, 2025
2c1bebb
Progress
rvanasa Oct 6, 2025
565e923
Setup Prettier
rvanasa Oct 6, 2025
0c1a8a8
Add .prettierignore
rvanasa Oct 6, 2025
4634cf1
Adjust 'fix' script and skip .did files
rvanasa Oct 6, 2025
1d2b42f
Add 'declarations/' to .prettierignore
rvanasa Oct 6, 2025
b5b1336
Merge branch 'main' of https://github.com/dfinity/mops into ryan/format
rvanasa Oct 6, 2025
782992b
Run 'npm audit fix'
rvanasa Oct 6, 2025
1977a2c
Adjust .prettierignore
rvanasa Oct 6, 2025
2573531
Run formatter
rvanasa Oct 6, 2025
5d97be7
Adjust linter rules to play better with Prettier
rvanasa Oct 6, 2025
859c8b9
Update package-lock.json
rvanasa Oct 6, 2025
3b17ecd
Remove parallel limit for CI jobs
rvanasa Oct 6, 2025
c769a7c
Update .prettierignore
rvanasa Oct 6, 2025
4a68b41
Merge branch 'ryan/format' of https://github.com/dfinity/mops into ry…
rvanasa Oct 6, 2025
da4412d
Implement building dfx canisters
rvanasa Oct 6, 2025
e30bfcb
Improve output format
rvanasa Oct 8, 2025
5dbdd8b
Misc
rvanasa Oct 13, 2025
2e598a0
Simplify
rvanasa Oct 13, 2025
a439ac6
Use 'interface' in place of 'type'
rvanasa Oct 14, 2025
ab40435
Add support for mops.toml [canisters] and [build] config
rvanasa Oct 16, 2025
815392b
Remove '--dfx' option for 'mops build' (initially just 'mops.toml' co…
rvanasa Oct 16, 2025
ea3ee0a
Show moc command path/args with --verbose
rvanasa Oct 24, 2025
3c5af69
Fix
rvanasa Oct 29, 2025
c377d0a
Add more .js suffixes to imports
rvanasa Oct 29, 2025
77f103a
Set up Wasm bindings
rvanasa Nov 15, 2025
b974232
Merge branch 'ryan/build-command' into check-candid-command
rvanasa Nov 15, 2025
1089a22
Merge remote-tracking branch 'origin/main' into check-candid-command
rvanasa Nov 15, 2025
59fa42c
Simplify
rvanasa Nov 17, 2025
19e458f
Update 'dist' script
rvanasa Nov 17, 2025
2140d47
Update prettierignore
rvanasa Nov 17, 2025
1225f25
Misc
rvanasa Nov 17, 2025
461a133
Copy 'wasm/pkg' into 'dist'
rvanasa Nov 17, 2025
ef4b7a9
Add 'wasm' dir to package files
rvanasa Nov 18, 2025
f38ac3c
Refactor / simplify
rvanasa Nov 18, 2025
733de46
Update prettierignore
rvanasa Nov 18, 2025
2aec907
Update setup-docker-action
rvanasa Nov 19, 2025
ce04817
Remove unused variable
rvanasa Nov 19, 2025
db41031
Support Wasm in bundled distribution
rvanasa Nov 19, 2025
dd9c6ea
Fix filename in bundle
rvanasa Nov 19, 2025
91aa230
Add Rust to Docker image
rvanasa Nov 19, 2025
32e5a1a
Add 'cargo' to env
rvanasa Nov 19, 2025
07ac1ad
Use 'web' target in place of 'bundler'
rvanasa Nov 20, 2025
94ea3b7
Try directly adding Cargo to path
rvanasa Nov 20, 2025
ba26a8f
Try different way of passing env
rvanasa Nov 20, 2025
14cc0d8
Try another way
rvanasa Nov 20, 2025
4c8d557
Try yet another way
rvanasa Nov 20, 2025
b1dd137
Use curl + bash
rvanasa Nov 20, 2025
5854c49
Try apt in place of apt-get
rvanasa Nov 20, 2025
e56f25c
Use apk
rvanasa Nov 20, 2025
9de217e
Source before run build script
rvanasa Nov 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"dist/",
"cli-releases/dist/",
"cli-releases/frontend/dist/",
"bundle/"
"bundle/",
"cli/wasm/"
],
"rules": {
"linebreak-style": ["error", "unix"],
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-hash.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Docker
uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19 # v4.3.0
uses: docker/setup-docker-action@efe9e3891a4f7307e689f2100b33a155b900a608 # v4.5.0
- name: Build using Docker
run: |
# MOPS_VERSION=$(cd cli && npm pkg get version | tr -d \")
Expand Down
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/cli/wasm

.svelte-kit/
declarations/
target/

*.md
*.did
*.js
*.d.ts
*.svelte
*.svelte
4 changes: 2 additions & 2 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ npm install -g tsx

2. Add `mops-local` alias to your shell (`~/.zshrc`, `~/.bashrc`)
```bash
alias mops-local="tsx /<path-to-local-mops>/cli/cli.ts"
alias mops-local="tsx /<path-to-local-mops>/cli/environments/nodejs/cli.ts"
```
or
```bash
alias mops-local="bun /<path-to-local-mops>/cli/cli.ts"
alias mops-local="bun /<path-to-local-mops>/cli/environments/nodejs/cli.ts"
```


Expand Down
6 changes: 5 additions & 1 deletion cli/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
FROM --platform=linux/amd64 zenvoich/mops-builder:1.1.0@sha256:9bb87e843a90f1c12ece4ba91ca703e19cd0d79522beb562f45b1f8a14cb05b6

# install Wasm build dependencies
RUN apk add --no-cache curl gcc musl-dev
RUN curl -sSf https://sh.rustup.rs/ | sh -s -- --default-toolchain=1.91.1 -y

# clone repository
ARG COMMIT_HASH
RUN git clone https://github.com/dfinity/mops.git
Expand All @@ -13,7 +17,7 @@ RUN bun i --ignore-scripts
# build
ARG MOPS_VERSION
RUN npm version $MOPS_VERSION --allow-same-version
RUN npm run build
RUN . "$HOME/.cargo/env" && npm run build

# verify
ARG SHASUM
Expand Down
15 changes: 15 additions & 0 deletions cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { add } from "./commands/add.js";
import { bench } from "./commands/bench.js";
import { build, DEFAULT_BUILD_OUTPUT_DIR } from "./commands/build.js";
import { bump } from "./commands/bump.js";
import { checkCandid } from "./commands/check-candid.js";
import { docsCoverage } from "./commands/docs-coverage.js";
import { docs } from "./commands/docs.js";
import { format } from "./commands/format.js";
Expand Down Expand Up @@ -286,6 +287,20 @@ program
});
});

// check-candid
program
.command("check-candid <new-candid> <original-candid>")
.description("Check Candid interface compatibility between two Candid files")
.action(async (newCandid, originalCandid) => {
checkConfigFile(true);
await installAll({
silent: true,
lock: "ignore",
installFromLockFile: true,
});
await checkCandid(newCandid, originalCandid);
});

// test
program
.command("test [filter]")
Expand Down
70 changes: 47 additions & 23 deletions cli/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { getMocPath } from "../helpers/get-moc-path.js";
import { readConfig } from "../mops.js";
import { CanisterConfig } from "../types.js";
import { sourcesArgs } from "./sources.js";
import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
import { cliError } from "../error.js";

export interface BuildOptions {
outputDir: string;
Expand All @@ -21,7 +23,7 @@ export async function build(
options: Partial<BuildOptions>,
): Promise<void> {
if (canisterNames?.length == 0) {
throw new Error("No canisters specified to build");
cliError("No canisters specified to build");
}

let outputDir = options.outputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
Expand All @@ -37,7 +39,7 @@ export async function build(
) ?? {};
}
if (!Object.keys(canisters).length) {
throw new Error(`No Motoko canisters found in mops.toml configuration`);
cliError(`No Motoko canisters found in mops.toml configuration`);
}

if (canisterNames) {
Expand All @@ -47,7 +49,7 @@ export async function build(
}
for (let name of canisterNames) {
if (!(name in canisters)) {
throw new Error(
cliError(
`Motoko canister '${name}' not found in mops.toml configuration`,
);
}
Expand All @@ -62,7 +64,7 @@ export async function build(
console.log(chalk.blue("build canister"), chalk.bold(canisterName));
let motokoPath = canister.main;
if (!motokoPath) {
throw new Error(`No main file is specified for canister ${canisterName}`);
cliError(`No main file is specified for canister ${canisterName}`);
}
let args = [
"-c",
Expand All @@ -75,15 +77,15 @@ export async function build(
];
if (config.build?.args) {
if (typeof config.build.args === "string") {
throw new Error(
cliError(
`[build] config 'args' should be an array of strings in mops.toml config file`,
);
}
args.push(...config.build.args);
}
if (canister.args) {
if (typeof canister.args === "string") {
throw new Error(
cliError(
`Canister config 'args' should be an array of strings for canister ${canisterName}`,
);
}
Expand All @@ -99,9 +101,6 @@ export async function build(
});

if (result.exitCode !== 0) {
console.error(
chalk.red(`Error: Failed to build canister ${canisterName}`),
);
if (!options.verbose) {
if (result.stderr) {
console.error(chalk.red(result.stderr));
Expand All @@ -111,27 +110,52 @@ export async function build(
console.error(result.stdout);
}
}
// throw new Error(
// `Build failed for canister ${canisterName} (exit code: ${result.exitCode})`,
// );
process.exit(1);
cliError(
`Build failed for canister ${canisterName} (exit code: ${result.exitCode})`,
);
}

if (options.verbose && result.stdout && result.stdout.trim()) {
console.log(result.stdout);
}
} catch (error: any) {
if (error.message?.includes("Build failed for canister")) {
throw error;

if (canister.candid) {
const generatedDidPath = join(outputDir, `${canisterName}.did`);
const originalCandidPath = canister.candid;

try {
const compatible = await isCandidCompatible(
generatedDidPath,
originalCandidPath,
);

if (!compatible) {
cliError(
`Candid compatibility check failed for canister ${canisterName}`,
);
}

if (options.verbose) {
console.log(
chalk.green(
`Candid compatibility check passed for canister ${canisterName}`,
),
);
}
} catch (candidError: any) {
cliError(
`Error during Candid compatibility check for canister ${canisterName}`,
candidError,
);
}
}
} catch (cliError: any) {
if (cliError.message?.includes("Build failed for canister")) {
throw cliError;
}
console.error(
chalk.red(`Error while compiling canister ${canisterName}`),
cliError(
`Error while compiling canister ${canisterName}${cliError?.message ? `\n${cliError.message}` : ""}`,
);
if (error.message) {
console.error(chalk.red(`Details: ${error.message}`));
}

throw new Error(`Build execution failed for canister ${canisterName}`);
}
options.verbose && console.timeEnd(`build canister ${canisterName}`);
}
Expand Down
24 changes: 24 additions & 0 deletions cli/commands/check-candid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import chalk from "chalk";
import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
import { cliError } from "../error.js";

export interface CheckCandidOptions {
verbose?: boolean;
}

export async function checkCandid(
newPath: string,
originalPath: string,
): Promise<void> {
try {
const compatible = await isCandidCompatible(newPath, originalPath);
if (!compatible) {
cliError("✖ Candid compatibility check failed");
}
console.log(chalk.green("✓ Candid compatibility check passed"));
} catch (error: any) {
cliError(
`Error while checking Candid compatibility${error?.message ? `\n${error.message}` : ""}`,
);
}
}
6 changes: 6 additions & 0 deletions cli/environments/nodejs/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as wasm from "../../wasm/pkg/nodejs/wasm.js";
import { setWasmBindings } from "../../wasm.js";

setWasmBindings(wasm);

export * from "../../cli.js";
6 changes: 6 additions & 0 deletions cli/environments/web/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as wasm from "../../wasm/pkg/web/wasm.js";
import { setWasmBindings } from "../../wasm.js";

setWasmBindings(wasm);

export * from "../../cli.js";
6 changes: 6 additions & 0 deletions cli/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import chalk from "chalk";

export function cliError(...args: unknown[]) {
console.error(chalk.red(...args));
process.exit(1);
}
22 changes: 22 additions & 0 deletions cli/helpers/is-candid-compatible.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { readFile, access } from "node:fs/promises";
import path from "node:path";
import { getWasmBindings } from "../wasm.js";

export async function isCandidCompatible(
newPath: string,
originalPath: string,
): Promise<boolean> {
try {
await access(newPath);
} catch {
throw new Error(`Candid file not found: ${newPath}`);
}
try {
await access(originalPath);
} catch {
throw new Error(`Candid file not found: ${originalPath}`);
}
const newText = await readFile(path.resolve(newPath), "utf8");
const originalText = await readFile(path.resolve(originalPath), "utf8");
return getWasmBindings().is_candid_compatible(newText, originalText);
}
Loading
Loading