Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/commands/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
ORBITER_WASM_NAME,
SATELLITE_WASM_NAME
} from '../constants/constants';
import {checkVersion, getSatelliteVersion} from '../services/version.services';
import {checkVersion, getSatelliteVersion} from '../services/version/version.services';
import type {AssetKey} from '../types/asset-key';
import {toAssetKeys} from '../utils/asset-key.utils';
import {orbiterKey, satelliteKey} from '../utils/cli.config.utils';
Expand Down
66 changes: 12 additions & 54 deletions src/commands/version.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import {isEmptyString, isNullish} from '@dfinity/utils';
import {green, red} from 'kleur';
import {clean} from 'semver';
import {isEmptyString} from '@dfinity/utils';
import {green} from 'kleur';
import {version as cliCurrentVersion} from '../../package.json';
import {
githubCliLastRelease,
githubJunoDockerLastRelease,
type GithubLastReleaseResult
} from '../rest/github.rest';
import {githubCliLastRelease, githubJunoDockerLastRelease} from '../rest/github.rest';
import {findEmulatorVersion} from '../services/emulator/version.services';
import {checkVersion, type CheckVersionResult} from '../services/version.services';
import {detectPackageManager} from '../utils/pm.utils';
import {
buildVersionFromGitHub,
checkVersion,
type CheckVersionResult
} from '../services/version/version.services';
import {pmInstallHint} from '../utils/pm.utils';

export const version = async () => {
const check = await cliVersion();
Expand All @@ -23,7 +22,7 @@ export const version = async () => {

const cliVersion = async (): Promise<CheckVersionResult> => {
const result = await buildVersionFromGitHub({
release: 'CLI',
logReleaseOnError: () => 'CLI',
releaseFn: githubCliLastRelease
});

Expand All @@ -37,23 +36,10 @@ const cliVersion = async (): Promise<CheckVersionResult> => {
currentVersion: cliCurrentVersion,
latestVersion,
displayHint: 'CLI',
commandLineHint: installHint()
commandLineHint: pmInstallHint()
});
};

const installHint = (): string => {
const pm = detectPackageManager();

switch (pm) {
case 'yarn':
return 'yarn global add @junobuild/cli';
case 'pnpm':
return 'pnpm add -g @junobuild/cli';
default:
return 'npm i -g @junobuild/cli';
}
};

const emulatorVersion = async () => {
const emulatorResult = await findEmulatorVersion();

Expand All @@ -64,7 +50,7 @@ const emulatorVersion = async () => {
const {version: emulatorCurrentVersion} = emulatorResult;

const result = await buildVersionFromGitHub({
release: 'Juno Docker',
logReleaseOnError: () => 'Juno Docker',
releaseFn: githubJunoDockerLastRelease
});

Expand All @@ -88,31 +74,3 @@ const emulatorVersion = async () => {
displayHint: 'Emulator'
});
};

const buildVersionFromGitHub = async ({
releaseFn,
release
}: {
releaseFn: () => Promise<GithubLastReleaseResult>;
release: 'CLI' | 'Juno Docker';
}): Promise<{result: 'success'; latestVersion: string} | {result: 'error'}> => {
const githubRelease = await releaseFn();

if (githubRelease.status === 'error') {
console.log(red(`Cannot fetch the last version of ${release} on GitHub 😢.`));
return {result: 'error'};
}

const {
release: {tag_name}
} = githubRelease;

const latestVersion = clean(tag_name);

if (isNullish(latestVersion)) {
console.log(red(`Cannot extract version from the ${release} release. Reach out Juno❗️`));
return {result: 'error'};
}

return {result: 'success', latestVersion};
};
2 changes: 1 addition & 1 deletion src/configs/cli.state.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
type CliStateSatelliteAppliedConfigHashes
} from '../types/cli/cli.state';

export const getStateConfig = (): Conf<CliState> =>
const getStateConfig = (): Conf<CliState> =>
new Conf<CliState>({projectName: ENV.config.projectStateName});

export const getLatestAppliedConfig = ({
Expand Down
32 changes: 32 additions & 0 deletions src/configs/cli.versions.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Conf from 'conf';
import {type CachedVersion, type CachedVersions} from '../types/cli/cli.versions';

const getVersionConfig = (): Conf<CachedVersions> =>
new Conf<CachedVersions>({projectName: 'juno-cli-versions'});

export const getCachedVersions = (): Conf<CachedVersions> => getVersionConfig();

export const updateLastCheckToNow = ({key}: {key: keyof CachedVersions}) => {
const config = getVersionConfig();

const currentVersions = config.get(key);

config.set(key, {
lastCheck: new Date().toISOString(),
...(currentVersions ?? {})
});
};

export const saveCachedVersions = ({
key,
versions
}: {
key: keyof CachedVersions;
versions: Omit<CachedVersion, 'lastCheck'>;
}) => {
const config = getVersionConfig();
config.set(key, {
lastCheck: new Date().toISOString(),
...versions
});
};
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {logHelpUpgrade} from './help/upgrade.help';
import {logHelpVersion} from './help/version.help';
import {logHelpWhoAmI} from './help/whoami.help';
import {checkNodeVersion} from './utils/env.utils';
import {checkWeeklyVersions} from './version';

export const run = async () => {
const {valid} = checkNodeVersion();
Expand Down Expand Up @@ -192,11 +193,15 @@ export const run = async () => {
break;
case 'help':
console.log(help);
process.exit(0);
break;
default:
console.log(red('Unknown command.'));
console.log(help);
process.exit(-1);
}

await checkWeeklyVersions({cmd});
};

// eslint-disable-next-line @typescript-eslint/no-floating-promises
Expand Down
2 changes: 1 addition & 1 deletion src/services/assets/deploy.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {clearProposalStagedAssets} from '../changes/changes.clear.services';
import {applyConfig} from '../config/apply.services';
import {init} from '../config/init.services';
import {links} from '../links.services';
import {getSatelliteVersion} from '../version.services';
import {getSatelliteVersion} from '../version/version.services';
import {parseBatchSize} from './_args.services';
import {deployImmediate} from './_deploy/deploy.individual.services';
import {deployWithProposal as executeDeployWithProposal} from './_deploy/deploy.with-proposal.services';
Expand Down
167 changes: 167 additions & 0 deletions src/services/version/version.check.services.ts
Original file line number Diff line number Diff line change
@@ -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 CachedVersions;
currentVersion: string;
releaseFn: () => Promise<GithubLastReleaseResult>;
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<BuildVersionFromGitHubResult> => {
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});
};
Loading
Loading