diff --git a/HACKING.md b/HACKING.md index 5a40d306..3606f359 100644 --- a/HACKING.md +++ b/HACKING.md @@ -41,7 +41,6 @@ docker run -it \ -p 5999:5999 \ -p 5866:5866 \ -v juno_skylab_test:/juno/.juno \ - -v "$(pwd)/juno.config.mjs:/juno/juno.config.mjs" \ -v "$(pwd)/target/deploy:/juno/target/deploy" \ junobuild/skylab:latest ``` diff --git a/README.md b/README.md index 5f35050b..d3dbe577 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,6 @@ docker run -it \ -p 5999:5999 \ -p 5866:5866 \ -v juno_skylab_test:/juno/.juno \ - -v "$(pwd)/juno.config.mjs:/juno/juno.config.mjs" \ -v "$(pwd)/target/deploy:/juno/target/deploy" \ junobuild/skylab:latest ``` diff --git a/cli/.env.satellite b/cli/.env.satellite index 26c66ace..36d4c068 100644 --- a/cli/.env.satellite +++ b/cli/.env.satellite @@ -1,2 +1,2 @@ MODULES="satellite" -WATCHERS="satellite, sputnik" \ No newline at end of file +WATCHERS="" \ No newline at end of file diff --git a/cli/.env.skylab b/cli/.env.skylab index 946136ef..ce1569b9 100644 --- a/cli/.env.skylab +++ b/cli/.env.skylab @@ -1,2 +1,2 @@ MODULES="console, observatory" -WATCHERS="satellite-dynamic, sputnik" \ No newline at end of file +WATCHERS="" \ No newline at end of file diff --git a/cli/src/commands/server.ts b/cli/src/commands/server.ts index 901e94fa..92056401 100644 --- a/cli/src/commands/server.ts +++ b/cli/src/commands/server.ts @@ -14,7 +14,8 @@ import {buildContext} from '../services/context.services'; import {collectIdentities} from '../services/identity.services'; import {setController} from '../services/server/controller.services'; import {transfer} from '../services/server/ledger.services'; -import {touchWatchedFile} from '../services/server/touch.services'; +import {upgradeSatellite} from '../services/server/satellite.services'; +import {buildSputnik} from '../services/server/sputnik.services'; import type {CliContext} from '../types/context'; import {nextArg} from '../utils/args.utils'; @@ -95,6 +96,8 @@ const buildServer = ({context}: {context: CliContext}): Server => return; } + const consoleBuild = process.env.CLI_BUILD === 'console'; + if (command === 'satellite') { switch (subCommand) { case 'controller': @@ -107,6 +110,34 @@ const buildServer = ({context}: {context: CliContext}): Server => return; } + if (!consoleBuild) { + switch (subCommand) { + case 'upgrade': + // We don't await the promise on purpose given the process takes some time. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + upgradeSatellite({ + context, + searchParams + }); + done(); + return; + } + } + + error404(); + return; + } + + if (!consoleBuild && command === 'sputnik') { + switch (subCommand) { + case 'build': + // We don't await the promise on purpose given the process takes some time. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + buildSputnik(); + done(); + return; + } + error404(); return; } @@ -125,11 +156,6 @@ const buildServer = ({context}: {context: CliContext}): Server => res.end(JSON.stringify(identities)); return; } - case 'touch': { - await touchWatchedFile({searchParams}); - done(); - return; - } } error404(); diff --git a/cli/src/configs/juno.config.ts b/cli/src/configs/juno.config.ts deleted file mode 100644 index f03ff70d..00000000 --- a/cli/src/configs/juno.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {JunoConfig, JunoConfigFnOrObject} from '@junobuild/config'; -import type {ConfigFilename} from '@junobuild/config-loader'; -import {readJunoConfig as readJunoConfigTools} from '@junobuild/config-loader'; -import {JUNO_CONFIG_FILENAME} from '../constants/dev.constants'; - -const JUNO_CONFIG_FILE: {filename: ConfigFilename} = {filename: JUNO_CONFIG_FILENAME}; - -export const readJunoConfig = async (): Promise => { - const config = (userConfig: JunoConfigFnOrObject): JunoConfig => - typeof userConfig === 'function' ? userConfig({mode: 'development'}) : userConfig; - - return await readJunoConfigTools({ - ...JUNO_CONFIG_FILE, - config - }); -}; diff --git a/cli/src/constants/dev.constants.ts b/cli/src/constants/dev.constants.ts index a1c4d522..c0f9dbd3 100644 --- a/cli/src/constants/dev.constants.ts +++ b/cli/src/constants/dev.constants.ts @@ -20,12 +20,6 @@ export const DEV_MISSION_CONTROL_WASM_FILENAME = 'mission_control.wasm.gz'; export const DEV_METADATA = join(DEV_DEPLOY_FOLDER, 'metadata.json'); -/** - * Configuration - */ - -export const JUNO_CONFIG_FILENAME = 'juno.config'; // .json | .js | .cjs | .mjs | .ts - /** * JS/TS */ diff --git a/cli/src/modules/satellite/dynamic.ts b/cli/src/modules/satellite/dynamic.ts index 74e6f98b..44c819fb 100644 --- a/cli/src/modules/satellite/dynamic.ts +++ b/cli/src/modules/satellite/dynamic.ts @@ -1,15 +1,9 @@ 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 {DEV_SATELLITE} from '../../constants/dev.constants'; import type {CliContext} from '../../types/context'; import type {InitDynamicModuleResult, ModuleCanisterId} from '../../types/module'; import {SATELLITE, SatelliteModule} from './index'; @@ -87,37 +81,12 @@ class SatelliteDynamicModule extends SatelliteModule { } export const initSatelliteDynamicModule = async ({ - context + context, + satelliteId }: { context: CliContext; + satelliteId: ModuleCanisterId; }): Promise> => { - 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, diff --git a/cli/src/services/server/satellite.services.ts b/cli/src/services/server/satellite.services.ts new file mode 100644 index 00000000..b91721f3 --- /dev/null +++ b/cli/src/services/server/satellite.services.ts @@ -0,0 +1,26 @@ +import {assertNonNullish} from '@dfinity/utils'; +import {DEV_SATELLITE_WASM_FILENAME} from '../../constants/dev.constants'; +import {initSatelliteDynamicModule} from '../../modules/satellite/dynamic'; +import type {CliContext} from '../../types/context'; +import type {InitDynamicModuleResult} from '../../types/module'; +import {DeployWatcher} from '../../watch/_watchers/deploy.watcher'; + +export const upgradeSatellite = async ({ + context, + searchParams +}: { + context: CliContext; + searchParams: URLSearchParams; +}) => { + const satelliteId = searchParams.get('id'); + + assertNonNullish(satelliteId, 'The request must provide a satellite ID.'); + + const satelliteDynamicWatcher = new DeployWatcher({ + moduleFileName: DEV_SATELLITE_WASM_FILENAME, + initModule: async ({context}: {context: CliContext}): Promise => + await initSatelliteDynamicModule({context, satelliteId}) + }); + + await satelliteDynamicWatcher.onRequest({context}); +}; diff --git a/cli/src/services/modules/sputnik.services.ts b/cli/src/services/server/sputnik.services.ts similarity index 100% rename from cli/src/services/modules/sputnik.services.ts rename to cli/src/services/server/sputnik.services.ts diff --git a/cli/src/services/server/touch.services.ts b/cli/src/services/server/touch.services.ts deleted file mode 100644 index 44d31005..00000000 --- a/cli/src/services/server/touch.services.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {isEmptyString} from '@dfinity/utils'; -import kleur from 'kleur'; -import {existsSync} from 'node:fs'; -import {utimes} from 'node:fs/promises'; -import {join} from 'node:path'; -import {DEV_DEPLOY_FOLDER} from '../../constants/dev.constants'; - -const {yellow, red} = kleur; - -/** - * Workaround Podman and Apple container issues on macOS: - * - https://github.com/containers/podman/issues/22343 - * - https://github.com/apple/container/issues/141 - */ -export const touchWatchedFile = async ({searchParams}: {searchParams: URLSearchParams}) => { - const file = searchParams.get('file'); - - if (isEmptyString(file)) { - console.log(`ℹ️ Skipped touching file: no file provided.`); - return; - } - - const filename = decodeURIComponent(file); - const filepath = join(DEV_DEPLOY_FOLDER, filename); - - if (!existsSync(filepath)) { - console.log(`ℹ️ Skipped touching file: ${yellow(filepath)} does not exist.`); - return; - } - - try { - await touch(filepath); - } catch (err: unknown) { - console.log(red(`️‼️ Unexpected error while touching ${filepath}`), err); - } -}; - -const touch = async (filePath: string) => { - const now = new Date(); - await utimes(filePath, now, now); -}; diff --git a/cli/src/watch/_modules/satellite.ts b/cli/src/watch/_modules/satellite.ts deleted file mode 100644 index fb8809fb..00000000 --- a/cli/src/watch/_modules/satellite.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {DEV_SATELLITE_WASM_FILENAME} from '../../constants/dev.constants'; -import {initSatelliteModule} from '../../modules/satellite'; -import {initSatelliteDynamicModule} from '../../modules/satellite/dynamic'; -import type {InitDynamicModuleResult} from '../../types/module'; -import {DeployWatcher} from '../_watchers/deploy.watcher'; - -export const satelliteWatcher = new DeployWatcher({ - moduleFileName: DEV_SATELLITE_WASM_FILENAME, - initModule: async (): Promise => { - const mod = initSatelliteModule(); - return {mod}; - } -}); - -export const satelliteDynamicWatcher = new DeployWatcher({ - moduleFileName: DEV_SATELLITE_WASM_FILENAME, - initModule: initSatelliteDynamicModule -}); diff --git a/cli/src/watch/_modules/sputnik.ts b/cli/src/watch/_modules/sputnik.ts deleted file mode 100644 index 0cde5962..00000000 --- a/cli/src/watch/_modules/sputnik.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {DEV_SPUTNIK_MJS_FILENAME} from '../../constants/dev.constants'; -import {satellite} from '../../modules/satellite'; -import {buildSputnik} from '../../services/modules/sputnik.services'; -import {BuildWatcher} from '../_watchers/build.watcher'; - -export const sputnikWatcher = new BuildWatcher({ - moduleFileName: DEV_SPUTNIK_MJS_FILENAME, - key: satellite.key, - name: satellite.name, - build: buildSputnik -}); diff --git a/cli/src/watch/_watchers/_watcher.ts b/cli/src/watch/_watchers/_watcher.ts index 42de0fd4..13b92332 100644 --- a/cli/src/watch/_watchers/_watcher.ts +++ b/cli/src/watch/_watchers/_watcher.ts @@ -30,6 +30,15 @@ export abstract class Watcher { this.#debounceExec({context}); }; + onRequest = async ({context}: {context: CliContext}) => { + if (this.executing) { + this.#requestExecution = true; + return; + } + + this.#debounceExec({context}); + }; + private readonly exec = async ({context}: {context: CliContext}) => { this.executing = true; diff --git a/cli/src/watch/_watchers/build.watcher.ts b/cli/src/watch/_watchers/build.watcher.ts deleted file mode 100644 index 999794ac..00000000 --- a/cli/src/watch/_watchers/build.watcher.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type {CliContext} from '../../types/context'; -import type {WatcherBuildDescription} from '../_types/watcher'; -import {Watcher} from './_watcher'; - -export class BuildWatcher extends Watcher { - readonly #moduleName: string; - readonly #build: () => Promise; - - constructor({moduleFileName, build, name}: WatcherBuildDescription) { - super({moduleFileName}); - this.#moduleName = name; - this.#build = build; - } - - protected async onExec(_params: {context: CliContext}) { - console.log(`🌀 Building ${this.#moduleName}...`); - - await this.execute(); - } - - private async execute() { - try { - await this.#build(); - } finally { - this.executing = false; - } - } -} diff --git a/cli/src/watch/watchers.ts b/cli/src/watch/watchers.ts index e5ddd142..0fb0f8e6 100644 --- a/cli/src/watch/watchers.ts +++ b/cli/src/watch/watchers.ts @@ -1,33 +1,17 @@ import {notEmptyString} from '@dfinity/utils'; import {consoleModule} from '../modules/console'; import {observatory} from '../modules/observatory'; -import {satellite} from '../modules/satellite'; import type {ModuleKey} from '../types/module'; import {consoleWatchers} from './_modules/console'; import {observatoryWatcher} from './_modules/oberservatory'; -import {satelliteDynamicWatcher, satelliteWatcher} from './_modules/satellite'; -import {sputnikWatcher} from './_modules/sputnik'; import type {Watcher} from './_watchers/_watcher'; interface WatcherKey { - // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents - key: ModuleKey | `${ModuleKey}-dynamic` | 'sputnik'; + key: ModuleKey; watcher: Watcher; } const WATCHERS: WatcherKey[] = [ - { - key: satellite.key, - watcher: satelliteWatcher - }, - { - key: `${satellite.key}-dynamic`, - watcher: satelliteDynamicWatcher - }, - { - key: 'sputnik', - watcher: sputnikWatcher - }, ...consoleWatchers.map((watcher) => ({ key: consoleModule.key, watcher