From cb93d93bbb7a5f82a1e44ba1c85275c5162ad595 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 2 Jun 2025 11:28:16 +0200 Subject: [PATCH 01/10] feat: juno functions upgrade --- src/commands/functions.ts | 4 ++++ src/constants/help.constants.ts | 1 + src/help/functions.help.ts | 1 + src/help/functions.upgrade.help.ts | 34 ++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 src/help/functions.upgrade.help.ts diff --git a/src/commands/functions.ts b/src/commands/functions.ts index 145d177b..79b7e853 100644 --- a/src/commands/functions.ts +++ b/src/commands/functions.ts @@ -2,6 +2,7 @@ import {red} from 'kleur'; import {logHelpFunctionsBuild} from '../help/functions.build.help'; import {logHelpFunctionsEject} from '../help/functions.eject.help'; import {logHelpFunctions} from '../help/functions.help'; +import {logHelpFunctionsUpgrade} from '../help/functions.upgrade.help'; import {build} from '../services/build/build.services'; import {eject} from '../services/eject/eject.services'; @@ -31,6 +32,9 @@ export const helpFunctions = (args?: string[]) => { case 'eject': logHelpFunctionsEject(args); break; + case 'upgrade': + logHelpFunctionsUpgrade(args); + break; default: logHelpFunctions(args); } diff --git a/src/constants/help.constants.ts b/src/constants/help.constants.ts index b0a08d61..ae0a94ba 100644 --- a/src/constants/help.constants.ts +++ b/src/constants/help.constants.ts @@ -25,6 +25,7 @@ export const WHOAMI_DESCRIPTION = export const DEV_START_DESCRIPTION = 'Start a local Internet Computer network in a container.'; +export const FUNCTIONS_UPGRADE_DESCRIPTION = 'Upgrade your serverless functions.'; export const FUNCTIONS_BUILD_DESCRIPTION = 'Build your serverless functions.'; export const FUNCTIONS_EJECT_DESCRIPTION = 'Generate the required files to begin developing serverless functions in your project.'; diff --git a/src/help/functions.help.ts b/src/help/functions.help.ts index 0aac1f17..98870db1 100644 --- a/src/help/functions.help.ts +++ b/src/help/functions.help.ts @@ -8,6 +8,7 @@ const usage = `Usage: ${green('juno')} ${cyan('functions')} ${magenta(' { + console.log(helpOutput(args) === 'doc' ? doc : help); +}; From e8fa656af55f74c67e5a7a1f9eb09daf92490257 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 2 Jun 2025 12:44:52 +0200 Subject: [PATCH 02/10] feat: upgrade immediate --- src/commands/functions.ts | 4 +++ src/constants/dev.constants.ts | 2 ++ src/constants/help.constants.ts | 6 +++++ src/help/deploy.help.ts | 9 +++++-- src/help/functions.upgrade.help.ts | 16 +++++++++--- src/help/upgrade.help.ts | 6 ++--- .../functions/build/build.rust.services.ts | 2 +- src/services/functions/upgrade.services.ts | 26 +++++++++++++++++++ .../upgrade/upgrade.satellite.services.ts | 17 +++++++++++- 9 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/services/functions/upgrade.services.ts diff --git a/src/commands/functions.ts b/src/commands/functions.ts index ff6de0bc..ad6efe11 100644 --- a/src/commands/functions.ts +++ b/src/commands/functions.ts @@ -5,6 +5,7 @@ import {logHelpFunctions} from '../help/functions.help'; import {logHelpFunctionsUpgrade} from '../help/functions.upgrade.help'; import {build} from '../services/functions/build/build.services'; import {eject} from '../services/functions/eject/eject.services'; +import {upgradeFunctions} from '../services/functions/upgrade.services'; export const functions = async (args?: string[]) => { const [subCommand] = args ?? []; @@ -16,6 +17,9 @@ export const functions = async (args?: string[]) => { case 'build': await build(args); break; + case 'upgrade': + await upgradeFunctions(args); + break; default: console.log(red('Unknown subcommand.')); logHelpFunctions(args); diff --git a/src/constants/dev.constants.ts b/src/constants/dev.constants.ts index 7c0247b2..1211d338 100644 --- a/src/constants/dev.constants.ts +++ b/src/constants/dev.constants.ts @@ -45,3 +45,5 @@ export const PACKAGE_JSON_PATH = join(process.cwd(), 'package.json'); export const SPUTNIK_INDEX_MJS = 'sputnik.index.mjs'; export const DEPLOY_SPUTNIK_PATH = join(DEPLOY_LOCAL_REPLICA_PATH, SPUTNIK_INDEX_MJS); + +export const SATELLITE_OUTPUT = join(DEPLOY_LOCAL_REPLICA_PATH, 'satellite.wasm'); diff --git a/src/constants/help.constants.ts b/src/constants/help.constants.ts index ae0a94ba..1215b5fb 100644 --- a/src/constants/help.constants.ts +++ b/src/constants/help.constants.ts @@ -42,3 +42,9 @@ export const CHANGES_REJECT_DESCRIPTION = 'Reject a change.'; export const OPTION_KEEP_STAGED = `${yellow('-k, --keep-staged')} Keep staged assets in memory after applying the change.`; export const OPTION_HASH = `${yellow('-s, --hash')} The expected hash of all included changes (for verification).`; export const OPTION_HELP = `${yellow('-h, --help')} Output usage information.`; + +export const OPTIONS_UPGRADE = `${yellow('-r, --reset')} Reset to the initial state. + ${yellow('-cc, --clear-chunks')} Clear any previously uploaded WASM chunks (applies if the WASM size is greater than 2MB). + ${yellow('-ns, --no-snapshot')} Skip creating a snapshot before upgrading.`; + +export const NOTE_KEEP_STAGED = `The option ${yellow('--keep-staged')} only applies when ${yellow('--no-apply')} is NOT used (i.e. the change is applied immediately).`; diff --git a/src/help/deploy.help.ts b/src/help/deploy.help.ts index 2ef4d5b5..3245548b 100644 --- a/src/help/deploy.help.ts +++ b/src/help/deploy.help.ts @@ -1,5 +1,10 @@ import {cyan, green, yellow} from 'kleur'; -import {DEPLOY_DESCRIPTION, OPTION_HELP, OPTION_KEEP_STAGED} from '../constants/help.constants'; +import { + DEPLOY_DESCRIPTION, + NOTE_KEEP_STAGED, + OPTION_HELP, + OPTION_KEEP_STAGED +} from '../constants/help.constants'; import {helpMode, helpOutput} from './common.help'; import {TITLE} from './help'; @@ -15,7 +20,7 @@ Options: Notes: -- The option ${yellow('--keep-staged')} only applies when ${yellow('--no-apply')} is NOT used (i.e. the change is applied immediately).`; +- ${NOTE_KEEP_STAGED}`; const doc = `${DEPLOY_DESCRIPTION} diff --git a/src/help/functions.upgrade.help.ts b/src/help/functions.upgrade.help.ts index 17a06e97..29be2b78 100644 --- a/src/help/functions.upgrade.help.ts +++ b/src/help/functions.upgrade.help.ts @@ -1,8 +1,10 @@ import {cyan, green, magenta, yellow} from 'kleur'; import { FUNCTIONS_UPGRADE_DESCRIPTION, + NOTE_KEEP_STAGED, OPTION_HELP, - OPTION_KEEP_STAGED + OPTION_KEEP_STAGED, + OPTIONS_UPGRADE } from '../constants/help.constants'; import {helpOutput} from './common.help'; import {TITLE} from './help'; @@ -10,10 +12,18 @@ import {TITLE} from './help'; const usage = `Usage: ${green('juno')} ${cyan('functions')} ${magenta('upgrade')} ${yellow('[options]')} Options: - ${yellow('-n, --no-apply')} Submit the upgrade as a change but do not apply it yet. + ${yellow('-na, --no-apply')} Submit the upgrade as a change but do not apply it yet. ${OPTION_KEEP_STAGED} ${yellow('-i, --immediate')} Push the upgrade immediately (bypasses the change workflow). - ${OPTION_HELP}`; + ${OPTION_HELP} + +Upgrade options: + ${yellow('-s, --src')} An optional local gzipped WASM file for the upgrade. + ${OPTIONS_UPGRADE} + +Notes: + +- ${NOTE_KEEP_STAGED}`; const doc = `${FUNCTIONS_UPGRADE_DESCRIPTION} diff --git a/src/help/upgrade.help.ts b/src/help/upgrade.help.ts index 7dd49856..7754f8b1 100644 --- a/src/help/upgrade.help.ts +++ b/src/help/upgrade.help.ts @@ -1,5 +1,5 @@ import {cyan, green, yellow} from 'kleur'; -import {OPTION_HELP, UPGRADE_DESCRIPTION} from '../constants/help.constants'; +import {OPTION_HELP, OPTIONS_UPGRADE, UPGRADE_DESCRIPTION} from '../constants/help.constants'; import {helpMode, helpOutput} from './common.help'; import {TITLE} from './help'; import {TARGET_OPTION_NOTE, targetOption} from './target.help'; @@ -9,9 +9,7 @@ const usage = `Usage: ${green('juno')} ${cyan('upgrade')} ${yellow('[options]')} Options: ${targetOption('upgrade')} ${yellow('-s, --src')} An optional local gzipped WASM file for the upgrade. By default, the CDN will be used. - ${yellow('-r, --reset')} Reset to the initial state. - ${yellow('-cc, --clear-chunks')} Clear any previously uploaded WASM chunks (applies if the WASM size is greater than 2MB). - ${yellow('-ns, --no-snapshot')} Skip creating a snapshot before upgrading. + ${OPTIONS_UPGRADE} ${helpMode} ${OPTION_HELP} diff --git a/src/services/functions/build/build.rust.services.ts b/src/services/functions/build/build.rust.services.ts index 88037e59..d78c0fd0 100644 --- a/src/services/functions/build/build.rust.services.ts +++ b/src/services/functions/build/build.rust.services.ts @@ -15,6 +15,7 @@ import { DEVELOPER_PROJECT_SATELLITE_PATH, IC_WASM_MIN_VERSION, JUNO_PACKAGE_JSON_PATH, + SATELLITE_OUTPUT, TARGET_PATH } from '../../../constants/dev.constants'; import type {BuildArgs} from '../../../types/build'; @@ -29,7 +30,6 @@ import {readPackageJson} from '../../../utils/pkg.utils'; import {confirmAndExit} from '../../../utils/prompt.utils'; const CARGO_RELEASE_DIR = join(process.cwd(), 'target', 'wasm32-unknown-unknown', 'release'); -const SATELLITE_OUTPUT = join(DEPLOY_LOCAL_REPLICA_PATH, 'satellite.wasm'); const SATELLITE_PROJECT_NAME = 'satellite'; export const buildRust = async ({path}: Pick = {}) => { diff --git a/src/services/functions/upgrade.services.ts b/src/services/functions/upgrade.services.ts new file mode 100644 index 00000000..f9c3072c --- /dev/null +++ b/src/services/functions/upgrade.services.ts @@ -0,0 +1,26 @@ +import {hasArgs, nextArg} from '@junobuild/cli-tools'; +import {SATELLITE_OUTPUT} from '../../constants/dev.constants'; +import {assertConfigAndLoadSatelliteContext} from '../../utils/satellite.utils'; +import {upgradeSatelliteWithSrc} from '../modules/upgrade/upgrade.satellite.services'; + +export const upgradeFunctions = async (args?: string[]) => { + const immediate = hasArgs({args, options: ['-i', '--immediate']}); + const noCommit = hasArgs({args, options: ['-na', '--no-apply']}); + + if (immediate) { + await upgradeImmediate(args); + + } +}; + +const upgradeImmediate = async (args?: string[]) => { + const {satellite} = await assertConfigAndLoadSatelliteContext(args); + + const src = nextArg({args, option: '-s'}) ?? nextArg({args, option: '--src'}); + + await upgradeSatelliteWithSrc({ + satellite, + src: src ?? `${SATELLITE_OUTPUT}.gz`, + args + }); +}; diff --git a/src/services/modules/upgrade/upgrade.satellite.services.ts b/src/services/modules/upgrade/upgrade.satellite.services.ts index 738f606a..98251e45 100644 --- a/src/services/modules/upgrade/upgrade.satellite.services.ts +++ b/src/services/modules/upgrade/upgrade.satellite.services.ts @@ -56,11 +56,26 @@ const upgradeSatelliteCustom = async ({ }): Promise<{success: boolean; err?: unknown}> => { const src = nextArg({args, option: '-s'}) ?? nextArg({args, option: '--src'}); - if (src === undefined) { + if (isNullish(src)) { console.log(red('No source file provided.')); return {success: false}; } + return await upgradeSatelliteWithSrc({ + satellite, + src + }); +}; + +export const upgradeSatelliteWithSrc = async ({ + satellite, + src, + args +}: { + satellite: SatelliteParameters; + src: string; + args?: string[]; +}): Promise<{success: boolean; err?: unknown}> => { // TODO: option to be removed const currentVersion = await satelliteVersion({ satellite From 0c9e758e2388c429fc8a2ef8265033d0d089765c Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 2 Jun 2025 22:01:05 +0200 Subject: [PATCH 03/10] feat: propose and upgrade --- package-lock.json | 70 +++++----- package.json | 10 +- src/commands/functions.ts | 2 +- src/services/functions/upgrade.services.ts | 26 ---- .../functions/upgrade/upgrade.services.ts | 34 +++++ .../upgrade/upgrade.with-proposal.services.ts | 126 ++++++++++++++++++ src/types/functions.ts | 7 + src/utils/wasm.utils.ts | 36 +++++ 8 files changed, 244 insertions(+), 67 deletions(-) delete mode 100644 src/services/functions/upgrade.services.ts create mode 100644 src/services/functions/upgrade/upgrade.services.ts create mode 100644 src/services/functions/upgrade/upgrade.with-proposal.services.ts create mode 100644 src/types/functions.ts create mode 100644 src/utils/wasm.utils.ts diff --git a/package-lock.json b/package-lock.json index 30ad5e68..fdb8fdcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,14 +15,14 @@ "@dfinity/ic-management": "^6.1.1", "@dfinity/identity": "^2.3.0", "@dfinity/principal": "^2.3.0", - "@junobuild/admin": "^0.5.0-next-2025-06-01", - "@junobuild/cdn": "^0.1.0-next-2025-06-01", - "@junobuild/cli-tools": "^0.2.0-next-2025-06-01", + "@junobuild/admin": "^0.5.0-next-2025-06-02.1", + "@junobuild/cdn": "^0.1.0-next-2025-06-02.1", + "@junobuild/cli-tools": "^0.2.0-next-2025-06-02.1", "@junobuild/config": "^0.1.8", "@junobuild/config-loader": "^0.2.1", - "@junobuild/core": "^0.1.15-next-2025-06-01", + "@junobuild/core": "^0.1.15-next-2025-06-02.1", "@junobuild/did-tools": "^0.2.1", - "@junobuild/storage": "^0.2.0-next-2025-06-01", + "@junobuild/storage": "^0.2.0-next-2025-06-02.1", "@junobuild/utils": "^0.1.3", "chokidar": "^4.0.3", "conf": "^13.1.0", @@ -1464,9 +1464,9 @@ } }, "node_modules/@junobuild/admin": { - "version": "0.5.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.5.0-next-2025-06-01.tgz", - "integrity": "sha512-94Wu/9z6DB4E++oLClu8I6Je6hnszqwz3WNjqDpCBCrTXAd9K2o8LZ/DC5fruCbx24whE6Jisltwk2f/LtS82g==", + "version": "0.5.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.5.0-next-2025-06-02.1.tgz", + "integrity": "sha512-R0wqlMar44mDNwrIcpA1ODa2HHi9tDk9YnwQQIr6jO4adQUSKKubEmZS1HG2MYo9qoWptP6NvpibGrK+NkjzKw==", "license": "MIT", "peerDependencies": { "@dfinity/agent": "*", @@ -1481,9 +1481,9 @@ } }, "node_modules/@junobuild/cdn": { - "version": "0.1.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/cdn/-/cdn-0.1.0-next-2025-06-01.tgz", - "integrity": "sha512-QbXh9pR2RDmYB8D8z8tFcMulIax6w82k9Rf8eJ32QO9Rp1QL1+mqX7wrmSv+WftXXz0njgk9MrtRHeaJUHkV6A==", + "version": "0.1.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/cdn/-/cdn-0.1.0-next-2025-06-02.1.tgz", + "integrity": "sha512-oKOPJ8dAW0y0qCEMnLiAWQP5RVM34rJfCZrKM39o4oFlEiXc/k7oUT4pEO1DRCLjJNW46W+faU95OJwRbhL4lg==", "license": "MIT", "peerDependencies": { "@dfinity/agent": "*", @@ -1497,9 +1497,9 @@ } }, "node_modules/@junobuild/cli-tools": { - "version": "0.2.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.2.0-next-2025-06-01.tgz", - "integrity": "sha512-KUnrAr+6PKA5KxlcMebS8Pn8GcPl6QOR4mHgrPf5/ZRpB9N9K5yC/LetGu/Xgz30zJMPkVLreRH+1XSMc+zAbg==", + "version": "0.2.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.2.0-next-2025-06-02.1.tgz", + "integrity": "sha512-+T/qsdTtFOO+vd5CAguCgRSB+4biqZz54jUNYCPWKyZ7V9g2gjodrNPeb3zg5mouDOqhmjsA37VekPVfOIWIPw==", "license": "MIT", "dependencies": { "file-type": "^21.0.0", @@ -1536,9 +1536,9 @@ } }, "node_modules/@junobuild/core": { - "version": "0.1.15-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/core/-/core-0.1.15-next-2025-06-01.tgz", - "integrity": "sha512-4/UhZdrzNnCEDow0elZ5Tm0rpukaa7ACg2ZtwRgGiFB0mmgf/OyZIYwHLXF/w/r3BsEUv7MGoWy9CBo1dm1ckA==", + "version": "0.1.15-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/core/-/core-0.1.15-next-2025-06-02.1.tgz", + "integrity": "sha512-VZOf07PGOPlj/W1UXRj8wwUl1w5E/RMnAunwnpnAYbzjlNv3eih5rdP2JPqXO6SzDMUSVD46sp/y26sYGE7c5A==", "license": "MIT", "dependencies": { "@junobuild/errors": "*", @@ -1591,9 +1591,9 @@ } }, "node_modules/@junobuild/storage": { - "version": "0.2.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/storage/-/storage-0.2.0-next-2025-06-01.tgz", - "integrity": "sha512-wNuiAlk8xKJJMA/nw7JAk+Uc0ErK/Qdi2UP6LCLbrjyw1Umn7d/RCHAWekt9BA2H/G808Sp/rzdypLiDhHEnNg==", + "version": "0.2.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/storage/-/storage-0.2.0-next-2025-06-02.1.tgz", + "integrity": "sha512-de1BKkemggrlBa6y94h7zH63lmDYFG2xhJjBJA8imbl5yZD9oNXw9pc1CMVjm57PNzmd32CmudySJN9H732DZQ==", "license": "MIT", "peerDependencies": { "@dfinity/agent": "*", @@ -8011,21 +8011,21 @@ } }, "@junobuild/admin": { - "version": "0.5.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.5.0-next-2025-06-01.tgz", - "integrity": "sha512-94Wu/9z6DB4E++oLClu8I6Je6hnszqwz3WNjqDpCBCrTXAd9K2o8LZ/DC5fruCbx24whE6Jisltwk2f/LtS82g==", + "version": "0.5.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/admin/-/admin-0.5.0-next-2025-06-02.1.tgz", + "integrity": "sha512-R0wqlMar44mDNwrIcpA1ODa2HHi9tDk9YnwQQIr6jO4adQUSKKubEmZS1HG2MYo9qoWptP6NvpibGrK+NkjzKw==", "requires": {} }, "@junobuild/cdn": { - "version": "0.1.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/cdn/-/cdn-0.1.0-next-2025-06-01.tgz", - "integrity": "sha512-QbXh9pR2RDmYB8D8z8tFcMulIax6w82k9Rf8eJ32QO9Rp1QL1+mqX7wrmSv+WftXXz0njgk9MrtRHeaJUHkV6A==", + "version": "0.1.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/cdn/-/cdn-0.1.0-next-2025-06-02.1.tgz", + "integrity": "sha512-oKOPJ8dAW0y0qCEMnLiAWQP5RVM34rJfCZrKM39o4oFlEiXc/k7oUT4pEO1DRCLjJNW46W+faU95OJwRbhL4lg==", "requires": {} }, "@junobuild/cli-tools": { - "version": "0.2.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.2.0-next-2025-06-01.tgz", - "integrity": "sha512-KUnrAr+6PKA5KxlcMebS8Pn8GcPl6QOR4mHgrPf5/ZRpB9N9K5yC/LetGu/Xgz30zJMPkVLreRH+1XSMc+zAbg==", + "version": "0.2.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.2.0-next-2025-06-02.1.tgz", + "integrity": "sha512-+T/qsdTtFOO+vd5CAguCgRSB+4biqZz54jUNYCPWKyZ7V9g2gjodrNPeb3zg5mouDOqhmjsA37VekPVfOIWIPw==", "requires": { "file-type": "^21.0.0", "listr": "^0.14.3", @@ -8046,9 +8046,9 @@ "requires": {} }, "@junobuild/core": { - "version": "0.1.15-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/core/-/core-0.1.15-next-2025-06-01.tgz", - "integrity": "sha512-4/UhZdrzNnCEDow0elZ5Tm0rpukaa7ACg2ZtwRgGiFB0mmgf/OyZIYwHLXF/w/r3BsEUv7MGoWy9CBo1dm1ckA==", + "version": "0.1.15-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/core/-/core-0.1.15-next-2025-06-02.1.tgz", + "integrity": "sha512-VZOf07PGOPlj/W1UXRj8wwUl1w5E/RMnAunwnpnAYbzjlNv3eih5rdP2JPqXO6SzDMUSVD46sp/y26sYGE7c5A==", "requires": { "@junobuild/errors": "*", "@junobuild/storage": "*", @@ -8074,9 +8074,9 @@ "requires": {} }, "@junobuild/storage": { - "version": "0.2.0-next-2025-06-01", - "resolved": "https://registry.npmjs.org/@junobuild/storage/-/storage-0.2.0-next-2025-06-01.tgz", - "integrity": "sha512-wNuiAlk8xKJJMA/nw7JAk+Uc0ErK/Qdi2UP6LCLbrjyw1Umn7d/RCHAWekt9BA2H/G808Sp/rzdypLiDhHEnNg==", + "version": "0.2.0-next-2025-06-02.1", + "resolved": "https://registry.npmjs.org/@junobuild/storage/-/storage-0.2.0-next-2025-06-02.1.tgz", + "integrity": "sha512-de1BKkemggrlBa6y94h7zH63lmDYFG2xhJjBJA8imbl5yZD9oNXw9pc1CMVjm57PNzmd32CmudySJN9H732DZQ==", "requires": {} }, "@junobuild/utils": { diff --git a/package.json b/package.json index 2c32a0a3..acf7dd19 100644 --- a/package.json +++ b/package.json @@ -30,14 +30,14 @@ "@dfinity/ic-management": "^6.1.1", "@dfinity/identity": "^2.3.0", "@dfinity/principal": "^2.3.0", - "@junobuild/admin": "^0.5.0-next-2025-06-01", - "@junobuild/cdn": "^0.1.0-next-2025-06-01", - "@junobuild/cli-tools": "^0.2.0-next-2025-06-01", + "@junobuild/admin": "^0.5.0-next-2025-06-02.1", + "@junobuild/cdn": "^0.1.0-next-2025-06-02.1", + "@junobuild/cli-tools": "^0.2.0-next-2025-06-02.1", "@junobuild/config": "^0.1.8", "@junobuild/config-loader": "^0.2.1", - "@junobuild/core": "^0.1.15-next-2025-06-01", + "@junobuild/core": "^0.1.15-next-2025-06-02.1", "@junobuild/did-tools": "^0.2.1", - "@junobuild/storage": "^0.2.0-next-2025-06-01", + "@junobuild/storage": "^0.2.0-next-2025-06-02.1", "@junobuild/utils": "^0.1.3", "chokidar": "^4.0.3", "conf": "^13.1.0", diff --git a/src/commands/functions.ts b/src/commands/functions.ts index ad6efe11..ab9d2adc 100644 --- a/src/commands/functions.ts +++ b/src/commands/functions.ts @@ -5,7 +5,7 @@ import {logHelpFunctions} from '../help/functions.help'; import {logHelpFunctionsUpgrade} from '../help/functions.upgrade.help'; import {build} from '../services/functions/build/build.services'; import {eject} from '../services/functions/eject/eject.services'; -import {upgradeFunctions} from '../services/functions/upgrade.services'; +import {upgradeFunctions} from '../services/functions/upgrade/upgrade.services'; export const functions = async (args?: string[]) => { const [subCommand] = args ?? []; diff --git a/src/services/functions/upgrade.services.ts b/src/services/functions/upgrade.services.ts deleted file mode 100644 index f9c3072c..00000000 --- a/src/services/functions/upgrade.services.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {hasArgs, nextArg} from '@junobuild/cli-tools'; -import {SATELLITE_OUTPUT} from '../../constants/dev.constants'; -import {assertConfigAndLoadSatelliteContext} from '../../utils/satellite.utils'; -import {upgradeSatelliteWithSrc} from '../modules/upgrade/upgrade.satellite.services'; - -export const upgradeFunctions = async (args?: string[]) => { - const immediate = hasArgs({args, options: ['-i', '--immediate']}); - const noCommit = hasArgs({args, options: ['-na', '--no-apply']}); - - if (immediate) { - await upgradeImmediate(args); - - } -}; - -const upgradeImmediate = async (args?: string[]) => { - const {satellite} = await assertConfigAndLoadSatelliteContext(args); - - const src = nextArg({args, option: '-s'}) ?? nextArg({args, option: '--src'}); - - await upgradeSatelliteWithSrc({ - satellite, - src: src ?? `${SATELLITE_OUTPUT}.gz`, - args - }); -}; diff --git a/src/services/functions/upgrade/upgrade.services.ts b/src/services/functions/upgrade/upgrade.services.ts new file mode 100644 index 00000000..d9630a73 --- /dev/null +++ b/src/services/functions/upgrade/upgrade.services.ts @@ -0,0 +1,34 @@ +import {hasArgs, nextArg} from '@junobuild/cli-tools'; +import {SATELLITE_OUTPUT} from '../../../constants/dev.constants'; +import {type UpgradeFunctionsParams} from '../../../types/functions'; +import {assertConfigAndLoadSatelliteContext} from '../../../utils/satellite.utils'; +import {upgradeSatelliteWithSrc} from '../../modules/upgrade/upgrade.satellite.services'; +import {upgradeFunctionsWithProposal} from './upgrade.with-proposal.services'; + +export const upgradeFunctions = async (args?: string[]) => { + const {satellite} = await assertConfigAndLoadSatelliteContext(args); + + const srcArgs = nextArg({args, option: '-s'}) ?? nextArg({args, option: '--src'}); + const src = srcArgs ?? `${SATELLITE_OUTPUT}.gz`; + + const immediate = hasArgs({args, options: ['-i', '--immediate']}); + + if (immediate) { + await upgradeImmediate({ + args, + src, + satellite + }); + return; + } + + await upgradeFunctionsWithProposal({ + args, + src, + satellite + }); +}; + +const upgradeImmediate = async (params: UpgradeFunctionsParams) => { + await upgradeSatelliteWithSrc(params); +}; diff --git a/src/services/functions/upgrade/upgrade.with-proposal.services.ts b/src/services/functions/upgrade/upgrade.with-proposal.services.ts new file mode 100644 index 00000000..2b750cf0 --- /dev/null +++ b/src/services/functions/upgrade/upgrade.with-proposal.services.ts @@ -0,0 +1,126 @@ +import {uploadAssetWithProposal} from '@junobuild/cdn'; +import { + type DeployResultWithProposal, + deploySatelliteWasmWithProposal, + type FilePaths, + hasArgs, + type UploadFileStorageWithProposal +} from '@junobuild/cli-tools'; +import {type UpgradeFunctionsParams} from '../../../types/functions'; +import type {SatelliteParametersWithId} from '../../../types/satellite'; +import {readCustomSectionJunoPackage} from '../../../utils/wasm.utils'; +import {assertSatelliteMemorySize} from '../../assets/deploy/deploy.assert.services'; +import {type UploadFileFnParamsWithProposal} from '../../assets/deploy/deploy.execute.services'; +import {clearProposalStagedAssets} from '../../changes/changes.clear.services'; +import {upgradeSatelliteWithSrc} from '../../modules/upgrade/upgrade.satellite.services'; + +export const upgradeFunctionsWithProposal = async (params: UpgradeFunctionsParams) => { + const result = await deployWasmWithProposal(params); + + if (result.result !== 'deployed') { + return; + } + + await upgradeSatelliteWithSrc(params); +}; + +const deployWasmWithProposal = async ({ + args, + src, + satellite +}: UpgradeFunctionsParams): Promise => { + const {version} = await readCustomSectionJunoPackage({path: src}); + + // TODO: isGzip + + const fullPath = `/_juno/releases/${crypto.randomUUID()}/satellite-v${version}.wasm.gz`; + + const result = await uploadWasmWithProposal({ + satellite, + version, + args, + filePath: src, + fullPath + }); + + if (result.result !== 'deployed') { + return result; + } + + const {proposalId} = result; + + await clearProposalStagedAssets({ + args, + proposalId + }); + + return result; +}; + +const uploadWasmWithProposal = async ({ + args, + satellite, + fullPath, + filePath, + version +}: Omit & + FilePaths & {version: string}): Promise => { + const noCommit = hasArgs({args, options: ['-na', '--no-apply']}); + + const uploadFileFn = async ({ + filename, + fullPath: storagePath, + data, + collection, + headers = [], + encoding, + satellite, + proposalId + }: UploadFileFnParamsWithProposal) => { + await uploadAssetWithProposal({ + cdn: {satellite}, + proposalId, + asset: { + filename, + fullPath: storagePath ?? fullPath, + // @ts-expect-error type incompatibility NodeJS vs bundle + data, + collection, + headers, + encoding + } + }); + }; + + const uploadFile = async (params: UploadFileStorageWithProposal) => { + const paramsWithSatellite: UploadFileStorageWithProposal & { + satellite: SatelliteParametersWithId; + } = { + ...params, + satellite + }; + + await uploadFileFn(paramsWithSatellite); + }; + + const assertMemory = async () => { + await assertSatelliteMemorySize(args); + }; + + return await deploySatelliteWasmWithProposal({ + deploy: { + uploadFile, + fullPath, + filePath, + token: crypto.randomUUID(), + assertMemory + }, + proposal: { + autoCommit: !noCommit, + cdn: { + satellite + }, + version + } + }); +}; diff --git a/src/types/functions.ts b/src/types/functions.ts new file mode 100644 index 00000000..5b82e081 --- /dev/null +++ b/src/types/functions.ts @@ -0,0 +1,7 @@ +import {type SatelliteParametersWithId} from './satellite'; + +export interface UpgradeFunctionsParams { + src: string; + satellite: SatelliteParametersWithId; + args?: string[]; +} diff --git a/src/utils/wasm.utils.ts b/src/utils/wasm.utils.ts new file mode 100644 index 00000000..46a1d0ad --- /dev/null +++ b/src/utils/wasm.utils.ts @@ -0,0 +1,36 @@ +import {gunzipFile, isGzip} from '@junobuild/cli-tools'; +import type {JunoPackage} from '@junobuild/config'; +import {readFile} from 'node:fs/promises'; +import {uint8ArrayToString} from 'uint8array-extras'; + +export const readCustomSectionJunoPackage = async ({ + path +}: { + path: string; +}): Promise => { + const section = await customSection({path, sectionName: 'icp:public juno:package'}); + return JSON.parse(section); +}; + +const customSection = async ({ + path, + sectionName +}: { + path: string; + sectionName: string; +}): Promise => { + const buffer = await readFile(path); + + const wasm = isGzip(buffer) + ? await gunzipFile({ + source: buffer + }) + : buffer; + + const wasmModule = await WebAssembly.compile(wasm); + + const pkgSections = WebAssembly.Module.customSections(wasmModule, sectionName); + + const [pkgBuffer] = pkgSections; + return uint8ArrayToString(pkgBuffer); +}; From 398aed3bdc26ea6f222db6eaf6eb8aad49be8479 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 3 Jun 2025 06:38:49 +0200 Subject: [PATCH 04/10] feat: support legacy --- .../upgrade/upgrade.with-proposal.services.ts | 12 +++++++++++- src/utils/wasm.utils.ts | 13 ++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/services/functions/upgrade/upgrade.with-proposal.services.ts b/src/services/functions/upgrade/upgrade.with-proposal.services.ts index 2b750cf0..5da848d1 100644 --- a/src/services/functions/upgrade/upgrade.with-proposal.services.ts +++ b/src/services/functions/upgrade/upgrade.with-proposal.services.ts @@ -1,3 +1,4 @@ +import {isNullish} from '@dfinity/utils'; import {uploadAssetWithProposal} from '@junobuild/cdn'; import { type DeployResultWithProposal, @@ -6,6 +7,7 @@ import { hasArgs, type UploadFileStorageWithProposal } from '@junobuild/cli-tools'; +import {red} from 'kleur'; import {type UpgradeFunctionsParams} from '../../../types/functions'; import type {SatelliteParametersWithId} from '../../../types/satellite'; import {readCustomSectionJunoPackage} from '../../../utils/wasm.utils'; @@ -29,7 +31,15 @@ const deployWasmWithProposal = async ({ src, satellite }: UpgradeFunctionsParams): Promise => { - const {version} = await readCustomSectionJunoPackage({path: src}); + const junoPackage = await readCustomSectionJunoPackage({path: src}); + + if (isNullish(junoPackage)) { + console.log(red('No Juno Package metadata detected.')); + console.log('Are you using the latest libraries and tooling?'); + process.exit(1); + } + + const {version} = junoPackage; // TODO: isGzip diff --git a/src/utils/wasm.utils.ts b/src/utils/wasm.utils.ts index 46a1d0ad..3bc27e0a 100644 --- a/src/utils/wasm.utils.ts +++ b/src/utils/wasm.utils.ts @@ -1,3 +1,4 @@ +import {isNullish, nonNullish} from '@dfinity/utils'; import {gunzipFile, isGzip} from '@junobuild/cli-tools'; import type {JunoPackage} from '@junobuild/config'; import {readFile} from 'node:fs/promises'; @@ -7,8 +8,13 @@ export const readCustomSectionJunoPackage = async ({ path }: { path: string; -}): Promise => { +}): Promise => { const section = await customSection({path, sectionName: 'icp:public juno:package'}); + + if (isNullish(section)) { + return undefined; + } + return JSON.parse(section); }; @@ -18,7 +24,7 @@ const customSection = async ({ }: { path: string; sectionName: string; -}): Promise => { +}): Promise => { const buffer = await readFile(path); const wasm = isGzip(buffer) @@ -32,5 +38,6 @@ const customSection = async ({ const pkgSections = WebAssembly.Module.customSections(wasmModule, sectionName); const [pkgBuffer] = pkgSections; - return uint8ArrayToString(pkgBuffer); + + return nonNullish(pkgBuffer) ? uint8ArrayToString(pkgBuffer) : undefined; }; From 14e330515ce5d8520ae66541b66d06ec2b0fc2d7 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 3 Jun 2025 06:48:18 +0200 Subject: [PATCH 05/10] feat: ensure gzip --- .../upgrade/upgrade.with-proposal.services.ts | 13 +++--- src/utils/wasm.utils.ts | 44 +++++++++++++------ 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/services/functions/upgrade/upgrade.with-proposal.services.ts b/src/services/functions/upgrade/upgrade.with-proposal.services.ts index 5da848d1..3aec7116 100644 --- a/src/services/functions/upgrade/upgrade.with-proposal.services.ts +++ b/src/services/functions/upgrade/upgrade.with-proposal.services.ts @@ -10,7 +10,7 @@ import { import {red} from 'kleur'; import {type UpgradeFunctionsParams} from '../../../types/functions'; import type {SatelliteParametersWithId} from '../../../types/satellite'; -import {readCustomSectionJunoPackage} from '../../../utils/wasm.utils'; +import {readWasmMetadata} from '../../../utils/wasm.utils'; import {assertSatelliteMemorySize} from '../../assets/deploy/deploy.assert.services'; import {type UploadFileFnParamsWithProposal} from '../../assets/deploy/deploy.execute.services'; import {clearProposalStagedAssets} from '../../changes/changes.clear.services'; @@ -30,18 +30,21 @@ const deployWasmWithProposal = async ({ args, src, satellite -}: UpgradeFunctionsParams): Promise => { - const junoPackage = await readCustomSectionJunoPackage({path: src}); +}: UpgradeFunctionsParams): Promise => { + const {junoPackage, gzipped} = await readWasmMetadata({path: src}); if (isNullish(junoPackage)) { console.log(red('No Juno Package metadata detected.')); console.log('Are you using the latest libraries and tooling?'); - process.exit(1); + return {result: 'error'}; } const {version} = junoPackage; - // TODO: isGzip + if (!gzipped) { + console.log(red('The submitted WASM file must be gzipped.')); + return {result: 'error'}; + } const fullPath = `/_juno/releases/${crypto.randomUUID()}/satellite-v${version}.wasm.gz`; diff --git a/src/utils/wasm.utils.ts b/src/utils/wasm.utils.ts index 3bc27e0a..debff6c9 100644 --- a/src/utils/wasm.utils.ts +++ b/src/utils/wasm.utils.ts @@ -4,12 +4,38 @@ import type {JunoPackage} from '@junobuild/config'; import {readFile} from 'node:fs/promises'; import {uint8ArrayToString} from 'uint8array-extras'; -export const readCustomSectionJunoPackage = async ({ +export const readWasmMetadata = async ({ path }: { path: string; +}): Promise<{ + gzipped: boolean; + junoPackage: JunoPackage | undefined; +}> => { + const buffer = await readFile(path); + + const gzipped = isGzip(buffer); + + const wasm = gzipped + ? await gunzipFile({ + source: buffer + }) + : buffer; + + const junoPackage = await readCustomSectionJunoPackage({wasm}); + + return { + gzipped, + junoPackage + }; +}; + +const readCustomSectionJunoPackage = async ({ + wasm +}: { + wasm: Buffer; }): Promise => { - const section = await customSection({path, sectionName: 'icp:public juno:package'}); + const section = await customSection({wasm, sectionName: 'icp:public juno:package'}); if (isNullish(section)) { return undefined; @@ -19,20 +45,12 @@ export const readCustomSectionJunoPackage = async ({ }; const customSection = async ({ - path, - sectionName + sectionName, + wasm }: { - path: string; sectionName: string; + wasm: Buffer; }): Promise => { - const buffer = await readFile(path); - - const wasm = isGzip(buffer) - ? await gunzipFile({ - source: buffer - }) - : buffer; - const wasmModule = await WebAssembly.compile(wasm); const pkgSections = WebAssembly.Module.customSections(wasmModule, sectionName); From 2573c4b721075f314fd637609227c91cc8a4947a Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 3 Jun 2025 07:09:26 +0200 Subject: [PATCH 06/10] fix: filename --- src/constants/functions.constants.ts | 1 + .../functions/upgrade/upgrade.with-proposal.services.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 src/constants/functions.constants.ts diff --git a/src/constants/functions.constants.ts b/src/constants/functions.constants.ts new file mode 100644 index 00000000..6f9b9cf9 --- /dev/null +++ b/src/constants/functions.constants.ts @@ -0,0 +1 @@ +export const CDN_RELEASES_FULL_PATH = '/_juno/releases'; \ No newline at end of file diff --git a/src/services/functions/upgrade/upgrade.with-proposal.services.ts b/src/services/functions/upgrade/upgrade.with-proposal.services.ts index 3aec7116..3120b102 100644 --- a/src/services/functions/upgrade/upgrade.with-proposal.services.ts +++ b/src/services/functions/upgrade/upgrade.with-proposal.services.ts @@ -8,6 +8,7 @@ import { type UploadFileStorageWithProposal } from '@junobuild/cli-tools'; import {red} from 'kleur'; +import {CDN_RELEASES_FULL_PATH} from '../../../constants/functions.constants'; import {type UpgradeFunctionsParams} from '../../../types/functions'; import type {SatelliteParametersWithId} from '../../../types/satellite'; import {readWasmMetadata} from '../../../utils/wasm.utils'; @@ -46,7 +47,7 @@ const deployWasmWithProposal = async ({ return {result: 'error'}; } - const fullPath = `/_juno/releases/${crypto.randomUUID()}/satellite-v${version}.wasm.gz`; + const fullPath = `${CDN_RELEASES_FULL_PATH}/satellite-v${version}-${crypto.randomUUID()}.wasm.gz`; const result = await uploadWasmWithProposal({ satellite, From f0e890e47271372821b111d904960d0f0d68f33e Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 3 Jun 2025 07:46:18 +0200 Subject: [PATCH 07/10] feat: spacing --- .../functions/upgrade/upgrade.with-proposal.services.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/functions/upgrade/upgrade.with-proposal.services.ts b/src/services/functions/upgrade/upgrade.with-proposal.services.ts index 3120b102..9d2a422c 100644 --- a/src/services/functions/upgrade/upgrade.with-proposal.services.ts +++ b/src/services/functions/upgrade/upgrade.with-proposal.services.ts @@ -24,6 +24,8 @@ export const upgradeFunctionsWithProposal = async (params: UpgradeFunctionsParam return; } + console.log(''); + await upgradeSatelliteWithSrc(params); }; From 45333a859e09d8053ddded29246621018daa57f5 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 3 Jun 2025 08:43:57 +0200 Subject: [PATCH 08/10] chore: fmt --- src/constants/functions.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/functions.constants.ts b/src/constants/functions.constants.ts index 6f9b9cf9..64ad0ec6 100644 --- a/src/constants/functions.constants.ts +++ b/src/constants/functions.constants.ts @@ -1 +1 @@ -export const CDN_RELEASES_FULL_PATH = '/_juno/releases'; \ No newline at end of file +export const CDN_RELEASES_FULL_PATH = '/_juno/releases'; From 9e7c1c550838d596e8c5975e97252574ef6cee01 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 3 Jun 2025 09:15:34 +0200 Subject: [PATCH 09/10] feat: simplier --- src/services/modules/upgrade/upgrade-assert.services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/modules/upgrade/upgrade-assert.services.ts b/src/services/modules/upgrade/upgrade-assert.services.ts index e03c966c..10b56a6d 100644 --- a/src/services/modules/upgrade/upgrade-assert.services.ts +++ b/src/services/modules/upgrade/upgrade-assert.services.ts @@ -54,6 +54,6 @@ export const assertUpgradeHash = async ({ reset }: Required>) => { await confirmAndExit( - `The Wasm hash to be applied for the upgrade is ${cyan(hash)}.${NEW_CMD_LINE}Start upgrade${reset ? ' and reset' : ''} now?` + `The Wasm hash for the upgrade is ${cyan(hash)}.${NEW_CMD_LINE}Start upgrade${reset ? ' and reset' : ''} now?` ); }; From 9096a20349b9669e90d0cf12f1c8a9e8218a8631 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Tue, 3 Jun 2025 09:27:50 +0200 Subject: [PATCH 10/10] chore: lint --- src/services/modules/upgrade/upgrade.services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/modules/upgrade/upgrade.services.ts b/src/services/modules/upgrade/upgrade.services.ts index cf13be30..3bad0602 100644 --- a/src/services/modules/upgrade/upgrade.services.ts +++ b/src/services/modules/upgrade/upgrade.services.ts @@ -73,7 +73,7 @@ const executeUpgradeWasm = async ({ spinner.stop(); if (err instanceof UpgradeCodeUnchangedError) { - console.log(`${yellow(err.message)}`); + console.log(yellow(err.message)); return; }