ic-mops 0.32.2 → 0.33.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/.npmrc +1 -0
- package/cli.ts +10 -5
- package/commands/add.ts +15 -2
- package/commands/install-all.ts +15 -2
- package/commands/install.ts +3 -3
- package/commands/remove.ts +12 -1
- package/commands/sync.ts +34 -27
- package/commands/test/test.ts +7 -1
- package/commands/update.ts +10 -1
- package/declarations/main/main.did +40 -0
- package/declarations/main/main.did.d.ts +26 -0
- package/declarations/main/main.did.js +35 -0
- package/dist/cli.js +10 -5
- package/dist/commands/add.d.ts +7 -4
- package/dist/commands/add.js +7 -2
- package/dist/commands/install-all.d.ts +7 -4
- package/dist/commands/install-all.js +9 -2
- package/dist/commands/install.js +3 -3
- package/dist/commands/remove.d.ts +8 -5
- package/dist/commands/remove.js +3 -1
- package/dist/commands/sync.d.ts +5 -1
- package/dist/commands/sync.js +23 -21
- package/dist/commands/test/test.js +7 -1
- package/dist/commands/update.d.ts +7 -1
- package/dist/commands/update.js +3 -1
- package/dist/declarations/main/main.did +40 -0
- package/dist/declarations/main/main.did.d.ts +26 -0
- package/dist/declarations/main/main.did.js +35 -0
- package/dist/integrity.d.ts +5 -0
- package/dist/integrity.js +155 -0
- package/dist/package.json +2 -1
- package/dist/vessel.js +2 -2
- package/integrity.ts +188 -0
- package/package.json +2 -1
- package/vessel.ts +2 -2
package/integrity.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {sha256} from '@noble/hashes/sha256';
|
|
4
|
+
import {bytesToHex} from '@noble/hashes/utils';
|
|
5
|
+
import {getDependencyType, getRootDir, mainActor} from './mops.js';
|
|
6
|
+
import {resolvePackages} from './resolve-packages.js';
|
|
7
|
+
|
|
8
|
+
type LockFileV1 = {
|
|
9
|
+
version: 1;
|
|
10
|
+
mopsTomlHash: string;
|
|
11
|
+
hashes: Record<string, Record<string, string>>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export async function checkIntegrity(lock?: 'save' | 'check' | 'ignore') {
|
|
15
|
+
let force = !!lock;
|
|
16
|
+
|
|
17
|
+
if (!lock) {
|
|
18
|
+
lock = process.env['CI'] ? 'check' : 'ignore';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (lock === 'save') {
|
|
22
|
+
await saveLockFile();
|
|
23
|
+
await checkLockFile(force);
|
|
24
|
+
}
|
|
25
|
+
else if (lock === 'check') {
|
|
26
|
+
await checkLockFile(force);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function getFileHashesFromRegistry(): Promise<[string, [string, Uint8Array | number[]][]][]> {
|
|
31
|
+
let packageIds = await getResolvedMopsPackageIds();
|
|
32
|
+
let actor = await mainActor();
|
|
33
|
+
let fileHashesByPackageIds = await actor.getFileHashesByPackageIds(packageIds);
|
|
34
|
+
return fileHashesByPackageIds;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function getResolvedMopsPackageIds(): Promise<string[]> {
|
|
38
|
+
let resolvedPackages = await resolvePackages();
|
|
39
|
+
let packageIds = Object.entries(resolvedPackages)
|
|
40
|
+
.filter(([_, version]) => getDependencyType(version) === 'mops')
|
|
41
|
+
.map(([name, version]) => `${name}@${version}`);
|
|
42
|
+
return packageIds;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// get hash of local file from '.mops' dir by fileId
|
|
46
|
+
export function getLocalFileHash(fileId: string): string {
|
|
47
|
+
let rootDir = getRootDir();
|
|
48
|
+
let file = path.join(rootDir, '.mops', fileId);
|
|
49
|
+
if (!fs.existsSync(file)) {
|
|
50
|
+
console.error(`Missing file ${fileId} in .mops dir`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
let fileData = fs.readFileSync(file);
|
|
54
|
+
return bytesToHex(sha256(fileData));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getMopsTomlHash(): string {
|
|
58
|
+
return bytesToHex(sha256(fs.readFileSync(getRootDir() + '/mops.toml')));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// compare hashes of local files with hashes from the registry
|
|
62
|
+
export async function checkRemote() {
|
|
63
|
+
let fileHashesFromRegistry = await getFileHashesFromRegistry();
|
|
64
|
+
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
66
|
+
for (let [_packageId, fileHashes] of fileHashesFromRegistry) {
|
|
67
|
+
for (let [fileId, hash] of fileHashes) {
|
|
68
|
+
let remoteHash = new Uint8Array(hash);
|
|
69
|
+
let localHash = getLocalFileHash(fileId);
|
|
70
|
+
|
|
71
|
+
if (localHash !== bytesToHex(remoteHash)) {
|
|
72
|
+
console.error('Integrity check failed.');
|
|
73
|
+
console.error(`Mismatched hash for ${fileId}: ${localHash} vs ${bytesToHex(remoteHash)}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function saveLockFile() {
|
|
81
|
+
let rootDir = getRootDir();
|
|
82
|
+
let lockFile = path.join(rootDir, 'mops.lock');
|
|
83
|
+
|
|
84
|
+
// if lock file exists and mops.toml hasn't changed, don't update it
|
|
85
|
+
if (fs.existsSync(lockFile)) {
|
|
86
|
+
let lockFileJson: LockFileV1 = JSON.parse(fs.readFileSync(lockFile).toString());
|
|
87
|
+
let mopsTomlHash = getMopsTomlHash();
|
|
88
|
+
if (mopsTomlHash === lockFileJson.mopsTomlHash) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let fileHashes = await getFileHashesFromRegistry();
|
|
94
|
+
|
|
95
|
+
let lockFileJson: LockFileV1 = {
|
|
96
|
+
version: 1,
|
|
97
|
+
mopsTomlHash: getMopsTomlHash(),
|
|
98
|
+
hashes: fileHashes.reduce((acc, [packageId, fileHashes]) => {
|
|
99
|
+
acc[packageId] = fileHashes.reduce((acc, [fileId, hash]) => {
|
|
100
|
+
acc[fileId] = bytesToHex(new Uint8Array(hash));
|
|
101
|
+
return acc;
|
|
102
|
+
}, {} as Record<string, string>);
|
|
103
|
+
return acc;
|
|
104
|
+
}, {} as Record<string, Record<string, string>>),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
fs.writeFileSync(lockFile, JSON.stringify(lockFileJson, null, 2));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// compare hashes of local files with hashes from the lock file
|
|
111
|
+
export async function checkLockFile(force = false) {
|
|
112
|
+
let rootDir = getRootDir();
|
|
113
|
+
let lockFile = path.join(rootDir, 'mops.lock');
|
|
114
|
+
|
|
115
|
+
// check if lock file exists
|
|
116
|
+
if (!fs.existsSync(lockFile)) {
|
|
117
|
+
if (force) {
|
|
118
|
+
console.error('Missing lock file. Run `mops install` to generate it.');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let lockFileJson: LockFileV1 = JSON.parse(fs.readFileSync(lockFile).toString());
|
|
125
|
+
let packageIds = await getResolvedMopsPackageIds();
|
|
126
|
+
|
|
127
|
+
// check lock file version
|
|
128
|
+
if (lockFileJson.version !== 1) {
|
|
129
|
+
console.error('Integrity check failed');
|
|
130
|
+
console.error(`Invalid lock file version: ${lockFileJson.version}. Supported versions: 1`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// check mops.toml hash
|
|
135
|
+
if (lockFileJson.mopsTomlHash !== getMopsTomlHash()) {
|
|
136
|
+
console.error('Integrity check failed');
|
|
137
|
+
console.error('Mismatched mops.toml hash');
|
|
138
|
+
console.error(`Locked hash: ${lockFileJson.mopsTomlHash}`);
|
|
139
|
+
console.error(`Actual hash: ${getMopsTomlHash()}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// check number of packages
|
|
144
|
+
if (Object.keys(lockFileJson.hashes).length !== packageIds.length) {
|
|
145
|
+
console.error('Integrity check failed');
|
|
146
|
+
console.error(`Mismatched number of resolved packages: ${JSON.stringify(Object.keys(lockFileJson.hashes).length)} vs ${JSON.stringify(packageIds.length)}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// check if resolved packages are in the lock file
|
|
151
|
+
for (let packageId of packageIds) {
|
|
152
|
+
if (!(packageId in lockFileJson.hashes)) {
|
|
153
|
+
console.error('Integrity check failed');
|
|
154
|
+
console.error(`Missing package ${packageId} in lock file`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (let [packageId, hashes] of Object.entries(lockFileJson.hashes)) {
|
|
160
|
+
|
|
161
|
+
// check if package is in resolved packages
|
|
162
|
+
if (!packageIds.includes(packageId)) {
|
|
163
|
+
console.error('Integrity check failed');
|
|
164
|
+
console.error(`Package ${packageId} in lock file but not in resolved packages`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (let [fileId, lockedHash] of Object.entries(hashes)) {
|
|
169
|
+
|
|
170
|
+
// check if file belongs to package
|
|
171
|
+
if (!fileId.startsWith(packageId)) {
|
|
172
|
+
console.error('Integrity check failed');
|
|
173
|
+
console.error(`File ${fileId} in lock file does not belong to package ${packageId}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// local file hash vs hash from lock file
|
|
178
|
+
let localHash = getLocalFileHash(fileId);
|
|
179
|
+
if (lockedHash !== localHash) {
|
|
180
|
+
console.error('Integrity check failed');
|
|
181
|
+
console.error(`Mismatched hash for ${fileId}`);
|
|
182
|
+
console.error(`Locked hash: ${lockedHash}`);
|
|
183
|
+
console.error(`Actual hash: ${localHash}`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ic-mops",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "dist/cli.js"
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"@dfinity/identity-secp256k1": "^0.18.1",
|
|
39
39
|
"@dfinity/principal": "^0.18.1",
|
|
40
40
|
"@iarna/toml": "^2.2.5",
|
|
41
|
+
"@noble/hashes": "1.3.2",
|
|
41
42
|
"as-table": "^1.0.55",
|
|
42
43
|
"cacheable-request": "10.2.12",
|
|
43
44
|
"camelcase": "^7.0.1",
|
package/vessel.ts
CHANGED
|
@@ -154,11 +154,11 @@ export const installFromGithub = async (name: string, repo: string, {verbose = f
|
|
|
154
154
|
let cacheName = `_github/${name}#${branch}` + (commitHash ? `@${commitHash}` : '');
|
|
155
155
|
|
|
156
156
|
if (existsSync(dir)) {
|
|
157
|
-
silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (local cache)
|
|
157
|
+
silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (local cache)`);
|
|
158
158
|
}
|
|
159
159
|
else if (isCached(cacheName)) {
|
|
160
160
|
await copyCache(cacheName, dir);
|
|
161
|
-
silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (global cache)
|
|
161
|
+
silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (global cache)`);
|
|
162
162
|
}
|
|
163
163
|
else {
|
|
164
164
|
mkdirSync(dir, {recursive: true});
|