Skip to content

Commit a201ad2

Browse files
feat: weekly versions check (#490)
* feat: weekly versions check Signed-off-by: David Dal Busco <[email protected]> * feat: iso date Signed-off-by: David Dal Busco <[email protected]> * feat: same version pattern Signed-off-by: David Dal Busco <[email protected]> * feat: check emulator version Signed-off-by: David Dal Busco <[email protected]> * feat: no fetch if not needed Signed-off-by: David Dal Busco <[email protected]> * feat: fine tuning Signed-off-by: David Dal Busco <[email protected]> * chore: lint Signed-off-by: David Dal Busco <[email protected]> --------- Signed-off-by: David Dal Busco <[email protected]>
1 parent 6b0f928 commit a201ad2

11 files changed

Lines changed: 333 additions & 62 deletions

File tree

src/commands/status.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
ORBITER_WASM_NAME,
1313
SATELLITE_WASM_NAME
1414
} from '../constants/constants';
15-
import {checkVersion, getSatelliteVersion} from '../services/version.services';
15+
import {checkVersion, getSatelliteVersion} from '../services/version/version.services';
1616
import type {AssetKey} from '../types/asset-key';
1717
import {toAssetKeys} from '../utils/asset-key.utils';
1818
import {orbiterKey, satelliteKey} from '../utils/cli.config.utils';

src/commands/version.ts

Lines changed: 12 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import {isEmptyString, isNullish} from '@dfinity/utils';
2-
import {green, red} from 'kleur';
3-
import {clean} from 'semver';
1+
import {isEmptyString} from '@dfinity/utils';
2+
import {green} from 'kleur';
43
import {version as cliCurrentVersion} from '../../package.json';
5-
import {
6-
githubCliLastRelease,
7-
githubJunoDockerLastRelease,
8-
type GithubLastReleaseResult
9-
} from '../rest/github.rest';
4+
import {githubCliLastRelease, githubJunoDockerLastRelease} from '../rest/github.rest';
105
import {findEmulatorVersion} from '../services/emulator/version.services';
11-
import {checkVersion, type CheckVersionResult} from '../services/version.services';
12-
import {detectPackageManager} from '../utils/pm.utils';
6+
import {
7+
buildVersionFromGitHub,
8+
checkVersion,
9+
type CheckVersionResult
10+
} from '../services/version/version.services';
11+
import {pmInstallHint} from '../utils/pm.utils';
1312

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

2423
const cliVersion = async (): Promise<CheckVersionResult> => {
2524
const result = await buildVersionFromGitHub({
26-
release: 'CLI',
25+
logReleaseOnError: () => 'CLI',
2726
releaseFn: githubCliLastRelease
2827
});
2928

@@ -37,23 +36,10 @@ const cliVersion = async (): Promise<CheckVersionResult> => {
3736
currentVersion: cliCurrentVersion,
3837
latestVersion,
3938
displayHint: 'CLI',
40-
commandLineHint: installHint()
39+
commandLineHint: pmInstallHint()
4140
});
4241
};
4342

44-
const installHint = (): string => {
45-
const pm = detectPackageManager();
46-
47-
switch (pm) {
48-
case 'yarn':
49-
return 'yarn global add @junobuild/cli';
50-
case 'pnpm':
51-
return 'pnpm add -g @junobuild/cli';
52-
default:
53-
return 'npm i -g @junobuild/cli';
54-
}
55-
};
56-
5743
const emulatorVersion = async () => {
5844
const emulatorResult = await findEmulatorVersion();
5945

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

6652
const result = await buildVersionFromGitHub({
67-
release: 'Juno Docker',
53+
logReleaseOnError: () => 'Juno Docker',
6854
releaseFn: githubJunoDockerLastRelease
6955
});
7056

@@ -88,31 +74,3 @@ const emulatorVersion = async () => {
8874
displayHint: 'Emulator'
8975
});
9076
};
91-
92-
const buildVersionFromGitHub = async ({
93-
releaseFn,
94-
release
95-
}: {
96-
releaseFn: () => Promise<GithubLastReleaseResult>;
97-
release: 'CLI' | 'Juno Docker';
98-
}): Promise<{result: 'success'; latestVersion: string} | {result: 'error'}> => {
99-
const githubRelease = await releaseFn();
100-
101-
if (githubRelease.status === 'error') {
102-
console.log(red(`Cannot fetch the last version of ${release} on GitHub 😢.`));
103-
return {result: 'error'};
104-
}
105-
106-
const {
107-
release: {tag_name}
108-
} = githubRelease;
109-
110-
const latestVersion = clean(tag_name);
111-
112-
if (isNullish(latestVersion)) {
113-
console.log(red(`Cannot extract version from the ${release} release. Reach out Juno❗️`));
114-
return {result: 'error'};
115-
}
116-
117-
return {result: 'success', latestVersion};
118-
};

