Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Mops CLI Changelog

## Next
- Fix `mops install --lock update` silently no-op'ing on a corrupt lockfile (#515)
- `mops publish` no longer rejects unknown `mops.toml` sections, `package.*` keys, or `requirements.*` entries — these typo guards were the only place in the CLI that complained about unknown keys, drifted from the docs/types, and blocked publish on harmless local-only config like `[moc]`, `[canisters]`, `[build]`, and `[lint]` (#512)

## 2.12.2
Expand Down
15 changes: 12 additions & 3 deletions cli/integrity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function checkIntegrity(lock?: "check" | "update" | "ignore") {
}

if (lock === "update") {
await updateLockFile();
await updateLockFile({ force });
await checkLockFile(force);
} else if (lock === "check") {
await checkLockFile(force);
Expand Down Expand Up @@ -159,9 +159,13 @@ export function checkLockFileLight(): boolean {
return false;
}

export async function updateLockFile() {
export async function updateLockFile({
force = false,
}: { force?: boolean } = {}) {
// if lock file exists and mops.toml hasn't changed, don't update it
if (checkLockFileLight()) {
// (unless forced: `--lock update` must unconditionally regenerate so users
// can recover from a corrupt lockfile without `rm mops.lock`)
if (!force && checkLockFileLight()) {
return;
}

Expand Down Expand Up @@ -313,6 +317,11 @@ export async function checkLockFile(force = false) {
console.error(`Mismatched hash for ${fileId}`);
console.error(`Locked hash: ${lockedHash}`);
console.error(`Actual hash: ${localHash}`);
console.error("");
console.error(
"If you have not modified files under .mops/, your lockfile may be stale or corrupt.",
);
console.error("Run `mops install --lock update` to regenerate it.");
process.exit(1);
}
}
Expand Down
38 changes: 37 additions & 1 deletion cli/tests/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, jest, test } from "@jest/globals";
import { existsSync, rmSync } from "node:fs";
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import path from "path";
import { cli } from "./helpers";

Expand Down Expand Up @@ -92,4 +92,40 @@ describe("install", () => {
rmSync(path.join(cwd, ".mops"), { recursive: true, force: true });
}
});

// Regression: `install --lock update` used to early-return if mops.toml's
// deps hash was unchanged, even when the lockfile's per-file hashes were
// stale/corrupt. The subsequent checkLockFile would then fail and exit 1,
// so `--lock update` could never recover a broken lock — the only escape
// was `rm mops.lock`. See issue #514.
test("--lock update rewrites a lockfile with a corrupt file hash", async () => {
const cwd = path.join(import.meta.dirname, "install/success");
const lockFile = path.join(cwd, "mops.lock");
rmSync(lockFile, { force: true });
try {
const first = await cli(["install"], { cwd, env: { CI: undefined } });
expect(first.exitCode).toBe(0);
expect(existsSync(lockFile)).toBe(true);

const bad =
"BAD0000000000000000000000000000000000000000000000000000000000BAD";
const original = readFileSync(lockFile, "utf8");
const corrupted = original.replace(
/"core@1\.0\.0\/mops\.toml":\s*"[0-9a-f]{64}"/,
`"core@1.0.0/mops.toml": "${bad}"`,
);
expect(corrupted).not.toBe(original);
writeFileSync(lockFile, corrupted);

const result = await cli(["install", "--lock", "update"], {
cwd,
env: { CI: undefined },
});
expect(result.exitCode).toBe(0);
expect(readFileSync(lockFile, "utf8")).not.toContain(bad);
} finally {
rmSync(lockFile, { force: true });
rmSync(path.join(cwd, ".mops"), { recursive: true, force: true });
}
});
});
2 changes: 1 addition & 1 deletion docs/docs/cli/1-deps/02-mops-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ See [mops.lock](/mops.lock) for details on lockfile contents and when to commit
What to do with the [lockfile](/mops.lock).

Possible values:
- `update` — keep the lockfile in sync with current dependencies and verify file integrity (default)
- `update` — keep the lockfile in sync with current dependencies and verify file integrity (default). Pass explicitly to force regeneration if the lockfile is stale or corrupt.
- `check` — verify file integrity against an existing lockfile; fail if the lockfile is missing or out of date
- `ignore` — skip the lockfile entirely

Expand Down
Loading