@redocly/cli
Version:
[@Redocly](https://redocly.com) CLI is your all-in-one OpenAPI utility. It builds, manages, improves, and quality-checks your OpenAPI descriptions, all of which comes in handy for various phases of the API Lifecycle. Create your own rulesets to make API g
116 lines (96 loc) • 3.49 kB
text/typescript
import { tmpdir } from 'os';
import { join } from 'path';
import { existsSync, writeFileSync, readFileSync, statSync } from 'fs';
import { compare } from 'semver';
import fetch, { DEFAULT_FETCH_TIMEOUT } from './fetch-with-timeout';
import { cyan, green, yellow } from 'colorette';
import { cleanColors } from './miscellaneous';
export const { version, name } = require('../../package.json');
const VERSION_CACHE_FILE = 'redocly-cli-version';
const SPACE_TO_BORDER = 4;
const INTERVAL_TO_CHECK = 1000 * 60 * 60 * 12;
const SHOULD_NOT_NOTIFY =
process.env.NODE_ENV === 'test' ||
process.env.CI ||
!!process.env.LAMBDA_TASK_ROOT ||
process.env.REDOCLY_SUPPRESS_UPDATE_NOTICE === 'true';
export const notifyUpdateCliVersion = () => {
if (SHOULD_NOT_NOTIFY) {
return;
}
try {
const latestVersion = readFileSync(join(tmpdir(), VERSION_CACHE_FILE)).toString();
if (isNewVersionAvailable(version, latestVersion)) {
renderUpdateBanner(version, latestVersion);
}
} catch (e) {
return;
}
};
const isNewVersionAvailable = (current: string, latest: string) => compare(current, latest) < 0;
const getLatestVersion = async (packageName: string): Promise<string | undefined> => {
const latestUrl = `http://registry.npmjs.org/${packageName}/latest`;
try {
const response = await fetch(latestUrl, { timeout: DEFAULT_FETCH_TIMEOUT });
const info = await response.json();
return info.version;
} catch {
// Do nothing
return;
}
};
export const cacheLatestVersion = () => {
if (!isNeedToBeCached() || SHOULD_NOT_NOTIFY) {
return;
}
getLatestVersion(name)
.then((version) => {
if (version) {
const lastCheckFile = join(tmpdir(), VERSION_CACHE_FILE);
writeFileSync(lastCheckFile, version);
}
})
.catch(() => {});
};
const renderUpdateBanner = (current: string, latest: string) => {
const messageLines = [
`A new version of ${cyan('Redocly CLI')} (${green(latest)}) is available.`,
`Update now: \`${cyan('npm i -g @redocly/cli@latest')}\`.`,
`Changelog: https://redocly.com/docs/cli/changelog/`,
];
const maxLength = Math.max(...messageLines.map((line) => cleanColors(line).length));
const border = yellow('═'.repeat(maxLength + SPACE_TO_BORDER));
const extraSpaces = ' '.repeat(SPACE_TO_BORDER);
const banner = [
'',
extraSpaces + yellow('╔' + border + '╗'),
extraSpaces + yellow('║' + ' '.repeat(maxLength + SPACE_TO_BORDER) + '║'),
messageLines.map(getLineWithPadding(maxLength, extraSpaces)).join('\n'),
extraSpaces + yellow('║' + ' '.repeat(maxLength + SPACE_TO_BORDER) + '║'),
extraSpaces + yellow('╚' + border + '╝'),
'',
'',
].join('\n');
process.stderr.write(banner);
};
const getLineWithPadding =
(maxLength: number, extraSpaces: string) =>
(line: string): string => {
const padding = ' '.repeat(maxLength - cleanColors(line).length);
return `${extraSpaces}${yellow('║')} ${line}${padding} ${yellow('║')}`;
};
const isNeedToBeCached = (): boolean => {
try {
// Last version from npm is stored in a file in the OS temp folder
const versionFile = join(tmpdir(), VERSION_CACHE_FILE);
if (!existsSync(versionFile)) {
return true;
}
const now = new Date().getTime();
const stats = statSync(versionFile);
const lastCheck = stats.mtime.getTime();
return now - lastCheck >= INTERVAL_TO_CHECK;
} catch (e) {
return false;
}
};