ic-mops 2.12.2 → 2.12.3

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,10 @@
2
2
 
3
3
  ## Next
4
4
 
5
+ ## 2.12.3
6
+ - Fix `mops install --lock update` silently no-op'ing on a corrupt lockfile (#515)
7
+ - `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)
8
+
5
9
  ## 2.12.2
6
10
  - Fix `mops install` (and any `--lock check` flow) failing with "Mismatched number of resolved packages" when a project's resolved dependencies include multiple aliases (e.g. `base`, `base@0`, `base@0.16`) that pin to the same `name@version`
7
11
 
package/bundle/cli.tgz CHANGED
Binary file
@@ -47,22 +47,6 @@ export async function publish(
47
47
 
48
48
  console.log(`Publishing ${config.package?.name}@${config.package?.version}`);
49
49
 
50
- // validate
51
- for (let key of Object.keys(config)) {
52
- if (
53
- ![
54
- "package",
55
- "dependencies",
56
- "dev-dependencies",
57
- "toolchain",
58
- "requirements",
59
- ].includes(key)
60
- ) {
61
- console.log(chalk.red("Error: ") + `Unknown config section [${key}]`);
62
- process.exit(1);
63
- }
64
- }
65
-
66
50
  // required fields
67
51
  if (!config.package) {
68
52
  console.log(
@@ -97,29 +81,6 @@ export async function publish(
97
81
  }
98
82
  }
99
83
 
100
- let packageKeys = [
101
- "name",
102
- "version",
103
- "keywords",
104
- "description",
105
- "repository",
106
- "documentation",
107
- "homepage",
108
- "baseDir",
109
- "readme",
110
- "license",
111
- "files",
112
- "dfx",
113
- "moc",
114
- "donation",
115
- ];
116
- for (let key of Object.keys(config.package)) {
117
- if (!packageKeys.includes(key)) {
118
- console.log(chalk.red("Error: ") + `Unknown config key 'package.${key}'`);
119
- process.exit(1);
120
- }
121
- }
122
-
123
84
  // disabled fields
124
85
  for (let key of ["dfx", "moc", "homepage", "documentation", "donation"]) {
125
86
  if ((config.package as any)[key]) {
@@ -222,15 +183,6 @@ export async function publish(
222
183
  }
223
184
  }
224
185
 
225
- if (config.requirements) {
226
- Object.keys(config.requirements).forEach((name) => {
227
- if (name !== "moc") {
228
- console.log(chalk.red("Error: ") + `Unknown requirement "${name}"`);
229
- return;
230
- }
231
- });
232
- }
233
-
234
186
  let toBackendDep = (dep: Dependency): DependencyV2 => {
235
187
  return {
236
188
  ...dep,
@@ -22,19 +22,6 @@ export async function publish(options = {}) {
22
22
  let rootDir = getRootDir();
23
23
  let config = readConfig();
24
24
  console.log(`Publishing ${config.package?.name}@${config.package?.version}`);
25
- // validate
26
- for (let key of Object.keys(config)) {
27
- if (![
28
- "package",
29
- "dependencies",
30
- "dev-dependencies",
31
- "toolchain",
32
- "requirements",
33
- ].includes(key)) {
34
- console.log(chalk.red("Error: ") + `Unknown config section [${key}]`);
35
- process.exit(1);
36
- }
37
- }
38
25
  // required fields
39
26
  if (!config.package) {
40
27
  console.log(chalk.red("Error: ") +
@@ -63,28 +50,6 @@ export async function publish(options = {}) {
63
50
  }
64
51
  }
65
52
  }
66
- let packageKeys = [
67
- "name",
68
- "version",
69
- "keywords",
70
- "description",
71
- "repository",
72
- "documentation",
73
- "homepage",
74
- "baseDir",
75
- "readme",
76
- "license",
77
- "files",
78
- "dfx",
79
- "moc",
80
- "donation",
81
- ];
82
- for (let key of Object.keys(config.package)) {
83
- if (!packageKeys.includes(key)) {
84
- console.log(chalk.red("Error: ") + `Unknown config key 'package.${key}'`);
85
- process.exit(1);
86
- }
87
- }
88
53
  // disabled fields
89
54
  for (let key of ["dfx", "moc", "homepage", "documentation", "donation"]) {
90
55
  if (config.package[key]) {
@@ -167,14 +132,6 @@ export async function publish(options = {}) {
167
132
  }
168
133
  }
169
134
  }
170
- if (config.requirements) {
171
- Object.keys(config.requirements).forEach((name) => {
172
- if (name !== "moc") {
173
- console.log(chalk.red("Error: ") + `Unknown requirement "${name}"`);
174
- return;
175
- }
176
- });
177
- }
178
135
  let toBackendDep = (dep) => {
179
136
  return {
180
137
  ...dep,
@@ -20,6 +20,8 @@ export declare function getLocalFileHash(fileId: string): string;
20
20
  export declare function checkRemote(): Promise<void>;
21
21
  export declare function readLockFile(): LockFile | null;
22
22
  export declare function checkLockFileLight(): boolean;
23
- export declare function updateLockFile(): Promise<void>;
23
+ export declare function updateLockFile({ force, }?: {
24
+ force?: boolean;
25
+ }): Promise<void>;
24
26
  export declare function checkLockFile(force?: boolean): Promise<void>;
25
27
  export {};
package/dist/integrity.js CHANGED
@@ -13,7 +13,7 @@ export async function checkIntegrity(lock) {
13
13
  lock = process.env["CI"] ? "check" : "update";
14
14
  }
15
15
  if (lock === "update") {
16
- await updateLockFile();
16
+ await updateLockFile({ force });
17
17
  await checkLockFile(force);
18
18
  }
19
19
  else if (lock === "check") {
@@ -108,9 +108,11 @@ export function checkLockFileLight() {
108
108
  }
109
109
  return false;
110
110
  }
111
- export async function updateLockFile() {
111
+ export async function updateLockFile({ force = false, } = {}) {
112
112
  // if lock file exists and mops.toml hasn't changed, don't update it
113
- if (checkLockFileLight()) {
113
+ // (unless forced: `--lock update` must unconditionally regenerate so users
114
+ // can recover from a corrupt lockfile without `rm mops.lock`)
115
+ if (!force && checkLockFileLight()) {
114
116
  return;
115
117
  }
116
118
  let resolvedDeps = await resolvePackages();
@@ -228,6 +230,9 @@ export async function checkLockFile(force = false) {
228
230
  console.error(`Mismatched hash for ${fileId}`);
229
231
  console.error(`Locked hash: ${lockedHash}`);
230
232
  console.error(`Actual hash: ${localHash}`);
233
+ console.error("");
234
+ console.error("If you have not modified files under .mops/, your lockfile may be stale or corrupt.");
235
+ console.error("Run `mops install --lock update` to regenerate it.");
231
236
  process.exit(1);
232
237
  }
233
238
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.12.2",
3
+ "version": "2.12.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "bin/mops.js",
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, jest, test } from "@jest/globals";
2
- import { existsSync, rmSync } from "node:fs";
2
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import path from "path";
4
4
  import { cli } from "./helpers";
5
5
  describe("cli", () => {
@@ -86,4 +86,34 @@ describe("install", () => {
86
86
  rmSync(path.join(cwd, ".mops"), { recursive: true, force: true });
87
87
  }
88
88
  });
89
+ // Regression: `install --lock update` used to early-return if mops.toml's
90
+ // deps hash was unchanged, even when the lockfile's per-file hashes were
91
+ // stale/corrupt. The subsequent checkLockFile would then fail and exit 1,
92
+ // so `--lock update` could never recover a broken lock — the only escape
93
+ // was `rm mops.lock`. See issue #514.
94
+ test("--lock update rewrites a lockfile with a corrupt file hash", async () => {
95
+ const cwd = path.join(import.meta.dirname, "install/success");
96
+ const lockFile = path.join(cwd, "mops.lock");
97
+ rmSync(lockFile, { force: true });
98
+ try {
99
+ const first = await cli(["install"], { cwd, env: { CI: undefined } });
100
+ expect(first.exitCode).toBe(0);
101
+ expect(existsSync(lockFile)).toBe(true);
102
+ const bad = "BAD0000000000000000000000000000000000000000000000000000000000BAD";
103
+ const original = readFileSync(lockFile, "utf8");
104
+ const corrupted = original.replace(/"core@1\.0\.0\/mops\.toml":\s*"[0-9a-f]{64}"/, `"core@1.0.0/mops.toml": "${bad}"`);
105
+ expect(corrupted).not.toBe(original);
106
+ writeFileSync(lockFile, corrupted);
107
+ const result = await cli(["install", "--lock", "update"], {
108
+ cwd,
109
+ env: { CI: undefined },
110
+ });
111
+ expect(result.exitCode).toBe(0);
112
+ expect(readFileSync(lockFile, "utf8")).not.toContain(bad);
113
+ }
114
+ finally {
115
+ rmSync(lockFile, { force: true });
116
+ rmSync(path.join(cwd, ".mops"), { recursive: true, force: true });
117
+ }
118
+ });
89
119
  });
package/integrity.ts CHANGED
@@ -41,7 +41,7 @@ export async function checkIntegrity(lock?: "check" | "update" | "ignore") {
41
41
  }
42
42
 
43
43
  if (lock === "update") {
44
- await updateLockFile();
44
+ await updateLockFile({ force });
45
45
  await checkLockFile(force);
46
46
  } else if (lock === "check") {
47
47
  await checkLockFile(force);
@@ -159,9 +159,13 @@ export function checkLockFileLight(): boolean {
159
159
  return false;
160
160
  }
161
161
 
162
- export async function updateLockFile() {
162
+ export async function updateLockFile({
163
+ force = false,
164
+ }: { force?: boolean } = {}) {
163
165
  // if lock file exists and mops.toml hasn't changed, don't update it
164
- if (checkLockFileLight()) {
166
+ // (unless forced: `--lock update` must unconditionally regenerate so users
167
+ // can recover from a corrupt lockfile without `rm mops.lock`)
168
+ if (!force && checkLockFileLight()) {
165
169
  return;
166
170
  }
167
171
 
@@ -313,6 +317,11 @@ export async function checkLockFile(force = false) {
313
317
  console.error(`Mismatched hash for ${fileId}`);
314
318
  console.error(`Locked hash: ${lockedHash}`);
315
319
  console.error(`Actual hash: ${localHash}`);
320
+ console.error("");
321
+ console.error(
322
+ "If you have not modified files under .mops/, your lockfile may be stale or corrupt.",
323
+ );
324
+ console.error("Run `mops install --lock update` to regenerate it.");
316
325
  process.exit(1);
317
326
  }
318
327
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.12.2",
3
+ "version": "2.12.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/bin/mops.js",
package/tests/cli.test.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, jest, test } from "@jest/globals";
2
- import { existsSync, rmSync } from "node:fs";
2
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import path from "path";
4
4
  import { cli } from "./helpers";
5
5
 
@@ -92,4 +92,40 @@ describe("install", () => {
92
92
  rmSync(path.join(cwd, ".mops"), { recursive: true, force: true });
93
93
  }
94
94
  });
95
+
96
+ // Regression: `install --lock update` used to early-return if mops.toml's
97
+ // deps hash was unchanged, even when the lockfile's per-file hashes were
98
+ // stale/corrupt. The subsequent checkLockFile would then fail and exit 1,
99
+ // so `--lock update` could never recover a broken lock — the only escape
100
+ // was `rm mops.lock`. See issue #514.
101
+ test("--lock update rewrites a lockfile with a corrupt file hash", async () => {
102
+ const cwd = path.join(import.meta.dirname, "install/success");
103
+ const lockFile = path.join(cwd, "mops.lock");
104
+ rmSync(lockFile, { force: true });
105
+ try {
106
+ const first = await cli(["install"], { cwd, env: { CI: undefined } });
107
+ expect(first.exitCode).toBe(0);
108
+ expect(existsSync(lockFile)).toBe(true);
109
+
110
+ const bad =
111
+ "BAD0000000000000000000000000000000000000000000000000000000000BAD";
112
+ const original = readFileSync(lockFile, "utf8");
113
+ const corrupted = original.replace(
114
+ /"core@1\.0\.0\/mops\.toml":\s*"[0-9a-f]{64}"/,
115
+ `"core@1.0.0/mops.toml": "${bad}"`,
116
+ );
117
+ expect(corrupted).not.toBe(original);
118
+ writeFileSync(lockFile, corrupted);
119
+
120
+ const result = await cli(["install", "--lock", "update"], {
121
+ cwd,
122
+ env: { CI: undefined },
123
+ });
124
+ expect(result.exitCode).toBe(0);
125
+ expect(readFileSync(lockFile, "utf8")).not.toContain(bad);
126
+ } finally {
127
+ rmSync(lockFile, { force: true });
128
+ rmSync(path.join(cwd, ".mops"), { recursive: true, force: true });
129
+ }
130
+ });
95
131
  });