diff --git a/package-lock.json b/package-lock.json index 150d92d1..8b73127d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@dfinity/ic-management": "^6.0.0", "@dfinity/identity": "^2.1.3", "@dfinity/principal": "^2.1.3", - "@junobuild/admin": "^0.0.58-next-2024-11-22", + "@junobuild/admin": "^0.0.58-next-2024-11-24", "@junobuild/cli-tools": "^0.0.16", "@junobuild/config-loader": "^0.0.7", "@junobuild/core-peer": "^0.0.29", @@ -1316,9 +1316,9 @@ } }, "node_modules/@junobuild/admin": { - "version": "0.0.58-next-2024-11-22", - "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.0.58-next-2024-11-22.tgz", - "integrity": "sha512-uqtRkK/TPr9GqpkUZHX8PGN53T8nRDKtZ5tzJ1je/+vT525MWSAGWC2zVvBu6sGQq4D1TsUmCM1ERLbzcGprqA==", + "version": "0.0.58-next-2024-11-24", + "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.0.58-next-2024-11-24.tgz", + "integrity": "sha512-Yz54O4v/3LqmsmAMJWkqsaA6rpFtObCaPaJMxX8wnNymcWlYy3Du23MdFv+BGCow9yHtKr40Iq0A54YMH8t48g==", "license": "MIT", "peerDependencies": { "@dfinity/agent": "*", @@ -7440,9 +7440,9 @@ } }, "@junobuild/admin": { - "version": "0.0.58-next-2024-11-22", - "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.0.58-next-2024-11-22.tgz", - "integrity": "sha512-uqtRkK/TPr9GqpkUZHX8PGN53T8nRDKtZ5tzJ1je/+vT525MWSAGWC2zVvBu6sGQq4D1TsUmCM1ERLbzcGprqA==", + "version": "0.0.58-next-2024-11-24", + "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.0.58-next-2024-11-24.tgz", + "integrity": "sha512-Yz54O4v/3LqmsmAMJWkqsaA6rpFtObCaPaJMxX8wnNymcWlYy3Du23MdFv+BGCow9yHtKr40Iq0A54YMH8t48g==", "requires": {} }, "@junobuild/cli-tools": { diff --git a/package.json b/package.json index e0dbb503..e5d2ab6b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@dfinity/ic-management": "^6.0.0", "@dfinity/identity": "^2.1.3", "@dfinity/principal": "^2.1.3", - "@junobuild/admin": "^0.0.58-next-2024-11-22", + "@junobuild/admin": "^0.0.58-next-2024-11-24", "@junobuild/cli-tools": "^0.0.16", "@junobuild/config-loader": "^0.0.7", "@junobuild/core-peer": "^0.0.29", diff --git a/src/help/upgrade.help.ts b/src/help/upgrade.help.ts index ddda72e5..a1e35906 100644 --- a/src/help/upgrade.help.ts +++ b/src/help/upgrade.help.ts @@ -11,6 +11,7 @@ Options: ${yellow('-s, --src')} A local gzipped wasm file for the upgrade. ${yellow('-r, --reset')} Reset to the initial state. ${yellow('-n, --nocheck')} Skip assertions and execute upgrade without prompts. + ${yellow('-c, --clear-chunks')} Clear any previously uploaded WASM chunks (applies if the WASM size is greater than 2MB). ${helpMode} ${yellow('-h, --help')} Output usage information. diff --git a/src/services/upgrade/upgrade.mission-control.services.ts b/src/services/upgrade/upgrade.mission-control.services.ts index 5bade65d..511514be 100644 --- a/src/services/upgrade/upgrade.mission-control.services.ts +++ b/src/services/upgrade/upgrade.mission-control.services.ts @@ -11,7 +11,12 @@ import {MISSION_CONTROL_WASM_NAME} from '../../constants/constants'; import type {UpgradeWasmModule} from '../../types/upgrade'; import {actorParameters} from '../../utils/actor.utils'; import {NEW_CMD_LINE} from '../../utils/prompt.utils'; -import {selectVersion, upgradeWasmCdn, upgradeWasmLocal} from './upgrade.services'; +import { + consoleUpgradeResult, + selectVersion, + upgradeWasmCdn, + upgradeWasmLocal +} from './upgrade.services'; export const upgradeMissionControl = async (args?: string[]) => { const missionControl = await getCliMissionControl(); @@ -34,20 +39,20 @@ export const upgradeMissionControl = async (args?: string[]) => { ...(await actorParameters()) }; - const consoleSuccess = () => { - console.log(`✅ Mission control successfully upgraded.`); + const consoleResult = (result: {success: boolean; err?: unknown}) => { + consoleUpgradeResult({...result, successMessage: 'Mission control successfully upgraded.'}); }; if (hasArgs({args, options: ['-s', '--src']})) { - await upgradeMissionControlCustom({args, missionControlParameters}); + const result = await upgradeMissionControlCustom({args, missionControlParameters}); - consoleSuccess(); + consoleResult(result); return; } - await updateMissionControlRelease({args, missionControlParameters}); + const result = await updateMissionControlRelease({args, missionControlParameters}); - consoleSuccess(); + consoleResult(result); }; const updateMissionControlRelease = async ({ @@ -56,7 +61,7 @@ const updateMissionControlRelease = async ({ }: { args?: string[]; missionControlParameters: MissionControlParameters; -}) => { +}): Promise<{success: boolean; err?: unknown}> => { const currentVersion = await missionControlVersion({ missionControl: missionControlParameters }); @@ -69,19 +74,21 @@ const updateMissionControlRelease = async ({ }); if (version === undefined) { - return; + return {success: false}; } const nocheck = hasArgs({args, options: ['-n', '--nocheck']}); + const preClearChunks = hasArgs({args, options: ['-c', '--clear-chunks']}); const upgradeMissionControlWasm = async (params: UpgradeWasmModule) => { await upgradeMissionControlAdmin({ missionControl: missionControlParameters, + preClearChunks, ...params }); }; - await upgradeWasmCdn({ + return await upgradeWasmCdn({ version, nocheck, assetKey: 'mission_control', @@ -95,22 +102,24 @@ const upgradeMissionControlCustom = async ({ }: { missionControlParameters: MissionControlParameters; args?: string[]; -}) => { +}): Promise<{success: boolean; err?: unknown}> => { const src = nextArg({args, option: '-s'}) ?? nextArg({args, option: '--src'}); if (src === undefined) { console.log(`${red('No source file provided.')}`); - return; + return {success: false}; } const nocheck = hasArgs({args, options: ['-n', '--nocheck']}); + const preClearChunks = hasArgs({args, options: ['-c', '--clear-chunks']}); const upgradeMissionControlWasm = async (params: UpgradeWasmModule) => { await upgradeMissionControlAdmin({ missionControl: missionControlParameters, + preClearChunks, ...params }); }; - await upgradeWasmLocal({src, nocheck, upgrade: upgradeMissionControlWasm}); + return await upgradeWasmLocal({src, nocheck, upgrade: upgradeMissionControlWasm}); }; diff --git a/src/services/upgrade/upgrade.orbiter.services.ts b/src/services/upgrade/upgrade.orbiter.services.ts index 136d4721..9e2374e4 100644 --- a/src/services/upgrade/upgrade.orbiter.services.ts +++ b/src/services/upgrade/upgrade.orbiter.services.ts @@ -11,7 +11,13 @@ import type {UpgradeWasmModule} from '../../types/upgrade'; import {actorParameters} from '../../utils/actor.utils'; import {NEW_CMD_LINE} from '../../utils/prompt.utils'; import {orbiterKey} from '../../utils/satellite.utils'; -import {confirmReset, selectVersion, upgradeWasmCdn, upgradeWasmLocal} from './upgrade.services'; +import { + confirmReset, + consoleUpgradeResult, + selectVersion, + upgradeWasmCdn, + upgradeWasmLocal +} from './upgrade.services'; export const upgradeOrbiters = async (args?: string[]) => { const authOrbiters = await getCliOrbiters(); @@ -28,20 +34,20 @@ export const upgradeOrbiters = async (args?: string[]) => { ...(await actorParameters()) }; - const consoleSuccess = () => { - console.log(`✅ Orbiter successfully upgraded.`); + const consoleResult = (result: {success: boolean; err?: unknown}) => { + consoleUpgradeResult({...result, successMessage: 'Orbiter successfully upgraded.'}); }; if (hasArgs({args, options: ['-s', '--src']})) { - await upgradeOrbiterCustom({args, orbiterParameters}); + const result = await upgradeOrbiterCustom({args, orbiterParameters}); - consoleSuccess(); + consoleResult(result); return; } - await updateOrbiterRelease(orbiterParameters); + const result = await updateOrbiterRelease(orbiterParameters); - consoleSuccess(); + consoleResult(result); }; for (const orbiter of authOrbiters) { @@ -55,34 +61,39 @@ const upgradeOrbiterCustom = async ({ }: { orbiterParameters: OrbiterParameters; args?: string[]; -}) => { +}): Promise<{success: boolean; err?: unknown}> => { const src = nextArg({args, option: '-s'}) ?? nextArg({args, option: '--src'}); if (src === undefined) { console.log(`${red('No source file provided.')}`); - return; + return {success: false}; } const reset = await confirmReset({args, assetKey: 'orbiter'}); const nocheck = hasArgs({args, options: ['-n', '--nocheck']}); + const preClearChunks = hasArgs({args, options: ['-c', '--clear-chunks']}); const upgradeOrbiterWasm = async (params: UpgradeWasmModule) => { await upgradeOrbiterAdmin({ orbiter: orbiterParameters, ...params, - ...(reset && {reset}) + ...(reset && {reset}), + preClearChunks }); }; - await upgradeWasmLocal({src, nocheck, upgrade: upgradeOrbiterWasm, reset}); + return await upgradeWasmLocal({src, nocheck, upgrade: upgradeOrbiterWasm, reset}); }; const updateOrbiterRelease = async ({ args, ...orbiterParameters }: Required> & - Omit & {args?: string[]}) => { + Omit & {args?: string[]}): Promise<{ + success: boolean; + err?: unknown; +}> => { const currentVersion = await orbiterVersion({ orbiter: orbiterParameters }); @@ -95,19 +106,28 @@ const updateOrbiterRelease = async ({ }); if (version === undefined) { - return; + return {success: false}; } const reset = await confirmReset({args, assetKey: 'orbiter'}); + const nocheck = hasArgs({args, options: ['-n', '--nocheck']}); + const preClearChunks = hasArgs({args, options: ['-c', '--clear-chunks']}); const upgradeOrbiterWasm = async (params: UpgradeWasmModule) => { await upgradeOrbiterAdmin({ orbiter: orbiterParameters, ...params, - ...(reset && {reset}) + ...(reset && {reset}), + preClearChunks }); }; - await upgradeWasmCdn({version, assetKey: 'orbiter', upgrade: upgradeOrbiterWasm, reset, nocheck}); + return await upgradeWasmCdn({ + version, + assetKey: 'orbiter', + upgrade: upgradeOrbiterWasm, + reset, + nocheck + }); }; diff --git a/src/services/upgrade/upgrade.satellite.services.ts b/src/services/upgrade/upgrade.satellite.services.ts index 82fe7d71..3940c9b9 100644 --- a/src/services/upgrade/upgrade.satellite.services.ts +++ b/src/services/upgrade/upgrade.satellite.services.ts @@ -18,6 +18,7 @@ import {satelliteKey, satelliteParameters} from '../../utils/satellite.utils'; import {assertSatelliteBuildType} from './upgrade-assert.services'; import { confirmReset, + consoleUpgradeResult, redoCustomDomains, selectVersion, upgradeWasmCdn, @@ -40,20 +41,20 @@ export const upgradeSatellite = async (args?: string[]) => { `${NEW_CMD_LINE}Initiating upgrade for satellite ${cyan(satelliteId)}.${NEW_CMD_LINE}` ); - const consoleSuccess = () => { - console.log(`✅ Satellite successfully upgraded.`); + const consoleResult = (result: {success: boolean; err?: unknown}) => { + consoleUpgradeResult({...result, successMessage: 'Satellite successfully upgraded.'}); }; if (hasArgs({args, options: ['-s', '--src']})) { - await upgradeSatelliteCustom({satellite, args}); + const result = await upgradeSatelliteCustom({satellite, args}); - consoleSuccess(); + consoleResult(result); return; } - await upgradeSatelliteRelease({satellite, args}); + const result = await upgradeSatelliteRelease({satellite, args}); - consoleSuccess(); + consoleResult(result); }; const upgradeSatelliteCustom = async ({ @@ -62,12 +63,12 @@ const upgradeSatelliteCustom = async ({ }: { satellite: SatelliteParameters; args?: string[]; -}) => { +}): Promise<{success: boolean; err?: unknown}> => { const src = nextArg({args, option: '-s'}) ?? nextArg({args, option: '--src'}); if (src === undefined) { console.log(`${red('No source file provided.')}`); - return; + return {success: false}; } // TODO: option to be removed @@ -76,15 +77,19 @@ const upgradeSatelliteCustom = async ({ }); const nocheck = hasArgs({args, options: ['-n', '--nocheck']}); + const preClearChunks = hasArgs({args, options: ['-c', '--clear-chunks']}); - const upgrade = async (params: Pick) => { - await upgradeWasmLocal({src, nocheck, ...params}); + const upgrade = async ( + params: Pick + ): Promise<{success: boolean; err?: unknown}> => { + return await upgradeWasmLocal({src, nocheck, ...params}); }; - await executeUpgradeSatellite({ + return await executeUpgradeSatellite({ satellite, args, currentVersion, + preClearChunks, upgrade }); }; @@ -95,7 +100,7 @@ const upgradeSatelliteRelease = async ({ }: { satellite: SatelliteParameters; args?: string[]; -}) => { +}): Promise<{success: boolean; err?: unknown}> => { const currentVersion = await satelliteVersion({ satellite }); @@ -104,19 +109,23 @@ const upgradeSatelliteRelease = async ({ const version = await selectVersion({currentVersion, assetKey: SATELLITE_WASM_NAME, displayHint}); if (isNullish(version)) { - return; + return {success: false}; } const nocheck = hasArgs({args, options: ['-n', '--nocheck']}); + const preClearChunks = hasArgs({args, options: ['-c', '--clear-chunks']}); - const upgrade = async (params: Pick) => { - await upgradeWasmCdn({version, assetKey: 'satellite', nocheck, ...params}); + const upgrade = async ( + params: Pick + ): Promise<{success: boolean; err?: unknown}> => { + return await upgradeWasmCdn({version, assetKey: 'satellite', nocheck, ...params}); }; - await executeUpgradeSatellite({ + return await executeUpgradeSatellite({ satellite, args, currentVersion, + preClearChunks, upgrade }); }; @@ -125,13 +134,17 @@ const executeUpgradeSatellite = async ({ satellite, args, currentVersion, - upgrade + upgrade, + preClearChunks }: { satellite: SatelliteParameters; args?: string[]; currentVersion: string; - upgrade: (params: Pick) => Promise; -}) => { + preClearChunks: boolean; + upgrade: ( + params: Pick + ) => Promise<{success: boolean; err?: unknown}>; +}): Promise<{success: boolean; err?: unknown}> => { const reset = await confirmReset({args, assetKey: 'satellite'}); // Information we want to try to redo once the satellite has been updated and resetted @@ -144,7 +157,8 @@ const executeUpgradeSatellite = async ({ // TODO: option to be removed deprecated: compare(currentVersion, '0.0.7') < 0, deprecatedNoScope: compare(currentVersion, '0.0.9') < 0, - ...(reset && {reset}) + ...(reset && {reset}), + preClearChunks }); }; @@ -152,13 +166,23 @@ const executeUpgradeSatellite = async ({ await assertSatelliteBuildType({satellite, ...params}); }; - await upgrade({ + const {success, err} = await upgrade({ upgrade: upgradeSatelliteWasm, reset, assert }); - if (reset && customDomains.length > 0) { - await redoCustomDomains({satellite, domains: customDomains}); + if (!success) { + return {success, err}; } + + try { + if (reset && customDomains.length > 0) { + await redoCustomDomains({satellite, domains: customDomains}); + } + } catch (err) { + return {success: false, err}; + } + + return {success: true}; }; diff --git a/src/services/upgrade/upgrade.services.ts b/src/services/upgrade/upgrade.services.ts index 1c95e9af..6bb456d3 100644 --- a/src/services/upgrade/upgrade.services.ts +++ b/src/services/upgrade/upgrade.services.ts @@ -1,10 +1,12 @@ import { checkUpgradeVersion, setCustomDomains, + UpgradeCodeUnchangedError, type CustomDomain, type SatelliteParameters } from '@junobuild/admin'; import {assertAnswerCtrlC, downloadFromURL, hasArgs} from '@junobuild/cli-tools'; +import {isNullish} from '@junobuild/utils'; import {createHash} from 'crypto'; import {red, yellow} from 'kleur'; import {readFile} from 'node:fs/promises'; @@ -50,7 +52,10 @@ export const upgradeWasmLocal = async ({ nocheck }: { src: string; -} & Pick) => { +} & Pick): Promise<{ + success: boolean; + err?: unknown; +}> => { const loadWasm = async (file: string): Promise<{hash: string; wasm: Buffer}> => { const wasm = await readFile(file); @@ -68,9 +73,12 @@ export const upgradeWasmLocal = async ({ spinner.stop(); await executeUpgradeWasm({upgrade, wasm, hash, reset, assert, nocheck}); + + return {success: true}; } catch (err: unknown) { spinner.stop(); - throw err; + + return {success: false, err}; } }; @@ -84,7 +92,10 @@ export const upgradeWasmCdn = async ({ }: { version: string; assetKey: AssetKey; -} & Pick) => { +} & Pick): Promise<{ + success: boolean; + err?: unknown; +}> => { const downloadWasm = async (): Promise<{hash: string; wasm: Buffer}> => { const {hostname} = new URL(JUNO_CDN_URL); @@ -110,9 +121,12 @@ export const upgradeWasmCdn = async ({ spinner.stop(); await executeUpgradeWasm({upgrade, wasm, hash, reset, assert, nocheck}); + + return {success: true}; } catch (err: unknown) { spinner.stop(); - throw err; + + return {success: false, err}; } }; @@ -225,3 +239,29 @@ export const confirmReset = async ({ return true; }; + +export const consoleUpgradeResult = ({ + success, + err, + successMessage +}: { + successMessage: string; + success: boolean; + err?: unknown; +}) => { + if (success) { + console.log(`✅ ${successMessage}`); + return; + } + + if (isNullish(err)) { + return; + } + + if (err instanceof UpgradeCodeUnchangedError) { + console.log(`🙅‍♂️ ${err.message}`); + return; + } + + throw err; +};