From a089f66cb1b65c099b2a0dcd4f95a46eb31f6ac9 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 16 Mar 2026 21:11:56 +0100 Subject: [PATCH 1/6] feat: enable or disable weekly version check Signed-off-by: David Dal Busco --- src/configs/cli.versions.config.ts | 9 ++++++++- src/constants/help.constants.ts | 2 +- src/help/version.help.ts | 7 +++++-- src/types/cli/cli.versions.ts | 1 + src/version.ts | 5 +++++ 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/configs/cli.versions.config.ts b/src/configs/cli.versions.config.ts index 655050a4..23a30b26 100644 --- a/src/configs/cli.versions.config.ts +++ b/src/configs/cli.versions.config.ts @@ -6,7 +6,14 @@ const getVersionConfig = (): Conf => export const getCachedVersions = (): Conf => getVersionConfig(); -export const updateLastCheckToNow = ({key}: {key: keyof CachedVersions}) => { +export const isWeeklyCheckDisabled = (): boolean => + getCachedVersions().get('weeklyCheckEnabled') === false; + +export const updateLastCheckToNow = ({ + key +}: { + key: keyof Omit; +}) => { const config = getVersionConfig(); const currentVersions = config.get(key); diff --git a/src/constants/help.constants.ts b/src/constants/help.constants.ts index 3fafaccb..3d1da389 100644 --- a/src/constants/help.constants.ts +++ b/src/constants/help.constants.ts @@ -16,7 +16,7 @@ export const SNAPSHOT_DESCRIPTION = 'Handle snapshot-related tasks.'; export const START_DESCRIPTION = 'Start a module.'; export const STOP_DESCRIPTION = 'Stop a module.'; export const UPGRADE_DESCRIPTION = 'Upgrade a module to a new version.'; -export const VERSION_DESCRIPTION = 'Check the version of the CLI.'; +export const VERSION_DESCRIPTION = 'Manage version related tasks.'; export const STATUS_DESCRIPTION = 'Check the status of the modules.'; export const WHOAMI_DESCRIPTION = 'Display your current profile, access key, and links to your satellite.'; diff --git a/src/help/version.help.ts b/src/help/version.help.ts index bd7a9f09..1f366ffd 100644 --- a/src/help/version.help.ts +++ b/src/help/version.help.ts @@ -1,9 +1,12 @@ -import {cyan, green, yellow} from 'kleur'; +import {cyan, green, magenta, yellow} from 'kleur'; import {OPTION_HELP, VERSION_DESCRIPTION} from '../constants/help.constants'; import {helpOutput} from './common.help'; import {TITLE} from './help'; -const usage = `Usage: ${green('juno')} ${cyan('version')} ${yellow('[options]')} +const usage = `Usage: ${green('juno')} ${cyan('version')} ${magenta('')} + +Subcommands: + ${magenta('check')} Configure the weekly version check. Options: ${OPTION_HELP}`; diff --git a/src/types/cli/cli.versions.ts b/src/types/cli/cli.versions.ts index 365665a4..a7e7bb52 100644 --- a/src/types/cli/cli.versions.ts +++ b/src/types/cli/cli.versions.ts @@ -7,6 +7,7 @@ export const CachedVersionSchema = j.strictObject({ }); export const CachedVersionsSchema = j.strictObject({ + weeklyCheckEnabled: j.boolean().optional(), cli: CachedVersionSchema.optional(), emulator: CachedVersionSchema.optional() }); diff --git a/src/version.ts b/src/version.ts index 1f5924d4..fe86aab6 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,3 +1,4 @@ +import {isWeeklyCheckDisabled} from './configs/cli.versions.config'; import {checkCliVersion, checkEmulatorVersion} from './services/version/version.check.services'; import {isHeadless} from './utils/process.utils'; @@ -7,6 +8,10 @@ export const checkWeeklyVersions = async ({cmd, args}: {cmd: string; args?: stri return; } + if (isWeeklyCheckDisabled()) { + return; + } + const [subCommand] = args ?? []; if (cmd === 'emulator' && ['start', 'wait'].includes(subCommand)) { From 1add1ec78d4031dba73df495b15b4733d9c6bd6d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 16 Mar 2026 21:47:26 +0100 Subject: [PATCH 2/6] feat: enable disable Signed-off-by: David Dal Busco --- src/commands/version.ts | 16 ++ src/configs/cli.versions.config.ts | 11 +- src/help/version.help.ts | 2 +- src/index.ts | 4 +- .../version/version.check.services.ts | 179 ++---------------- .../version/version.weekly.check.services.ts | 167 ++++++++++++++++ src/version.ts | 2 +- 7 files changed, 214 insertions(+), 167 deletions(-) create mode 100644 src/services/version/version.weekly.check.services.ts diff --git a/src/commands/version.ts b/src/commands/version.ts index 19ff2853..85208e78 100644 --- a/src/commands/version.ts +++ b/src/commands/version.ts @@ -1,5 +1,21 @@ import {printVersion} from '../services/version/version.print.services'; +import {red} from 'kleur'; +import {logHelpVersion} from '../help/version.help'; +import {enableDisableVersionCheck} from '../services/version/version.check.services'; export const logVersion = async () => { await printVersion(); }; + +export const version = async (args?: string[]) => { + const [subCommand] = args ?? []; + + switch (subCommand) { + case 'check': + await enableDisableVersionCheck(); + break + default: + console.log(red('Unknown subcommand.')); + logHelpVersion(args); + } +} \ No newline at end of file diff --git a/src/configs/cli.versions.config.ts b/src/configs/cli.versions.config.ts index 23a30b26..39a44088 100644 --- a/src/configs/cli.versions.config.ts +++ b/src/configs/cli.versions.config.ts @@ -6,8 +6,15 @@ const getVersionConfig = (): Conf => export const getCachedVersions = (): Conf => getVersionConfig(); -export const isWeeklyCheckDisabled = (): boolean => - getCachedVersions().get('weeklyCheckEnabled') === false; +export const isWeeklyCheckEnabled = (): boolean => + getCachedVersions().get('weeklyCheckEnabled') !== false; + +export const isWeeklyCheckDisabled = (): boolean => !isWeeklyCheckEnabled(); + +export const toggleWeeklyCheck = (enabled: boolean) => { + const config = getVersionConfig(); + config.set('weeklyCheckEnabled', enabled); +} export const updateLastCheckToNow = ({ key diff --git a/src/help/version.help.ts b/src/help/version.help.ts index 1f366ffd..0ff445c1 100644 --- a/src/help/version.help.ts +++ b/src/help/version.help.ts @@ -6,7 +6,7 @@ import {TITLE} from './help'; const usage = `Usage: ${green('juno')} ${cyan('version')} ${magenta('')} Subcommands: - ${magenta('check')} Configure the weekly version check. + ${magenta('check')} Configure the weekly version check. Options: ${OPTION_HELP}`; diff --git a/src/index.ts b/src/index.ts index 98f847ad..94a8dd49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,7 @@ import {helpSnapshot, snapshot} from './commands/snapshot'; import {startStop} from './commands/start-stop'; import {status} from './commands/status'; import {upgrade} from './commands/upgrade'; -import {logVersion} from './commands/version'; +import {logVersion, version as versionCommand} from './commands/version'; import {whoami} from './commands/whoami'; import {help} from './help/help'; import {logHelpLogin} from './help/login.help'; @@ -149,7 +149,7 @@ export const run = async () => { await clear(); break; case 'version': - await logVersion(); + await versionCommand(args); break; case 'status': await status(); diff --git a/src/services/version/version.check.services.ts b/src/services/version/version.check.services.ts index 35830f55..61c1e9c7 100644 --- a/src/services/version/version.check.services.ts +++ b/src/services/version/version.check.services.ts @@ -1,167 +1,24 @@ -import {isNullish, nonNullish} from '@dfinity/utils'; -import ora from 'ora'; -import {compare} from 'semver'; -import {version as cliCurrentVersion} from '../../../package.json'; -import { - getCachedVersions, - saveCachedVersions, - updateLastCheckToNow -} from '../../configs/cli.versions.config'; -import { - githubCliLastRelease, - githubJunoDockerLastRelease, - type GithubLastReleaseResult -} from '../../rest/github.rest'; -import {type CachedVersions} from '../../types/cli/cli.versions'; -import {pmInstallHint} from '../../utils/pm.utils'; -import {findEmulatorVersion} from '../emulator/version.services'; -import { - buildVersionFromGitHub, - type BuildVersionFromGitHubResult, - checkVersion -} from './version.services'; - -const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000; - -export const checkCliVersion = async () => { - const checkVersionFn = ({latestVersion}: {latestVersion: string}) => { - checkVersion({ - currentVersion: cliCurrentVersion, - latestVersion, - displayHint: 'CLI', - commandLineHint: pmInstallHint(), - logUpToDate: false, - logSpacer: true - }); - }; - - await check({ - key: 'cli', - currentVersion: cliCurrentVersion, - releaseFn: githubCliLastRelease, - checkVersionFn - }); -}; - -export const checkEmulatorVersion = async () => { - const emulatorResult = await findEmulatorVersion(); - - if (emulatorResult.status !== 'success') { - return; - } - - const {version: emulatorCurrentVersion} = emulatorResult; - - // We fetched the emulator but the version is null which could happen has providing the metadata - // was patched in Juno Docker v0.6.3 - if (isNullish(emulatorCurrentVersion)) { - return; - } - - const checkVersionFn = ({latestVersion}: {latestVersion: string}) => { - checkVersion({ - currentVersion: emulatorCurrentVersion, - latestVersion, - displayHint: 'Emulator', - logUpToDate: false, - logSpacer: true - }); - }; - - await check({ - key: 'emulator', - currentVersion: emulatorCurrentVersion, - releaseFn: githubJunoDockerLastRelease, - checkVersionFn - }); -}; - -const check = async ({ - key, - currentVersion, - releaseFn, - checkVersionFn -}: { - key: keyof CachedVersions; - currentVersion: string; - releaseFn: () => Promise; - checkVersionFn: (params: {latestVersion: string}) => void; -}) => { - const cachedVersions = getCachedVersions(); - - const cachedInfo = cachedVersions.get(key); - - const lastCheck = cachedInfo?.lastCheck; - - if (isNullish(lastCheck)) { - saveCachedVersions({ - key, - versions: { - local: currentVersion - } - }); - return; - } - - const cachedLocalVersion = cachedInfo?.local; - - // The version was never cached or the developer upgraded since the last check. - // We assume they are on the latest version. If not, the next weekly check will catch it. - if (isNullish(cachedLocalVersion) || compare(currentVersion, cachedLocalVersion) > 0) { - saveCachedVersions({ - key, - versions: { - local: currentVersion - } - }); - return; - } - - const checkIsDue = - new Date(new Date(lastCheck).getTime() + ONE_WEEK_MS).getTime() <= new Date().getTime(); - - const cachedRemoteVersion = cachedInfo?.remote; - - // The weekly check is not due and we got a remote version in cache - if (!checkIsDue && nonNullish(cachedRemoteVersion)) { - // The current version is newer or equals, we assume the dev use the latest - if (compare(currentVersion, cachedRemoteVersion) >= 0) { - return; +import {isNullish} from '@dfinity/utils'; +import prompts from 'prompts'; +import {isWeeklyCheckEnabled, toggleWeeklyCheck} from '../../configs/cli.versions.config'; + +export const enableDisableVersionCheck = async () => { + const current = isWeeklyCheckEnabled(); + + const {enabled}: {enabled: boolean | undefined} = await prompts([ + { + type: 'toggle', + name: 'enabled', + message: 'Enable weekly version check?', + initial: current, + active: 'yes', + inactive: 'no' } + ]); - // Behind but check not due, compare cached remote version - checkVersionFn({latestVersion: cachedRemoteVersion}); + if (isNullish(enabled)) { return; } - const loadVersionWithGitHub = async (): Promise => { - const spinner = ora(`Two secs, fetching ${key} latest version...`).start(); - - try { - return await buildVersionFromGitHub({ - releaseFn - }); - } finally { - spinner.stop(); - } - }; - - const result = await loadVersionWithGitHub(); - - if (result.result === 'error') { - updateLastCheckToNow({key}); - return; - } - - const {latestVersion} = result; - - saveCachedVersions({ - key, - versions: { - local: currentVersion, - remote: latestVersion - } - }); - - checkVersionFn({latestVersion}); + toggleWeeklyCheck(enabled); }; diff --git a/src/services/version/version.weekly.check.services.ts b/src/services/version/version.weekly.check.services.ts new file mode 100644 index 00000000..331c796d --- /dev/null +++ b/src/services/version/version.weekly.check.services.ts @@ -0,0 +1,167 @@ +import {isNullish, nonNullish} from '@dfinity/utils'; +import ora from 'ora'; +import {compare} from 'semver'; +import {version as cliCurrentVersion} from '../../../package.json'; +import { + getCachedVersions, + saveCachedVersions, + updateLastCheckToNow +} from '../../configs/cli.versions.config'; +import { + githubCliLastRelease, + githubJunoDockerLastRelease, + type GithubLastReleaseResult +} from '../../rest/github.rest'; +import {type CachedVersions} from '../../types/cli/cli.versions'; +import {pmInstallHint} from '../../utils/pm.utils'; +import {findEmulatorVersion} from '../emulator/version.services'; +import { + buildVersionFromGitHub, + type BuildVersionFromGitHubResult, + checkVersion +} from './version.services'; + +const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000; + +export const checkCliVersion = async () => { + const checkVersionFn = ({latestVersion}: {latestVersion: string}) => { + checkVersion({ + currentVersion: cliCurrentVersion, + latestVersion, + displayHint: 'CLI', + commandLineHint: pmInstallHint(), + logUpToDate: false, + logSpacer: true + }); + }; + + await check({ + key: 'cli', + currentVersion: cliCurrentVersion, + releaseFn: githubCliLastRelease, + checkVersionFn + }); +}; + +export const checkEmulatorVersion = async () => { + const emulatorResult = await findEmulatorVersion(); + + if (emulatorResult.status !== 'success') { + return; + } + + const {version: emulatorCurrentVersion} = emulatorResult; + + // We fetched the emulator but the version is null which could happen has providing the metadata + // was patched in Juno Docker v0.6.3 + if (isNullish(emulatorCurrentVersion)) { + return; + } + + const checkVersionFn = ({latestVersion}: {latestVersion: string}) => { + checkVersion({ + currentVersion: emulatorCurrentVersion, + latestVersion, + displayHint: 'Emulator', + logUpToDate: false, + logSpacer: true + }); + }; + + await check({ + key: 'emulator', + currentVersion: emulatorCurrentVersion, + releaseFn: githubJunoDockerLastRelease, + checkVersionFn + }); +}; + +const check = async ({ + key, + currentVersion, + releaseFn, + checkVersionFn +}: { + key: keyof Omit; + currentVersion: string; + releaseFn: () => Promise; + checkVersionFn: (params: {latestVersion: string}) => void; +}) => { + const cachedVersions = getCachedVersions(); + + const cachedInfo = cachedVersions.get(key); + + const lastCheck = cachedInfo?.lastCheck; + + if (isNullish(lastCheck)) { + saveCachedVersions({ + key, + versions: { + local: currentVersion + } + }); + return; + } + + const cachedLocalVersion = cachedInfo?.local; + + // The version was never cached or the developer upgraded since the last check. + // We assume they are on the latest version. If not, the next weekly check will catch it. + if (isNullish(cachedLocalVersion) || compare(currentVersion, cachedLocalVersion) > 0) { + saveCachedVersions({ + key, + versions: { + local: currentVersion + } + }); + return; + } + + const checkIsDue = + new Date(new Date(lastCheck).getTime() + ONE_WEEK_MS).getTime() <= new Date().getTime(); + + const cachedRemoteVersion = cachedInfo?.remote; + + // The weekly check is not due and we got a remote version in cache + if (!checkIsDue && nonNullish(cachedRemoteVersion)) { + // The current version is newer or equals, we assume the dev use the latest + if (compare(currentVersion, cachedRemoteVersion) >= 0) { + return; + } + + // Behind but check not due, compare cached remote version + checkVersionFn({latestVersion: cachedRemoteVersion}); + return; + } + + const loadVersionWithGitHub = async (): Promise => { + const spinner = ora(`Two secs, fetching ${key} latest version...`).start(); + + try { + return await buildVersionFromGitHub({ + releaseFn + }); + } finally { + spinner.stop(); + } + }; + + const result = await loadVersionWithGitHub(); + + if (result.result === 'error') { + updateLastCheckToNow({key}); + return; + } + + const {latestVersion} = result; + + saveCachedVersions({ + key, + versions: { + local: currentVersion, + remote: latestVersion + } + }); + + checkVersionFn({latestVersion}); +}; diff --git a/src/version.ts b/src/version.ts index fe86aab6..5e25ef0f 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,5 +1,5 @@ import {isWeeklyCheckDisabled} from './configs/cli.versions.config'; -import {checkCliVersion, checkEmulatorVersion} from './services/version/version.check.services'; +import {checkCliVersion, checkEmulatorVersion} from './services/version/version.weekly.check.services'; import {isHeadless} from './utils/process.utils'; export const checkWeeklyVersions = async ({cmd, args}: {cmd: string; args?: string[]}) => { From 1422ab2cfd1915be4a10cf2ea6a85fbde5849a1d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 16 Mar 2026 21:50:01 +0100 Subject: [PATCH 3/6] feat: no weekly if version Signed-off-by: David Dal Busco --- src/version.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/version.ts b/src/version.ts index 5e25ef0f..f7f12db2 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,5 +1,8 @@ import {isWeeklyCheckDisabled} from './configs/cli.versions.config'; -import {checkCliVersion, checkEmulatorVersion} from './services/version/version.weekly.check.services'; +import { + checkCliVersion, + checkEmulatorVersion +} from './services/version/version.weekly.check.services'; import {isHeadless} from './utils/process.utils'; export const checkWeeklyVersions = async ({cmd, args}: {cmd: string; args?: string[]}) => { @@ -12,6 +15,10 @@ export const checkWeeklyVersions = async ({cmd, args}: {cmd: string; args?: stri return; } + if (cmd === 'version') { + return; + } + const [subCommand] = args ?? []; if (cmd === 'emulator' && ['start', 'wait'].includes(subCommand)) { From 7df42e39726b52326f573da92dc35e3839c21548 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 16 Mar 2026 21:51:32 +0100 Subject: [PATCH 4/6] chore: fmt Signed-off-by: David Dal Busco --- src/commands/version.ts | 6 +++--- src/configs/cli.versions.config.ts | 2 +- src/help/version.help.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/commands/version.ts b/src/commands/version.ts index 85208e78..d9e0ef20 100644 --- a/src/commands/version.ts +++ b/src/commands/version.ts @@ -1,7 +1,7 @@ -import {printVersion} from '../services/version/version.print.services'; import {red} from 'kleur'; import {logHelpVersion} from '../help/version.help'; import {enableDisableVersionCheck} from '../services/version/version.check.services'; +import {printVersion} from '../services/version/version.print.services'; export const logVersion = async () => { await printVersion(); @@ -13,9 +13,9 @@ export const version = async (args?: string[]) => { switch (subCommand) { case 'check': await enableDisableVersionCheck(); - break + break; default: console.log(red('Unknown subcommand.')); logHelpVersion(args); } -} \ No newline at end of file +}; diff --git a/src/configs/cli.versions.config.ts b/src/configs/cli.versions.config.ts index 39a44088..c675aa1d 100644 --- a/src/configs/cli.versions.config.ts +++ b/src/configs/cli.versions.config.ts @@ -14,7 +14,7 @@ export const isWeeklyCheckDisabled = (): boolean => !isWeeklyCheckEnabled(); export const toggleWeeklyCheck = (enabled: boolean) => { const config = getVersionConfig(); config.set('weeklyCheckEnabled', enabled); -} +}; export const updateLastCheckToNow = ({ key diff --git a/src/help/version.help.ts b/src/help/version.help.ts index 0ff445c1..7c23f91a 100644 --- a/src/help/version.help.ts +++ b/src/help/version.help.ts @@ -1,4 +1,4 @@ -import {cyan, green, magenta, yellow} from 'kleur'; +import {cyan, green, magenta} from 'kleur'; import {OPTION_HELP, VERSION_DESCRIPTION} from '../constants/help.constants'; import {helpOutput} from './common.help'; import {TITLE} from './help'; From 6853d39c422aa0a96ecab2e1815a8b348969d5a8 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 16 Mar 2026 21:52:29 +0100 Subject: [PATCH 5/6] chore. rename Signed-off-by: David Dal Busco --- ...eekly.check.services.ts => version.check.weekly.services.ts} | 0 src/version.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/services/version/{version.weekly.check.services.ts => version.check.weekly.services.ts} (100%) diff --git a/src/services/version/version.weekly.check.services.ts b/src/services/version/version.check.weekly.services.ts similarity index 100% rename from src/services/version/version.weekly.check.services.ts rename to src/services/version/version.check.weekly.services.ts diff --git a/src/version.ts b/src/version.ts index f7f12db2..3b48aea3 100644 --- a/src/version.ts +++ b/src/version.ts @@ -2,7 +2,7 @@ import {isWeeklyCheckDisabled} from './configs/cli.versions.config'; import { checkCliVersion, checkEmulatorVersion -} from './services/version/version.weekly.check.services'; +} from './services/version/version.check.weekly.services'; import {isHeadless} from './utils/process.utils'; export const checkWeeklyVersions = async ({cmd, args}: {cmd: string; args?: string[]}) => { From 80a841bb2d12b8ed7769ebbc11a9d4ee4ea335b8 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 16 Mar 2026 21:53:55 +0100 Subject: [PATCH 6/6] chore: merge main Signed-off-by: David Dal Busco --- src/services/version/version.check.services.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/version/version.check.services.ts b/src/services/version/version.check.services.ts index c7af556e..61c1e9c7 100644 --- a/src/services/version/version.check.services.ts +++ b/src/services/version/version.check.services.ts @@ -20,6 +20,5 @@ export const enableDisableVersionCheck = async () => { return; } - toggleWeeklyCheck(enabled); };