0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-16 21:56:25 -05:00

feat: verdaccio update notification on CLI (#988) (#998)

This commit is contained in:
Ayush Sharma 2018-09-20 09:53:29 +02:00 committed by GitHub
parent 0b7224f038
commit bc04703ce7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 367 additions and 102 deletions

View file

@ -1,108 +1,98 @@
// flow-typed signature: fa51178772ad1f35158cb4238bc3f1eb
// flow-typed version: da30fe6876/chalk_v2.x.x/flow_>=v0.25.x
// flow-typed signature: db5b2cdde8db39d47e27cc8ab84f89bf
// flow-typed version: d662d43161/chalk_v2.x.x/flow_>=v0.25.x
type $npm$chalk$StyleElement = {
open: string,
close: string
};
type $npm$chalk$Chain = $npm$chalk$Style & ((...text: any[]) => string);
type $npm$chalk$Style = {
// General
reset: $npm$chalk$Chain,
bold: $npm$chalk$Chain,
dim: $npm$chalk$Chain,
italic: $npm$chalk$Chain,
underline: $npm$chalk$Chain,
inverse: $npm$chalk$Chain,
strikethrough: $npm$chalk$Chain,
// Text colors
black: $npm$chalk$Chain,
red: $npm$chalk$Chain,
redBright: $npm$chalk$Chain,
green: $npm$chalk$Chain,
greenBright: $npm$chalk$Chain,
yellow: $npm$chalk$Chain,
yellowBright: $npm$chalk$Chain,
blue: $npm$chalk$Chain,
blueBright: $npm$chalk$Chain,
magenta: $npm$chalk$Chain,
magentaBright: $npm$chalk$Chain,
cyan: $npm$chalk$Chain,
cyanBright: $npm$chalk$Chain,
white: $npm$chalk$Chain,
whiteBright: $npm$chalk$Chain,
gray: $npm$chalk$Chain,
grey: $npm$chalk$Chain,
// Background colors
bgBlack: $npm$chalk$Chain,
bgBlackBright: $npm$chalk$Chain,
bgRed: $npm$chalk$Chain,
bgRedBright: $npm$chalk$Chain,
bgGreen: $npm$chalk$Chain,
bgGreenBright: $npm$chalk$Chain,
bgYellow: $npm$chalk$Chain,
bgYellowBright: $npm$chalk$Chain,
bgBlue: $npm$chalk$Chain,
bgBlueBright: $npm$chalk$Chain,
bgMagenta: $npm$chalk$Chain,
bgMagentaBright: $npm$chalk$Chain,
bgCyan: $npm$chalk$Chain,
bgCyanBright: $npm$chalk$Chain,
bgWhite: $npm$chalk$Chain,
bgWhiteBright: $npm$chalk$Chain
};
// From: https://github.com/chalk/chalk/blob/master/index.js.flow
declare module "chalk" {
declare var enabled: boolean;
declare var supportsColor: boolean;
declare type TemplateStringsArray = $ReadOnlyArray<string>;
// General
declare var reset: $npm$chalk$Chain;
declare var bold: $npm$chalk$Chain;
declare var dim: $npm$chalk$Chain;
declare var italic: $npm$chalk$Chain;
declare var underline: $npm$chalk$Chain;
declare var inverse: $npm$chalk$Chain;
declare var strikethrough: $npm$chalk$Chain;
declare type Level = $Values<{
None: 0,
Basic: 1,
Ansi256: 2,
TrueColor: 3
}>;
// Text colors
declare var black: $npm$chalk$Chain;
declare var red: $npm$chalk$Chain;
declare var redBright: $npm$chalk$Chain;
declare var green: $npm$chalk$Chain;
declare var greenBright: $npm$chalk$Chain;
declare var yellow: $npm$chalk$Chain;
declare var yellowBright: $npm$chalk$Chain;
declare var blue: $npm$chalk$Chain;
declare var blueBright: $npm$chalk$Chain;
declare var magenta: $npm$chalk$Chain;
declare var magentaBright: $npm$chalk$Chain;
declare var cyan: $npm$chalk$Chain;
declare var cyanBright: $npm$chalk$Chain;
declare var white: $npm$chalk$Chain;
declare var whiteBright: $npm$chalk$Chain;
declare var gray: $npm$chalk$Chain;
declare var grey: $npm$chalk$Chain;
declare type ChalkOptions = {|
enabled?: boolean,
level?: Level
|};
// Background colors
declare var bgBlack: $npm$chalk$Chain;
declare var bgBlackBright: $npm$chalk$Chain;
declare var bgRed: $npm$chalk$Chain;
declare var bgRedBright: $npm$chalk$Chain;
declare var bgGreen: $npm$chalk$Chain;
declare var bgGreenBright: $npm$chalk$Chain;
declare var bgYellow: $npm$chalk$Chain;
declare var bgYellowBright: $npm$chalk$Chain;
declare var bgBlue: $npm$chalk$Chain;
declare var bgBlueBright: $npm$chalk$Chain;
declare var bgMagenta: $npm$chalk$Chain;
declare var bgMagentaBright: $npm$chalk$Chain;
declare var bgCyan: $npm$chalk$Chain;
declare var bgCyanBright: $npm$chalk$Chain;
declare var bgWhite: $npm$chalk$Chain;
declare var bgWhiteBright: $npm$chalk$Chain;
declare type ColorSupport = {|
level: Level,
hasBasic: boolean,
has256: boolean,
has16m: boolean
|};
declare interface Chalk {
(...text: string[]): string,
(text: TemplateStringsArray, ...placeholders: string[]): string,
constructor(options?: ChalkOptions): Chalk,
enabled: boolean,
level: Level,
rgb(r: number, g: number, b: number): Chalk,
hsl(h: number, s: number, l: number): Chalk,
hsv(h: number, s: number, v: number): Chalk,
hwb(h: number, w: number, b: number): Chalk,
bgHex(color: string): Chalk,
bgKeyword(color: string): Chalk,
bgRgb(r: number, g: number, b: number): Chalk,
bgHsl(h: number, s: number, l: number): Chalk,
bgHsv(h: number, s: number, v: number): Chalk,
bgHwb(h: number, w: number, b: number): Chalk,
hex(color: string): Chalk,
keyword(color: string): Chalk,
+reset: Chalk,
+bold: Chalk,
+dim: Chalk,
+italic: Chalk,
+underline: Chalk,
+inverse: Chalk,
+hidden: Chalk,
+strikethrough: Chalk,
+visible: Chalk,
+black: Chalk,
+red: Chalk,
+green: Chalk,
+yellow: Chalk,
+blue: Chalk,
+magenta: Chalk,
+cyan: Chalk,
+white: Chalk,
+gray: Chalk,
+grey: Chalk,
+blackBright: Chalk,
+redBright: Chalk,
+greenBright: Chalk,
+yellowBright: Chalk,
+blueBright: Chalk,
+magentaBright: Chalk,
+cyanBright: Chalk,
+whiteBright: Chalk,
+bgBlack: Chalk,
+bgRed: Chalk,
+bgGreen: Chalk,
+bgYellow: Chalk,
+bgBlue: Chalk,
+bgMagenta: Chalk,
+bgCyan: Chalk,
+bgWhite: Chalk,
+bgBlackBright: Chalk,
+bgRedBright: Chalk,
+bgGreenBright: Chalk,
+bgYellowBright: Chalk,
+bgBlueBright: Chalk,
+bgMagentaBright: Chalk,
+bgCyanBright: Chalk,
+bgWhiteBrigh: Chalk,
supportsColor: ColorSupport
}
declare module.exports: Chalk;
}

View file

@ -164,7 +164,7 @@
"pretest": "npm run code:build",
"test": "npm run test:unit",
"test:clean": "npx jest --clearCache",
"test:unit": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC jest --config ./jest.config.js --maxWorkers 2",
"test:unit": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC FORCE_COLOR=1 jest --config ./jest.config.js --maxWorkers 2",
"test:functional": "cross-env NODE_ENV=testOldEnv jest --config ./test/jest.config.functional.js --testPathPattern ./test/functional/index*",
"test:e2e": "cross-env BABEL_ENV=testOldEnv jest --config ./test/jest.config.e2e.js",
"test:size": "bundlesize",

View file

@ -8,6 +8,7 @@ import semver from 'semver';
import chalk from 'chalk';
import {startVerdaccio, listenDefaultCallback} from './bootstrap';
import findConfigFile from './config-path';
import {verdaccioUpdateBanner} from './update-banner';
if (process.getuid && process.getuid() === 0) {
global.console.warn(chalk.bgYellow('Verdaccio doesn\'t need superuser privileges. Don\'t run it under root.'));
@ -31,6 +32,11 @@ const pkginfo = require('pkginfo')(module); // eslint-disable-line no-unused-var
const pkgVersion = module.exports.version;
const pkgName = module.exports.name;
/**
* Checking verdaccio version on NPM
*/
verdaccioUpdateBanner(pkgVersion);
commander
.option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)')
.option('-c, --config <config.yaml>', 'use this configuration file (default: ./config.yaml)')

View file

@ -104,3 +104,7 @@ export const PACKAGE_ACCESS = {
SCOPE: '@*/*',
ALL: '**',
};
export const UPDATE_BANNER = {
CHANGELOG_URL: 'https://github.com/verdaccio/verdaccio/releases/tag/'
}

61
src/lib/update-banner.js Normal file
View file

@ -0,0 +1,61 @@
// @prettier
// @flow
import request from 'request';
import semver from 'semver';
import chalk from 'chalk';
import _ from 'lodash';
import { UPDATE_BANNER, DEFAULT_REGISTRY, HTTP_STATUS } from './constants';
const VERDACCIO_LATEST_REGISTRY_URL = `${DEFAULT_REGISTRY}/verdaccio/latest`;
/**
* Creates NPM update banner using chalk
*/
export function createBanner(currentVersion: string, newVersion: string, releaseType: string): string {
const changelog = `${UPDATE_BANNER.CHANGELOG_URL}v${newVersion}`
const versionUpdate = `${chalk.bold.red(currentVersion)}${chalk.bold.green(newVersion)}`
const banner = chalk`
{white.bold A new ${_.upperCase(releaseType)} version of Verdaccio is available. ${versionUpdate} }
{white.bold Run ${chalk.green.bold('npm install -g verdaccio')} to update}.
{white.bold Registry: ${DEFAULT_REGISTRY}}
{blue.bold Changelog: ${changelog}}
`;
return banner;
}
/**
* creates error banner
*/
export function createErrorBanner(message: string): string {
const banner = chalk`
{red.bold Unable to check verdaccio version on ${DEFAULT_REGISTRY}}
{red.bold Error: ${message}}
`;
return banner;
}
/**
* Show verdaccio update banner on start
*/
export function verdaccioUpdateBanner(pkgVersion: string) {
request(VERDACCIO_LATEST_REGISTRY_URL, function (error: ?Object = null, response: Object = {}) {
if (!error && response.statusCode === HTTP_STATUS.OK && response.body) {
// In case, NPM does not returns version, keeping version equals to
// verdaccio version.
const { version = pkgVersion } = JSON.parse(response.body);
const releaseType = semver.diff(version, pkgVersion);
if (releaseType && semver.gt(version, pkgVersion)) {
const banner = createBanner(pkgVersion, version, releaseType);
/* eslint-disable-next-line */
console.log(banner);
}
} else {
const errorBanner = createErrorBanner(JSON.stringify(error));
/* eslint-disable-next-line */
console.log(errorBanner);
}
});
}

View file

@ -0,0 +1,117 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Verdaccio update banner should print major update banner 1`] = `
[MockFunction] {
"calls": Array [
Array [
"
A new MAJOR version of Verdaccio is available. 3.0.0 → 4.5.6 
Run npm install -g verdaccio to update.
Registry: https://registry.npmjs.org
Changelog: https://github.com/verdaccio/verdaccio/releases/tag/v4.5.6
",
],
],
"results": Array [
Object {
"isThrow": false,
"value": undefined,
},
],
}
`;
exports[`Verdaccio update banner should print minor update banner 1`] = `
[MockFunction] {
"calls": Array [
Array [
"
A new MINOR version of Verdaccio is available. 4.0.0 → 4.5.6 
Run npm install -g verdaccio to update.
Registry: https://registry.npmjs.org
Changelog: https://github.com/verdaccio/verdaccio/releases/tag/v4.5.6
",
],
],
"results": Array [
Object {
"isThrow": false,
"value": undefined,
},
],
}
`;
exports[`Verdaccio update banner should print patch update banner 1`] = `
[MockFunction] {
"calls": Array [
Array [
"
A new PATCH version of Verdaccio is available. 4.5.0 → 4.5.6 
Run npm install -g verdaccio to update.
Registry: https://registry.npmjs.org
Changelog: https://github.com/verdaccio/verdaccio/releases/tag/v4.5.6
",
],
],
"results": Array [
Object {
"isThrow": false,
"value": undefined,
},
],
}
`;
exports[`Verdaccio update banner when default registry returns with error 1`] = `
[MockFunction] {
"calls": Array [
Array [
"
Unable to check verdaccio version on https://registry.npmjs.org
Error: {\\"message\\":\\"internal server error\\",\\"statusCode\\":500}
",
],
],
"results": Array [
Object {
"isThrow": false,
"value": undefined,
},
],
}
`;
exports[`create banner should create a major update banner 1`] = `
"
A new MAJOR version of Verdaccio is available. 1.0.0 → 2.0.0 
Run npm install -g verdaccio to update.
Registry: https://registry.npmjs.org
Changelog: https://github.com/verdaccio/verdaccio/releases/tag/v2.0.0
"
`;
exports[`create banner should create a minor update banner 1`] = `
"
A new MINOR version of Verdaccio is available. 1.0.0 → 1.1.0 
Run npm install -g verdaccio to update.
Registry: https://registry.npmjs.org
Changelog: https://github.com/verdaccio/verdaccio/releases/tag/v1.1.0
"
`;
exports[`create banner should create a patch update banner 1`] = `
"
A new PATCH version of Verdaccio is available. 1.0.0 → 1.0.1 
Run npm install -g verdaccio to update.
Registry: https://registry.npmjs.org
Changelog: https://github.com/verdaccio/verdaccio/releases/tag/v1.0.1
"
`;
exports[`createErrorBanner should create an error banner 1`] = `
"
Unable to check verdaccio version on https://registry.npmjs.org
Error: message
"
`;

View file

@ -0,0 +1,87 @@
// @prettier
// @flow
import {
createBanner,
createErrorBanner
} from '../../../src/lib/update-banner';
import {HTTP_STATUS, API_ERROR} from '../../../src/lib/constants';
jest.resetModules();
jest.doMock('request', () => (url: string, resolver: Function) => {
const response = {
body: JSON.stringify({version: '4.5.6' }),
statusCode: HTTP_STATUS.OK
}
resolver(null, response);
});
const banner = require('../../../src/lib/update-banner');
describe('Verdaccio update banner', () => {
let log;
beforeEach(() => {
// mocking global console.log method
global.console.log = jest.fn();
log = global.console.log
});
test('should print major update banner', () => {
banner.verdaccioUpdateBanner('3.0.0');
expect(log).toMatchSnapshot();
});
test('should print minor update banner', () => {
banner.verdaccioUpdateBanner('4.0.0');
expect(log).toMatchSnapshot();
});
test('should print patch update banner', () => {
banner.verdaccioUpdateBanner('4.5.0');
expect(log).toMatchSnapshot();
});
test('when local version is equals to npm version', () => {
banner.verdaccioUpdateBanner('4.5.6');
expect(log).not.toHaveBeenCalledWith();
});
test('when local version is greater than npm version', () => {
banner.verdaccioUpdateBanner('4.5.7');
expect(log).not.toHaveBeenCalledWith();
});
test('when default registry returns with error', () => {
jest.resetModules();
jest.doMock('request', () => (url: string, resolver: Function) => {
const error = {
message: API_ERROR.INTERNAL_SERVER_ERROR,
statusCode: HTTP_STATUS.INTERNAL_ERROR
}
resolver(error, null);
});
const banner = require('../../../src/lib/update-banner');
banner.verdaccioUpdateBanner('4.5.7');
expect(log).toMatchSnapshot();
})
});
describe('createErrorBanner', () => {
test('should create an error banner', () => {
expect(createErrorBanner('message')).toMatchSnapshot();
});
});
describe('create banner', () => {
test('should create a major update banner', () => {
expect(createBanner('1.0.0', '2.0.0', 'major')).toMatchSnapshot();
});
test('should create a minor update banner', () => {
expect(createBanner('1.0.0', '1.1.0', 'minor')).toMatchSnapshot();
});
test('should create a patch update banner', () => {
expect(createBanner('1.0.0', '1.0.1', 'patch')).toMatchSnapshot();
});
});