src/configs/cli.state.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
type CliStateSatelliteAppliedConfigHashes
88
} from '../types/cli/cli.state';
99

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

1313
export const getLatestAppliedConfig = ({

src/configs/cli.versions.config.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import Conf from 'conf';
2+
import {type CachedVersion, type CachedVersions} from '../types/cli/cli.versions';
3+
4+
const getVersionConfig = (): Conf<CachedVersions> =>
5+
new Conf<CachedVersions>({projectName: 'juno-cli-versions'});
6+
7+
export const getCachedVersions = (): Conf<CachedVersions> => getVersionConfig();
8+
9+
export const updateLastCheckToNow = ({key}: {key: keyof CachedVersions}) => {
10+
const config = getVersionConfig();
11+
12+
const currentVersions = config.get(key);
13+
14+
config.set(key, {
15+
lastCheck: new Date().toISOString(),
16+
...(currentVersions ?? {})
17+
});
18+
};
19+
20+
export const saveCachedVersions = ({
21+
key,
22+
versions
23+
}: {
24+
key: keyof CachedVersions;
25+
versions: Omit<CachedVersion, 'lastCheck'>;
26+
}) => {
27+
const config = getVersionConfig();
28+
config.set(key, {
29+
lastCheck: new Date().toISOString(),
30+
...versions
31+
});
32+
};

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {logHelpUpgrade} from './help/upgrade.help';
3131
import {logHelpVersion} from './help/version.help';
3232
import {logHelpWhoAmI} from './help/whoami.help';
3333
import {checkNodeVersion} from './utils/env.utils';
34+
import {checkWeeklyVersions} from './version';
3435

3536
export const run = async () => {
3637
const {valid} = checkNodeVersion();
@@ -192,11 +193,15 @@ export const run = async () => {
192193
break;
193194
case 'help':
194195
console.log(help);
196+
process.exit(0);
195197
break;
196198
default:
197199
console.log(red('Unknown command.'));
198200
console.log(help);
201+
process.exit(-1);
199202
}
203+
204+
await checkWeeklyVersions({cmd});
200205
};
201206

202207
// eslint-disable-next-line @typescript-eslint/no-floating-promises

src/services/assets/deploy.services.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {clearProposalStagedAssets} from '../changes/changes.clear.services';
77
import {applyConfig} from '../config/apply.services';
88
import {init} from '../config/init.services';
99
import {links} from '../links.services';
10-
import {getSatelliteVersion} from '../version.services';
10+
import {getSatelliteVersion} from '../version/version.services';
1111
import {parseBatchSize} from './_args.services';
1212
import {deployImmediate} from './_deploy/deploy.individual.services';
1313
import {deployWithProposal as executeDeployWithProposal} from './_deploy/deploy.with-proposal.services';
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import {isNullish, nonNullish} from '@dfinity/utils';
2+
import ora from 'ora';
3+
import {compare} from 'semver';
4+
import {version as cliCurrentVersion} from '../../../package.json';
5+
import {
6+
getCachedVersions,
7+
saveCachedVersions,
8+
updateLastCheckToNow
9+
} from '../../configs/cli.versions.config';
10+
import {
11+
githubCliLastRelease,
12+
githubJunoDockerLastRelease,
13+
type GithubLastReleaseResult
14+
} from '../../rest/github.rest';
15+
import {type CachedVersions} from '../../types/cli/cli.versions';
16+
import {pmInstallHint} from '../../utils/pm.utils';
17+
import {findEmulatorVersion} from '../emulator/version.services';
18+
import {
19+
buildVersionFromGitHub,
20+
type BuildVersionFromGitHubResult,
21+
checkVersion
22+
} from './version.services';
23+
24+
const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;
25+
26+
export const checkCliVersion = async () => {
27+
const checkVersionFn = ({latestVersion}: {latestVersion: string}) => {
28+
checkVersion({
29+
currentVersion: cliCurrentVersion,
30+
latestVersion,
31+
displayHint: 'CLI',
32+
commandLineHint: pmInstallHint(),
33+
logUpToDate: false,
34+
logSpacer: true
35+
});
36+
};
37+
38+
await check({
39+
key: 'cli',
40+
currentVersion: cliCurrentVersion,
41+
releaseFn: githubCliLastRelease,
42+
checkVersionFn
43+
});
44+
};
45+
46+
export const checkEmulatorVersion = async () => {
47+
const emulatorResult = await findEmulatorVersion();
48+
49+
if (emulatorResult.status !== 'success') {
50+
return;
51+
}
52+
53+
const {version: emulatorCurrentVersion} = emulatorResult;
54+
55+
// We fetched the emulator but the version is null which could happen has providing the metadata
56+
// was patched in Juno Docker v0.6.3
57+
if (isNullish(emulatorCurrentVersion)) {
58+
return;
59+
}
60+
61+
const checkVersionFn = ({latestVersion}: {latestVersion: string}) => {
62+
checkVersion({
63+
currentVersion: emulatorCurrentVersion,
64+
latestVersion,
65+
displayHint: 'Emulator',
66+
logUpToDate: false,
67+
logSpacer: true
68+
});
69+
};
70+
71+
await check({
72+
key: 'emulator',
73+
currentVersion: emulatorCurrentVersion,
74+
releaseFn: githubJunoDockerLastRelease,
75+
checkVersionFn
76+
});
77+
};
78+
79+
const check = async ({
80+
key,
81+
currentVersion,
82+
releaseFn,
83+
checkVersionFn
84+
}: {
85+
key: keyof CachedVersions;
86+
currentVersion: string;
87+
releaseFn: () => Promise<GithubLastReleaseResult>;
88+
checkVersionFn: (params: {latestVersion: string}) => void;
89+
}) => {
90+
const cachedVersions = getCachedVersions();
91+
92+
const cachedInfo = cachedVersions.get(key);
93+
94+
const lastCheck = cachedInfo?.lastCheck;
95+
96+
if (isNullish(lastCheck)) {
97+
saveCachedVersions({
98+
key,
99+
versions: {
100+
local: currentVersion
101+
}
102+
});
103+
return;
104+
}
105+
106+
const cachedLocalVersion = cachedInfo?.local;
107+
108+
// The version was never cached or the developer upgraded since the last check.
109+
// We assume they are on the latest version. If not, the next weekly check will catch it.
110+
if (isNullish(cachedLocalVersion) || compare(currentVersion, cachedLocalVersion) > 0) {
111+
saveCachedVersions({
112+
key,
113+
versions: {
114+
local: currentVersion
115+
}
116+
});
117+
return;
118+
}
119+
120+
const checkIsDue =
121+
new Date(new Date(lastCheck).getTime() + ONE_WEEK_MS).getTime() <= new Date().getTime();
122+
123+
const cachedRemoteVersion = cachedInfo?.remote;
124+
125+
// The weekly check is not due and we got a remote version in cache
126+
if (!checkIsDue && nonNullish(cachedRemoteVersion)) {
127+
// The current version is newer or equals, we assume the dev use the latest
128+
if (compare(currentVersion, cachedRemoteVersion) >= 0) {
129+
return;
130+
}
131+
132+
// Behind but check not due, compare cached remote version
133+
checkVersionFn({latestVersion: cachedRemoteVersion});
134+
return;
135+
}
136+
137+
const loadVersionWithGitHub = async (): Promise<BuildVersionFromGitHubResult> => {
138+
const spinner = ora(`Two secs, fetching ${key} latest version...`).start();
139+
140+
try {
141+
return await buildVersionFromGitHub({
142+
releaseFn
143+
});
144+
} finally {
145+
spinner.stop();
146+
}
147+
};
148+
149+
const result = await loadVersionWithGitHub();
150+
151+
if (result.result === 'error') {
152+
updateLastCheckToNow({key});
153+
return;
154+
}
155+
156+
const {latestVersion} = result;
157+
158+
saveCachedVersions({
159+
key,
160+
versions: {
161+
local: currentVersion,
162+
remote: latestVersion
163+
}
164+
});
165+
166+
checkVersionFn({latestVersion});
167+
};

0 commit comments

Comments
 (0)