ic-mops 2.10.0 → 2.11.0
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 +7 -0
- package/bundle/cli.tgz +0 -0
- package/cli.ts +24 -0
- package/commands/build.ts +10 -1
- package/commands/check-stable.ts +52 -21
- package/commands/check.ts +65 -52
- package/commands/migrate.ts +165 -0
- package/declarations/main/main.did +38 -0
- package/declarations/main/main.did.d.ts +36 -0
- package/declarations/main/main.did.js +36 -0
- package/dist/cli.js +18 -0
- package/dist/commands/build.js +5 -1
- package/dist/commands/check-stable.d.ts +1 -0
- package/dist/commands/check-stable.js +39 -21
- package/dist/commands/check.js +51 -42
- package/dist/commands/migrate.d.ts +2 -0
- package/dist/commands/migrate.js +104 -0
- package/dist/declarations/main/main.did +38 -0
- package/dist/declarations/main/main.did.d.ts +36 -0
- package/dist/declarations/main/main.did.js +36 -0
- package/dist/helpers/migrations.d.ts +10 -0
- package/dist/helpers/migrations.js +109 -0
- package/dist/helpers/resolve-canisters.d.ts +1 -1
- package/dist/helpers/resolve-canisters.js +15 -1
- package/dist/package.json +1 -1
- package/dist/tests/migrate.test.d.ts +1 -0
- package/dist/tests/migrate.test.js +160 -0
- package/dist/types.d.ts +7 -0
- package/helpers/migrations.ts +166 -0
- package/helpers/resolve-canisters.ts +17 -0
- package/package.json +1 -1
- package/tests/__snapshots__/migrate.test.ts.snap +119 -0
- package/tests/migrate/basic/deployed.most +12 -0
- package/tests/migrate/basic/migrations/20250101_000000_Init.mo +5 -0
- package/tests/migrate/basic/migrations/20250201_000000_AddName.mo +5 -0
- package/tests/migrate/basic/migrations/20250301_000000_AddEmail.mo +9 -0
- package/tests/migrate/basic/mops.toml +15 -0
- package/tests/migrate/basic/next-migration/.gitkeep +0 -0
- package/tests/migrate/basic/src/main.mo +11 -0
- package/tests/migrate/with-next/deployed.most +12 -0
- package/tests/migrate/with-next/migrations/20250101_000000_Init.mo +5 -0
- package/tests/migrate/with-next/migrations/20250201_000000_AddName.mo +5 -0
- package/tests/migrate/with-next/migrations/20250301_000000_AddEmail.mo +9 -0
- package/tests/migrate/with-next/mops.toml +15 -0
- package/tests/migrate/with-next/next-migration/20250401_000000_RenameId.mo +9 -0
- package/tests/migrate/with-next/src/main.mo +11 -0
- package/tests/migrate.test.ts +228 -0
- package/types.ts +8 -0
package/dist/cli.js
CHANGED
|
@@ -33,6 +33,7 @@ import { test } from "./commands/test/test.js";
|
|
|
33
33
|
import { toolchain } from "./commands/toolchain/index.js";
|
|
34
34
|
import { update } from "./commands/update.js";
|
|
35
35
|
import { getPrincipal, getUserProp, importPem, setUserProp, } from "./commands/user.js";
|
|
36
|
+
import { migrateNew, migrateFreeze } from "./commands/migrate.js";
|
|
36
37
|
import { watch } from "./commands/watch/watch.js";
|
|
37
38
|
import { apiVersion, checkApiCompatibility, checkConfigFile, getGlobalMocArgs, getNetworkFile, readConfig, setNetwork, version, } from "./mops.js";
|
|
38
39
|
import { resolvePackages } from "./resolve-packages.js";
|
|
@@ -550,6 +551,23 @@ toolchainCommand
|
|
|
550
551
|
console.log(bin);
|
|
551
552
|
});
|
|
552
553
|
program.addCommand(toolchainCommand);
|
|
554
|
+
// migrate
|
|
555
|
+
const migrateCommand = new Command("migrate").description("Manage enhanced migration chains");
|
|
556
|
+
migrateCommand
|
|
557
|
+
.command("new <name> [canister]")
|
|
558
|
+
.description("Create a new migration file in the next-migration directory")
|
|
559
|
+
.action(async (name, canister) => {
|
|
560
|
+
checkConfigFile(true);
|
|
561
|
+
await migrateNew(name, canister);
|
|
562
|
+
});
|
|
563
|
+
migrateCommand
|
|
564
|
+
.command("freeze [canister]")
|
|
565
|
+
.description("Move the next migration into the frozen chain")
|
|
566
|
+
.action(async (canister) => {
|
|
567
|
+
checkConfigFile(true);
|
|
568
|
+
await migrateFreeze(canister);
|
|
569
|
+
});
|
|
570
|
+
program.addCommand(migrateCommand);
|
|
553
571
|
// self
|
|
554
572
|
const selfCommand = new Command("self").description("Mops CLI management");
|
|
555
573
|
selfCommand
|
package/dist/commands/build.js
CHANGED
|
@@ -7,6 +7,7 @@ import { lock, unlockSync } from "proper-lockfile";
|
|
|
7
7
|
import { cliError } from "../error.js";
|
|
8
8
|
import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
|
|
9
9
|
import { filterCanisters, resolveCanisterConfigs, validateCanisterArgs, } from "../helpers/resolve-canisters.js";
|
|
10
|
+
import { prepareMigrationArgs } from "../helpers/migrations.js";
|
|
10
11
|
import { getWasmBindings } from "../wasm.js";
|
|
11
12
|
import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
|
|
12
13
|
import { sourcesArgs } from "./sources.js";
|
|
@@ -62,6 +63,7 @@ export async function build(canisterNames, options) {
|
|
|
62
63
|
catch { }
|
|
63
64
|
};
|
|
64
65
|
process.on("exit", exitCleanup);
|
|
66
|
+
const migration = await prepareMigrationArgs(canister.migrations, canisterName, "build", options.verbose);
|
|
65
67
|
try {
|
|
66
68
|
let args = [
|
|
67
69
|
"-c",
|
|
@@ -72,6 +74,7 @@ export async function build(canisterNames, options) {
|
|
|
72
74
|
motokoPath,
|
|
73
75
|
...(await sourcesArgs()).flat(),
|
|
74
76
|
...getGlobalMocArgs(config),
|
|
77
|
+
...migration.migrationArgs,
|
|
75
78
|
];
|
|
76
79
|
args.push(...collectExtraArgs(config, canister, canisterName, options.extraArgs));
|
|
77
80
|
const isPublicCandid = true; // always true for now to reduce corner cases
|
|
@@ -148,6 +151,7 @@ export async function build(canisterNames, options) {
|
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
finally {
|
|
154
|
+
await migration.cleanup();
|
|
151
155
|
process.removeListener("exit", exitCleanup);
|
|
152
156
|
try {
|
|
153
157
|
await release?.();
|
|
@@ -173,7 +177,7 @@ function collectExtraArgs(config, canister, canisterName, extraArgs) {
|
|
|
173
177
|
args.push(...config.build.args);
|
|
174
178
|
}
|
|
175
179
|
if (canister.args) {
|
|
176
|
-
validateCanisterArgs(canister, canisterName);
|
|
180
|
+
validateCanisterArgs(canister, canisterName, config);
|
|
177
181
|
args.push(...canister.args);
|
|
178
182
|
}
|
|
179
183
|
if (extraArgs) {
|
|
@@ -4,6 +4,7 @@ import { 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 { prepareMigrationArgs } from "../helpers/migrations.js";
|
|
7
8
|
import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
|
|
8
9
|
import { filterCanisters, looksLikeFile, resolveCanisterConfigs, resolveSingleCanister, validateCanisterArgs, } from "../helpers/resolve-canisters.js";
|
|
9
10
|
import { sourcesArgs } from "./sources.js";
|
|
@@ -41,16 +42,23 @@ export async function checkStable(args, options = {}) {
|
|
|
41
42
|
if (!canister.main) {
|
|
42
43
|
cliError(`No main file specified for canister '${name}' in mops.toml`);
|
|
43
44
|
}
|
|
44
|
-
validateCanisterArgs(canister, name);
|
|
45
|
-
await
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
validateCanisterArgs(canister, name, config);
|
|
46
|
+
const migration = await prepareMigrationArgs(canister.migrations, name, "check", options.verbose);
|
|
47
|
+
try {
|
|
48
|
+
await runStableCheck({
|
|
49
|
+
oldFile,
|
|
50
|
+
canisterMain: resolveConfigPath(canister.main),
|
|
51
|
+
canisterName: name,
|
|
52
|
+
mocPath,
|
|
53
|
+
globalMocArgs,
|
|
54
|
+
canisterArgs: [...migration.migrationArgs, ...(canister.args ?? [])],
|
|
55
|
+
options,
|
|
56
|
+
hasMigrations: !!canister.migrations,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
await migration.cleanup();
|
|
61
|
+
}
|
|
54
62
|
return;
|
|
55
63
|
}
|
|
56
64
|
const canisters = resolveCanisterConfigs(config);
|
|
@@ -62,23 +70,30 @@ export async function checkStable(args, options = {}) {
|
|
|
62
70
|
if (!canister.main) {
|
|
63
71
|
cliError(`No main file specified for canister '${name}' in mops.toml`);
|
|
64
72
|
}
|
|
65
|
-
validateCanisterArgs(canister, name);
|
|
73
|
+
validateCanisterArgs(canister, name, config);
|
|
66
74
|
const stablePath = resolveStablePath(canister, name, {
|
|
67
75
|
required: !!canisterNames,
|
|
68
76
|
});
|
|
69
77
|
if (!stablePath) {
|
|
70
78
|
continue;
|
|
71
79
|
}
|
|
72
|
-
await
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const migration = await prepareMigrationArgs(canister.migrations, name, "check", options.verbose);
|
|
81
|
+
try {
|
|
82
|
+
await runStableCheck({
|
|
83
|
+
oldFile: stablePath,
|
|
84
|
+
canisterMain: resolveConfigPath(canister.main),
|
|
85
|
+
canisterName: name,
|
|
86
|
+
mocPath,
|
|
87
|
+
globalMocArgs,
|
|
88
|
+
canisterArgs: [...migration.migrationArgs, ...(canister.args ?? [])],
|
|
89
|
+
sources,
|
|
90
|
+
options,
|
|
91
|
+
hasMigrations: !!canister.migrations,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await migration.cleanup();
|
|
96
|
+
}
|
|
82
97
|
checked++;
|
|
83
98
|
}
|
|
84
99
|
if (checked === 0 && !canisterNames) {
|
|
@@ -118,6 +133,9 @@ export async function runStableCheck(params) {
|
|
|
118
133
|
if (result.stderr) {
|
|
119
134
|
console.error(result.stderr);
|
|
120
135
|
}
|
|
136
|
+
if (params.hasMigrations) {
|
|
137
|
+
console.error(chalk.yellow("Hint: You may need a migration. Run `mops migrate new <Name>` to create one."));
|
|
138
|
+
}
|
|
121
139
|
cliError(`✗ Stable compatibility check failed for canister '${canisterName}'`);
|
|
122
140
|
}
|
|
123
141
|
console.log(chalk.green(`✓ Stable compatibility check passed for canister '${canisterName}'`));
|
package/dist/commands/check.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getGlobalMocArgs, getRootDir, readConfig, resolveConfigPath, } from "..
|
|
|
6
6
|
import { autofixMotoko } from "../helpers/autofix-motoko.js";
|
|
7
7
|
import { getMocSemVer } from "../helpers/get-moc-version.js";
|
|
8
8
|
import { filterCanisters, looksLikeFile, resolveCanisterConfigs, validateCanisterArgs, } from "../helpers/resolve-canisters.js";
|
|
9
|
+
import { prepareMigrationArgs } from "../helpers/migrations.js";
|
|
9
10
|
import { resolveStablePath, runStableCheck } from "./check-stable.js";
|
|
10
11
|
import { sourcesArgs } from "./sources.js";
|
|
11
12
|
import { toolchain } from "./toolchain/index.js";
|
|
@@ -87,53 +88,61 @@ async function checkCanisters(config, canisters, options) {
|
|
|
87
88
|
if (!canister.main) {
|
|
88
89
|
cliError(`No main file specified for canister '${canisterName}' in mops.toml`);
|
|
89
90
|
}
|
|
90
|
-
validateCanisterArgs(canister, canisterName);
|
|
91
|
+
validateCanisterArgs(canister, canisterName, config);
|
|
91
92
|
const motokoPath = resolveConfigPath(canister.main);
|
|
92
|
-
const
|
|
93
|
-
"--check",
|
|
94
|
-
...(allLibs ? ["--all-libs"] : []),
|
|
95
|
-
...sources,
|
|
96
|
-
...globalMocArgs,
|
|
97
|
-
...(canister.args ?? []),
|
|
98
|
-
...(options.extraArgs ?? []),
|
|
99
|
-
];
|
|
100
|
-
if (options.fix) {
|
|
101
|
-
if (options.verbose) {
|
|
102
|
-
console.log(chalk.blue("check"), chalk.gray(`Attempting to fix ${canisterName}`));
|
|
103
|
-
}
|
|
104
|
-
const fixResult = await autofixMotoko(mocPath, [motokoPath], mocArgs);
|
|
105
|
-
logAutofixResult(fixResult, options.verbose);
|
|
106
|
-
}
|
|
93
|
+
const migration = await prepareMigrationArgs(canister.migrations, canisterName, "check", options.verbose);
|
|
107
94
|
try {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
95
|
+
const mocArgs = [
|
|
96
|
+
"--check",
|
|
97
|
+
...(allLibs ? ["--all-libs"] : []),
|
|
98
|
+
...sources,
|
|
99
|
+
...globalMocArgs,
|
|
100
|
+
...migration.migrationArgs,
|
|
101
|
+
...(canister.args ?? []),
|
|
102
|
+
...(options.extraArgs ?? []),
|
|
103
|
+
];
|
|
104
|
+
if (options.fix) {
|
|
105
|
+
if (options.verbose) {
|
|
106
|
+
console.log(chalk.blue("check"), chalk.gray(`Attempting to fix ${canisterName}`));
|
|
107
|
+
}
|
|
108
|
+
const fixResult = await autofixMotoko(mocPath, [motokoPath], mocArgs);
|
|
109
|
+
logAutofixResult(fixResult, options.verbose);
|
|
112
110
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
try {
|
|
112
|
+
const args = [motokoPath, ...mocArgs];
|
|
113
|
+
if (options.verbose) {
|
|
114
|
+
console.log(chalk.blue("check"), chalk.gray(`Checking canister ${canisterName}:`));
|
|
115
|
+
console.log(chalk.gray(mocPath, JSON.stringify(args)));
|
|
116
|
+
}
|
|
117
|
+
const result = await execa(mocPath, args, {
|
|
118
|
+
stdio: "inherit",
|
|
119
|
+
reject: false,
|
|
120
|
+
});
|
|
121
|
+
if (result.exitCode !== 0) {
|
|
122
|
+
cliError(`✗ Check failed for canister ${canisterName} (exit code: ${result.exitCode})`);
|
|
123
|
+
}
|
|
124
|
+
console.log(chalk.green(`✓ ${canisterName}`));
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
cliError(`Error while checking canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`);
|
|
128
|
+
}
|
|
129
|
+
const stablePath = resolveStablePath(canister, canisterName);
|
|
130
|
+
if (stablePath) {
|
|
131
|
+
await runStableCheck({
|
|
132
|
+
oldFile: stablePath,
|
|
133
|
+
canisterMain: motokoPath,
|
|
134
|
+
canisterName,
|
|
135
|
+
mocPath,
|
|
136
|
+
globalMocArgs,
|
|
137
|
+
canisterArgs: [...migration.migrationArgs, ...(canister.args ?? [])],
|
|
138
|
+
sources,
|
|
139
|
+
options: { verbose: options.verbose, extraArgs: options.extraArgs },
|
|
140
|
+
hasMigrations: !!canister.migrations,
|
|
141
|
+
});
|
|
119
142
|
}
|
|
120
|
-
console.log(chalk.green(`✓ ${canisterName}`));
|
|
121
|
-
}
|
|
122
|
-
catch (err) {
|
|
123
|
-
cliError(`Error while checking canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`);
|
|
124
143
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
await runStableCheck({
|
|
128
|
-
oldFile: stablePath,
|
|
129
|
-
canisterMain: motokoPath,
|
|
130
|
-
canisterName,
|
|
131
|
-
mocPath,
|
|
132
|
-
globalMocArgs,
|
|
133
|
-
canisterArgs: canister.args ?? [],
|
|
134
|
-
sources,
|
|
135
|
-
options: { verbose: options.verbose, extraArgs: options.extraArgs },
|
|
136
|
-
});
|
|
144
|
+
finally {
|
|
145
|
+
await migration.cleanup();
|
|
137
146
|
}
|
|
138
147
|
}
|
|
139
148
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, renameSync } from "node:fs";
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { cliError } from "../error.js";
|
|
6
|
+
import { getNextMigrationFile, validateMigrationsConfig, validateNextMigrationOrder, } from "../helpers/migrations.js";
|
|
7
|
+
import { resolveCanisterConfigs } from "../helpers/resolve-canisters.js";
|
|
8
|
+
import { readConfig, resolveConfigPath } from "../mops.js";
|
|
9
|
+
function resolveMigrationCanister(canisterName) {
|
|
10
|
+
const config = readConfig();
|
|
11
|
+
const canisters = resolveCanisterConfigs(config);
|
|
12
|
+
const withMigrations = Object.entries(canisters).filter(([, c]) => c.migrations);
|
|
13
|
+
if (withMigrations.length === 0) {
|
|
14
|
+
cliError("No canisters with [migrations] config found in mops.toml.\n" +
|
|
15
|
+
"Add a [canisters.<name>.migrations] section first:\n\n" +
|
|
16
|
+
" [canisters.backend.migrations]\n" +
|
|
17
|
+
' chain = "migrations"\n' +
|
|
18
|
+
' next = "next-migration" # required for migrate new/freeze');
|
|
19
|
+
}
|
|
20
|
+
if (canisterName) {
|
|
21
|
+
const canister = canisters[canisterName];
|
|
22
|
+
if (!canister) {
|
|
23
|
+
cliError(`Canister '${canisterName}' not found in mops.toml. Available: ${Object.keys(canisters).join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
if (!canister.migrations) {
|
|
26
|
+
cliError(`Canister '${canisterName}' has no [canisters.${canisterName}.migrations] config in mops.toml`);
|
|
27
|
+
}
|
|
28
|
+
return { name: canisterName, canister };
|
|
29
|
+
}
|
|
30
|
+
if (withMigrations.length > 1) {
|
|
31
|
+
cliError(`Multiple canisters with [migrations] config. Please specify one: ${withMigrations.map(([n]) => n).join(", ")}`);
|
|
32
|
+
}
|
|
33
|
+
return { name: withMigrations[0][0], canister: withMigrations[0][1] };
|
|
34
|
+
}
|
|
35
|
+
const VALID_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
36
|
+
function generateTimestamp() {
|
|
37
|
+
const now = new Date();
|
|
38
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
39
|
+
return (`${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}` +
|
|
40
|
+
`_${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(now.getUTCSeconds())}`);
|
|
41
|
+
}
|
|
42
|
+
const MIGRATION_TEMPLATE = `module {
|
|
43
|
+
public func migration(old : {}) : {} {
|
|
44
|
+
{}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
export async function migrateNew(name, canisterName) {
|
|
49
|
+
if (!VALID_NAME_RE.test(name)) {
|
|
50
|
+
cliError(`Invalid migration name: "${name}"\n` +
|
|
51
|
+
"Name must start with a letter and contain only letters, digits, and underscores.");
|
|
52
|
+
}
|
|
53
|
+
const { name: resolvedName, canister } = resolveMigrationCanister(canisterName);
|
|
54
|
+
const migrations = canister.migrations;
|
|
55
|
+
validateMigrationsConfig(migrations, resolvedName);
|
|
56
|
+
if (!migrations.next) {
|
|
57
|
+
cliError(`[canisters.${resolvedName}.migrations] is missing the "next" field.\n` +
|
|
58
|
+
'Add next = "next-migration" to use `mops migrate new/freeze`.');
|
|
59
|
+
}
|
|
60
|
+
const chainDir = resolveConfigPath(migrations.chain);
|
|
61
|
+
const nextDir = resolveConfigPath(migrations.next);
|
|
62
|
+
const existingNext = existsSync(nextDir)
|
|
63
|
+
? getNextMigrationFile(nextDir)
|
|
64
|
+
: null;
|
|
65
|
+
if (existingNext) {
|
|
66
|
+
cliError(`A next migration already exists: ${existingNext}\n` +
|
|
67
|
+
"Freeze it first with `mops migrate freeze`.");
|
|
68
|
+
}
|
|
69
|
+
const timestamp = generateTimestamp();
|
|
70
|
+
const fileName = `${timestamp}_${name}.mo`;
|
|
71
|
+
validateNextMigrationOrder(chainDir, fileName);
|
|
72
|
+
if (!existsSync(chainDir)) {
|
|
73
|
+
mkdirSync(chainDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
if (!existsSync(nextDir)) {
|
|
76
|
+
mkdirSync(nextDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
const filePath = join(nextDir, fileName);
|
|
79
|
+
await writeFile(filePath, MIGRATION_TEMPLATE);
|
|
80
|
+
console.log(chalk.green(`✓ Created migration: ${filePath}`));
|
|
81
|
+
}
|
|
82
|
+
export async function migrateFreeze(canisterName) {
|
|
83
|
+
const { name: resolvedName, canister } = resolveMigrationCanister(canisterName);
|
|
84
|
+
const migrations = canister.migrations;
|
|
85
|
+
validateMigrationsConfig(migrations, resolvedName);
|
|
86
|
+
if (!migrations.next) {
|
|
87
|
+
cliError(`[canisters.${resolvedName}.migrations] is missing the "next" field.\n` +
|
|
88
|
+
'Add next = "next-migration" to use `mops migrate new/freeze`.');
|
|
89
|
+
}
|
|
90
|
+
const chainDir = resolveConfigPath(migrations.chain);
|
|
91
|
+
const nextDir = resolveConfigPath(migrations.next);
|
|
92
|
+
const nextFile = existsSync(nextDir) ? getNextMigrationFile(nextDir) : null;
|
|
93
|
+
if (!nextFile) {
|
|
94
|
+
cliError("No next migration to freeze. Create one with `mops migrate new <Name>`.");
|
|
95
|
+
}
|
|
96
|
+
validateNextMigrationOrder(chainDir, nextFile);
|
|
97
|
+
if (!existsSync(chainDir)) {
|
|
98
|
+
mkdirSync(chainDir, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
const src = join(nextDir, nextFile);
|
|
101
|
+
const dest = join(chainDir, nextFile);
|
|
102
|
+
renameSync(src, dest);
|
|
103
|
+
console.log(chalk.green(`✓ Frozen migration: ${nextFile} → ${chainDir}/`));
|
|
104
|
+
}
|
|
@@ -42,6 +42,11 @@ type StreamingCallbackResponse =
|
|
|
42
42
|
};
|
|
43
43
|
type StreamingCallback = func (StreamingToken) ->
|
|
44
44
|
(opt StreamingCallbackResponse) query;
|
|
45
|
+
type StructureStats =
|
|
46
|
+
record {
|
|
47
|
+
bytes: nat;
|
|
48
|
+
count: nat;
|
|
49
|
+
};
|
|
45
50
|
type StorageStats =
|
|
46
51
|
record {
|
|
47
52
|
cyclesBalance: nat;
|
|
@@ -272,6 +277,38 @@ type PackageChanges =
|
|
|
272
277
|
prevDocsCoverage: float64;
|
|
273
278
|
tests: TestsChanges;
|
|
274
279
|
};
|
|
280
|
+
type MemoryStats =
|
|
281
|
+
record {
|
|
282
|
+
dailySnapshots: StructureStats;
|
|
283
|
+
dailySnapshotsByPackageId: StructureStats;
|
|
284
|
+
dailySnapshotsByPackageName: StructureStats;
|
|
285
|
+
dailyTempRecords: StructureStats;
|
|
286
|
+
downloadsByPackageId: StructureStats;
|
|
287
|
+
downloadsByPackageName: StructureStats;
|
|
288
|
+
fileIdsByPackage: StructureStats;
|
|
289
|
+
hashByFileId: StructureStats;
|
|
290
|
+
highestConfigs: StructureStats;
|
|
291
|
+
maintainersByPackage: StructureStats;
|
|
292
|
+
ownersByPackage: StructureStats;
|
|
293
|
+
packageBenchmarks: StructureStats;
|
|
294
|
+
packageConfigs: StructureStats;
|
|
295
|
+
packageDocsCoverage: StructureStats;
|
|
296
|
+
packageFileStats: StructureStats;
|
|
297
|
+
packageNotes: StructureStats;
|
|
298
|
+
packagePublications: StructureStats;
|
|
299
|
+
packageTestStats: StructureStats;
|
|
300
|
+
packageVersions: StructureStats;
|
|
301
|
+
rtsHeapSize: nat;
|
|
302
|
+
rtsMemorySize: nat;
|
|
303
|
+
names: StructureStats;
|
|
304
|
+
storageByFileId: StructureStats;
|
|
305
|
+
storages: StructureStats;
|
|
306
|
+
users: StructureStats;
|
|
307
|
+
weeklySnapshots: StructureStats;
|
|
308
|
+
weeklySnapshotsByPackageId: StructureStats;
|
|
309
|
+
weeklySnapshotsByPackageName: StructureStats;
|
|
310
|
+
weeklyTempRecords: StructureStats;
|
|
311
|
+
};
|
|
275
312
|
type Main =
|
|
276
313
|
service {
|
|
277
314
|
addMaintainer: (packageName: PackageName, newMaintainer: principal) ->
|
|
@@ -311,6 +348,7 @@ type Main =
|
|
|
311
348
|
SemverPart;
|
|
312
349
|
}) -> (Result_6) query;
|
|
313
350
|
getHighestVersion: (name: PackageName) -> (Result_5) query;
|
|
351
|
+
getMemoryStats: () -> (MemoryStats) query;
|
|
314
352
|
getMostDownloadedPackages: () -> (vec PackageSummary) query;
|
|
315
353
|
getMostDownloadedPackagesIn7Days: () -> (vec PackageSummary) query;
|
|
316
354
|
getNewPackages: () -> (vec PackageSummary) query;
|
|
@@ -45,6 +45,41 @@ export interface HttpRequestResult {
|
|
|
45
45
|
'body' : Uint8Array | number[],
|
|
46
46
|
'headers' : Array<HttpHeader>,
|
|
47
47
|
}
|
|
48
|
+
export interface StructureStats {
|
|
49
|
+
'count' : bigint,
|
|
50
|
+
'bytes' : bigint,
|
|
51
|
+
}
|
|
52
|
+
export interface MemoryStats {
|
|
53
|
+
'rtsHeapSize' : bigint,
|
|
54
|
+
'rtsMemorySize' : bigint,
|
|
55
|
+
'packageVersions' : StructureStats,
|
|
56
|
+
'packageConfigs' : StructureStats,
|
|
57
|
+
'highestConfigs' : StructureStats,
|
|
58
|
+
'packagePublications' : StructureStats,
|
|
59
|
+
'ownersByPackage' : StructureStats,
|
|
60
|
+
'maintainersByPackage' : StructureStats,
|
|
61
|
+
'fileIdsByPackage' : StructureStats,
|
|
62
|
+
'hashByFileId' : StructureStats,
|
|
63
|
+
'packageFileStats' : StructureStats,
|
|
64
|
+
'packageTestStats' : StructureStats,
|
|
65
|
+
'packageBenchmarks' : StructureStats,
|
|
66
|
+
'packageNotes' : StructureStats,
|
|
67
|
+
'packageDocsCoverage' : StructureStats,
|
|
68
|
+
'downloadsByPackageName' : StructureStats,
|
|
69
|
+
'downloadsByPackageId' : StructureStats,
|
|
70
|
+
'dailySnapshots' : StructureStats,
|
|
71
|
+
'weeklySnapshots' : StructureStats,
|
|
72
|
+
'dailySnapshotsByPackageName' : StructureStats,
|
|
73
|
+
'dailySnapshotsByPackageId' : StructureStats,
|
|
74
|
+
'weeklySnapshotsByPackageName' : StructureStats,
|
|
75
|
+
'weeklySnapshotsByPackageId' : StructureStats,
|
|
76
|
+
'dailyTempRecords' : StructureStats,
|
|
77
|
+
'weeklyTempRecords' : StructureStats,
|
|
78
|
+
'storages' : StructureStats,
|
|
79
|
+
'storageByFileId' : StructureStats,
|
|
80
|
+
'users' : StructureStats,
|
|
81
|
+
'names' : StructureStats,
|
|
82
|
+
}
|
|
48
83
|
export interface Main {
|
|
49
84
|
'addMaintainer' : ActorMethod<[PackageName, Principal], Result_3>,
|
|
50
85
|
'addOwner' : ActorMethod<[PackageName, Principal], Result_3>,
|
|
@@ -77,6 +112,7 @@ export interface Main {
|
|
|
77
112
|
Result_6
|
|
78
113
|
>,
|
|
79
114
|
'getHighestVersion' : ActorMethod<[PackageName], Result_5>,
|
|
115
|
+
'getMemoryStats' : ActorMethod<[], MemoryStats, 'query'>,
|
|
80
116
|
'getMostDownloadedPackages' : ActorMethod<[], Array<PackageSummary>>,
|
|
81
117
|
'getMostDownloadedPackagesIn7Days' : ActorMethod<[], Array<PackageSummary>>,
|
|
82
118
|
'getNewPackages' : ActorMethod<[], Array<PackageSummary>>,
|
|
@@ -196,6 +196,41 @@ export const idlFactory = ({ IDL }) => {
|
|
|
196
196
|
'cyclesBalance' : IDL.Nat,
|
|
197
197
|
'memorySize' : IDL.Nat,
|
|
198
198
|
});
|
|
199
|
+
const StructureStats = IDL.Record({
|
|
200
|
+
'count' : IDL.Nat,
|
|
201
|
+
'bytes' : IDL.Nat,
|
|
202
|
+
});
|
|
203
|
+
const MemoryStats = IDL.Record({
|
|
204
|
+
'rtsHeapSize' : IDL.Nat,
|
|
205
|
+
'rtsMemorySize' : IDL.Nat,
|
|
206
|
+
'packageVersions' : StructureStats,
|
|
207
|
+
'packageConfigs' : StructureStats,
|
|
208
|
+
'highestConfigs' : StructureStats,
|
|
209
|
+
'packagePublications' : StructureStats,
|
|
210
|
+
'ownersByPackage' : StructureStats,
|
|
211
|
+
'maintainersByPackage' : StructureStats,
|
|
212
|
+
'fileIdsByPackage' : StructureStats,
|
|
213
|
+
'hashByFileId' : StructureStats,
|
|
214
|
+
'packageFileStats' : StructureStats,
|
|
215
|
+
'packageTestStats' : StructureStats,
|
|
216
|
+
'packageBenchmarks' : StructureStats,
|
|
217
|
+
'packageNotes' : StructureStats,
|
|
218
|
+
'packageDocsCoverage' : StructureStats,
|
|
219
|
+
'downloadsByPackageName' : StructureStats,
|
|
220
|
+
'downloadsByPackageId' : StructureStats,
|
|
221
|
+
'dailySnapshots' : StructureStats,
|
|
222
|
+
'weeklySnapshots' : StructureStats,
|
|
223
|
+
'dailySnapshotsByPackageName' : StructureStats,
|
|
224
|
+
'dailySnapshotsByPackageId' : StructureStats,
|
|
225
|
+
'weeklySnapshotsByPackageName' : StructureStats,
|
|
226
|
+
'weeklySnapshotsByPackageId' : StructureStats,
|
|
227
|
+
'dailyTempRecords' : StructureStats,
|
|
228
|
+
'weeklyTempRecords' : StructureStats,
|
|
229
|
+
'storages' : StructureStats,
|
|
230
|
+
'storageByFileId' : StructureStats,
|
|
231
|
+
'users' : StructureStats,
|
|
232
|
+
'names' : StructureStats,
|
|
233
|
+
});
|
|
199
234
|
const Header = IDL.Tuple(IDL.Text, IDL.Text);
|
|
200
235
|
const Request = IDL.Record({
|
|
201
236
|
'url' : IDL.Text,
|
|
@@ -308,6 +343,7 @@ export const idlFactory = ({ IDL }) => {
|
|
|
308
343
|
['query'],
|
|
309
344
|
),
|
|
310
345
|
'getHighestVersion' : IDL.Func([PackageName], [Result_5], ['query']),
|
|
346
|
+
'getMemoryStats' : IDL.Func([], [MemoryStats], ['query']),
|
|
311
347
|
'getMostDownloadedPackages' : IDL.Func(
|
|
312
348
|
[],
|
|
313
349
|
[IDL.Vec(PackageSummary)],
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MigrationsConfig } from "../types.js";
|
|
2
|
+
export interface MigrationArgsResult {
|
|
3
|
+
migrationArgs: string[];
|
|
4
|
+
cleanup: () => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare function getMigrationFiles(dir: string): string[];
|
|
7
|
+
export declare function getNextMigrationFile(nextDir: string): string | null;
|
|
8
|
+
export declare function validateNextMigrationOrder(chainDirOrFiles: string | string[], nextFile: string): void;
|
|
9
|
+
export declare function validateMigrationsConfig(migrations: MigrationsConfig, canisterName: string): void;
|
|
10
|
+
export declare function prepareMigrationArgs(migrations: MigrationsConfig | undefined, canisterName: string, mode: "check" | "build", verbose?: boolean): Promise<MigrationArgsResult>;
|