-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdynamic.ts
136 lines (112 loc) · 4.23 KB
/
dynamic.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import {ICManagementCanister} from '@dfinity/ic-management';
import {Principal} from '@dfinity/principal';
import {fromNullable, isNullish, nonNullish, uint8ArrayToHexString} from '@dfinity/utils';
import {
junoConfigExist as junoConfigExistTools,
junoConfigFile as junoConfigFileTools
} from '@junobuild/config-loader';
import kleur from 'kleur';
import {existsSync} from 'node:fs';
import {basename} from 'node:path';
import {readJunoConfig} from '../../configs/juno.config';
import {DEV_SATELLITE, JUNO_CONFIG_FILENAME} from '../../constants/dev.constants';
import type {CliContext} from '../../types/context';
import type {ModuleCanisterId} from '../../types/module';
import type {WatcherDeployInitModuleResult} from '../../watch/_types/watcher';
import {SATELLITE, SatelliteModule} from './index';
const {red, cyan} = kleur;
interface SatelliteDynamicModuleRegisterParams {
context: CliContext;
canisterId: ModuleCanisterId;
}
class SatelliteDynamicModule extends SatelliteModule {
async register({context, canisterId}: SatelliteDynamicModuleRegisterParams): Promise<void> {
const {state} = context;
const metadata = state.getModule(this.key);
// We check the canisterId in case the developer update their configuration at runtime.
if (nonNullish(metadata) && metadata.canisterId === canisterId) {
// Satellite has already been registered and initialized once. We do not need to add it to the state again.
return;
}
// We gather the current hash of the canister by fetching the canister status with th IC mgmt.
// This is useful otherwise dev would have to build twice to make the watcher notice the hash is really different and upgrade the satellite.
// Plus, this allows to preventively checks if the main identity is a controller of the Satellite.
let hash: string | undefined = undefined;
try {
hash = await this.currentModuleHash({context, canisterId});
} catch (err: unknown) {
console.log(red('️‼️ Unexpected error while fetching current module hash:'), err);
throw err;
}
if (isNullish(hash)) {
const err = new Error(
`‼️ The module hash for ${SATELLITE.name}, ID: ${cyan(canisterId.toString())} is undefined.`
);
console.log(red(err.message));
throw err;
}
state.saveModule({
key: this.key,
name: this.name,
canisterId,
// Deployed because the Satellite was created by the dev with the Console UI
status: 'deployed',
hash
});
}
private async currentModuleHash({
context,
canisterId
}: SatelliteDynamicModuleRegisterParams): Promise<string | undefined> {
const {agent} = context;
const {canisterStatus} = ICManagementCanister.create({
agent
});
const {module_hash} = await canisterStatus(Principal.from(canisterId));
const installedHash = fromNullable(module_hash);
if (isNullish(installedHash)) {
return undefined;
}
return uint8ArrayToHexString(installedHash);
}
}
export const initSatelliteDynamicModule = async ({
context
}: {
context: CliContext;
}): Promise<WatcherDeployInitModuleResult> => {
if (!(await junoConfigExistTools({filename: JUNO_CONFIG_FILENAME}))) {
const err = new Error(
`ℹ️ No configuration file provided. Skipping upgrade of ${SATELLITE.name}.`
);
console.log(err.message);
return {
err
};
}
const config = await readJunoConfig();
const satelliteId = config.satellite.ids?.development;
if (isNullish(satelliteId)) {
const {configPath} = junoConfigFileTools({filename: JUNO_CONFIG_FILENAME});
const err = new Error(
`ℹ️ No ${SATELLITE.name} provided in ${basename(configPath)}. Skipping upgrade.`
);
console.log(err.message);
return {
err
};
}
const mod = new SatelliteDynamicModule({
...SATELLITE,
canisterId: satelliteId,
...(existsSync(DEV_SATELLITE) && {wasmPath: DEV_SATELLITE})
});
try {
// The Satellite is not attached to a static canister ID and is created by the DEV in the Console.
// That's why we need to register it in the state as if it was deployed - which it is.
await mod.register({context, canisterId: satelliteId});
} catch (err: unknown) {
return {err};
}
return {mod};
};