Socket
Socket
Sign inDemoInstall

@oclif/plugin-update

Package Overview
Dependencies
Maintainers
2
Versions
192
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@oclif/plugin-update - npm Package Compare versions

Comparing version 1.0.5 to 1.1.0

lib/github.d.ts

2

.oclif.manifest.json

@@ -1,1 +0,1 @@

{"version":"1.0.5","commands":{"update":{"id":"update","description":"update the <%= config.bin %> CLI","pluginName":"@oclif/plugin-update","pluginType":"core","aliases":[],"flags":{"autoupdate":{"name":"autoupdate","type":"boolean","hidden":true}},"args":[{"name":"channel"}]}}}
{"version":"1.1.0","commands":{"update":{"id":"update","description":"update the <%= config.bin %> CLI","pluginName":"@oclif/plugin-update","pluginType":"core","aliases":[],"flags":{"autoupdate":{"name":"autoupdate","type":"boolean","hidden":true}},"args":[{"name":"channel"}]}}}

@@ -0,1 +1,14 @@

<a name="1.1.0"></a>
# [1.1.0](https://github.com/oclif/plugin-update/compare/v1.0.5...v1.1.0) (2018-04-07)
### Bug Fixes
* fixed github check ([7fe3d42](https://github.com/oclif/plugin-update/commit/7fe3d42))
### Features
* added github updater ([cbd2a4f](https://github.com/oclif/plugin-update/commit/cbd2a4f))
<a name="1.0.5"></a>

@@ -2,0 +15,0 @@ ## [1.0.5](https://github.com/oclif/plugin-update/compare/v1.0.4...v1.0.5) (2018-04-07)

@@ -12,3 +12,3 @@ import Command from '@oclif/command';

name: string;
char?: "a" | "b" | "i" | "p" | "q" | "s" | "u" | "g" | "c" | "d" | "e" | "f" | "h" | "j" | "k" | "l" | "m" | "n" | "o" | "r" | "t" | "v" | "x" | "y" | "z" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "X" | "Y" | "Z" | undefined;
char?: "a" | "b" | "i" | "p" | "q" | "s" | "u" | "g" | "A" | "c" | "d" | "e" | "f" | "h" | "j" | "k" | "l" | "m" | "n" | "o" | "r" | "t" | "v" | "x" | "y" | "z" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "X" | "Y" | "Z" | undefined;
description?: string | undefined;

@@ -29,4 +29,3 @@ hidden?: boolean | undefined;

private mtime(f);
private shouldUpdate(manifest);
private debounce();
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const color_1 = require("@heroku-cli/color");
const command_1 = require("@oclif/command");

@@ -15,3 +14,3 @@ const cli_ux_1 = require("cli-ux");

super(...arguments);
this.updater = new __1.Updater(this.config);
this.updater = __1.fetchUpdater(this.config);
}

@@ -21,3 +20,3 @@ async run() {

this.autoupdate = !!flags.autoupdate;
if (flags.autoupdate) {
if (this.autoupdate) {
await this.debounce();

@@ -29,9 +28,5 @@ }

}
// if (this.config.updateDisabled) {
// // cli.warn(this.config.updateDisabled)
// } else {
cli_ux_1.default.action.start(`${this.config.name}: Updating CLI`);
let channel = args.channel || this.updater.channel;
let manifest = await this.updater.fetchManifest(channel);
if (this.config.version === manifest.version && channel === this.updater.channel) {
if (!await this.updater.needsUpdate(channel)) {
if (!process.env.OCLIF_HIDE_UPDATED_MESSAGE) {

@@ -41,11 +36,5 @@ cli_ux_1.default.action.stop(`already on latest version: ${this.config.version}`);

}
else if (this.shouldUpdate(manifest)) {
cli_ux_1.default.action.start(`${this.config.name}: Updating CLI from ${color_1.color.green(this.config.version)} to ${color_1.color.green(manifest.version)}${channel === 'stable' ? '' : ' (' + color_1.color.yellow(channel) + ')'}`);
await this.updater.update(manifest);
else {
await this.updater.update({ channel });
}
// }
this.debug('fetch version');
await this.updater.fetchVersion(true);
this.debug('plugins update');
// await PluginsUpdate.run([], this.config)
this.debug('log chop');

@@ -55,3 +44,2 @@ await this.logChop();

await this.updater.tidy();
// const hooks = new Hooks(this.config)
await this.config.runHook('update', { channel });

@@ -74,17 +62,5 @@ this.debug('done');

}
shouldUpdate(manifest) {
try {
const chance = Math.random() * 100;
if (this.autoupdate && manifest.priority && chance < manifest.priority) {
cli_ux_1.default.log(`skipping update. priority is ${manifest.priority} but chance is ${chance}`);
return false;
}
}
catch (err) {
cli_ux_1.default.warn(err);
}
return true;
}
async debounce() {
const m = await this.mtime(this.updater.lastrunfile);
const lastrunfile = path.join(this.config.cacheDir, 'lastrun');
const m = await this.mtime(lastrunfile);
const waitUntil = dateAddHours(m, 1);

@@ -91,0 +67,0 @@ if (dateIsAfter(waitUntil, new Date())) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_ux_1 = require("cli-ux");
const __1 = require("..");
const spawn = require("cross-spawn");
const dateIsAfter = require("date-fns/is_after");
const dateSubHours = require("date-fns/sub_hours");
const fs = require("fs-extra");
const path = require("path");
const util_1 = require("../util");
const debug = require('debug')('cli:updater');
function timestamp(msg) {
return `[${new Date().toISOString()}] ${msg}`;
}
async function mtime(f) {
const { mtime } = await fs.stat(f);
return mtime;
}
exports.init = async function (opts) {
cli_ux_1.default.config.errlog = opts.config.errlog;
if (opts.id === 'update')
return;
const updater = new __1.Updater(opts.config);
await updater.autoupdate();
cli_ux_1.default.config.errlog = opts.config.errlog;
const binPath = this.config.scopedEnvVar('CLI_BINPATH') || this.config.bin;
const lastrunfile = path.join(this.config.cacheDir, 'lastrun');
const autoupdatefile = path.join(this.config.cacheDir, 'autoupdate');
const autoupdatelogfile = path.join(this.config.cacheDir, 'autoupdate.log');
const clientRoot = path.join(this.config.dataDir, 'client');
const autoupdateEnv = Object.assign({}, process.env, { [this.config.scopedEnvVarKey('TIMESTAMPS')]: '1', [this.config.scopedEnvVarKey('SKIP_ANALYTICS')]: '1' });
async function autoupdateNeeded() {
try {
const m = await mtime(autoupdatefile);
return dateIsAfter(m, dateSubHours(new Date(), 5));
}
catch (err) {
if (err.code !== 'ENOENT')
cli_ux_1.default.error(err.stack);
if (global.testing)
return false;
debug('autoupdate ENOENT');
return true;
}
}
await util_1.touch(lastrunfile);
const clientDir = path.join(clientRoot, this.config.version);
if (await fs.pathExists(clientDir)) {
await util_1.touch(clientDir);
}
if (!await autoupdateNeeded())
return;
debug('autoupdate running');
await fs.outputFile(autoupdatefile, '');
debug(`spawning autoupdate on ${binPath}`);
let fd = await fs.open(autoupdatelogfile, 'a');
// @ts-ignore
fs.write(fd, timestamp(`starting \`${binPath} update --autoupdate\` from ${process.argv.slice(1, 3).join(' ')}\n`));
spawn(binPath, ['update', '--autoupdate'], {
detached: !this.config.windows,
stdio: ['ignore', fd, fd],
env: autoupdateEnv,
})
.on('error', (e) => process.emitWarning(e))
.unref();
};
import * as Config from '@oclif/config';
export interface IVersion {
version: string;
channel: string;
message?: string;
}
export interface IManifest {
version: string;
channel: string;
sha256gz: string;
priority?: number;
}
export declare class Updater {
export declare function fetchUpdater(config: Config.IConfig): Updater;
export declare abstract class Updater {
config: Config.IConfig;

@@ -18,29 +8,16 @@ constructor(config: Config.IConfig);

readonly reexecBin: string | undefined;
readonly name: string;
readonly autoupdatefile: string;
readonly autoupdatelogfile: string;
readonly versionFile: string;
readonly lastrunfile: string;
private readonly clientRoot;
private readonly clientBin;
private readonly binPath;
private readonly s3Host;
s3url(channel: string, p: string): string;
fetchManifest(channel: string): Promise<IManifest>;
fetchVersion(download: boolean): Promise<IVersion>;
warnIfUpdateAvailable(): Promise<void>;
autoupdate(force?: boolean): Promise<void>;
update(manifest: IManifest): Promise<void>;
update({version, url, sha256, channel}: {
url: string;
version: string;
sha256?: string;
channel?: string;
}): Promise<void>;
tidy(): Promise<void>;
private extract(stream, dir, sha);
private base(manifest);
private autoupdateNeeded();
readonly timestampEnvVar: string;
readonly skipAnalyticsEnvVar: string;
readonly autoupdateEnv: {
[k: string]: string | undefined;
};
private reexecUpdate();
private _createBin(manifest);
private _catch(fn);
abstract needsUpdate(channel: string): Promise<boolean>;
protected base(version: string): string;
private extract(stream, dir, sha?);
private reexec();
private _createBin(version);
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const color_1 = require("@heroku-cli/color");
const cli_ux_1 = require("cli-ux");

@@ -7,3 +8,2 @@ const spawn = require("cross-spawn");

const dateSubDays = require("date-fns/sub_days");
const dateSubHours = require("date-fns/sub_hours");
const fs = require("fs-extra");

@@ -13,13 +13,15 @@ const path = require("path");

const debug = require('debug')('cli:updater');
async function mtime(f) {
const { mtime } = await fs.stat(f);
return mtime;
function fetchUpdater(config) {
switch (config.pjson.oclif.autoupdate) {
case 'github':
return new (require('./github').GithubUpdater)(config);
case 's3':
return new (require('./s3').S3Updater)(config);
}
throw new Error('oclif.autoupdate must be set to "github" or "s3"');
}
function timestamp(msg) {
return `[${new Date().toISOString()}] ${msg}`;
}
exports.fetchUpdater = fetchUpdater;
class Updater {
constructor(config) {
this.config = config;
this.config = config;
}

@@ -35,17 +37,2 @@ get channel() {

}
get name() {
return this.config.name === '@oclif/plugin-update' ? 'heroku-cli' : this.config.name;
}
get autoupdatefile() {
return path.join(this.config.cacheDir, 'autoupdate');
}
get autoupdatelogfile() {
return path.join(this.config.cacheDir, 'autoupdate.log');
}
get versionFile() {
return path.join(this.config.cacheDir, `${this.channel}.version`);
}
get lastrunfile() {
return path.join(this.config.cacheDir, 'lastrun');
}
get clientRoot() {

@@ -58,99 +45,14 @@ return path.join(this.config.dataDir, 'client');

}
get binPath() {
return this.reexecBin || this.config.bin;
}
get s3Host() {
const pjson = this.config.pjson.oclif;
return (pjson.s3 && pjson.s3.host) || this.config.scopedEnvVar('S3_HOST');
}
s3url(channel, p) {
if (!this.s3Host)
throw new Error('S3 host not defined');
return `https://${this.s3Host}/${this.name}/channels/${channel}/${p}`;
}
async fetchManifest(channel) {
const http = require('http-call').HTTP;
try {
let { body } = await http.get(this.s3url(channel, `${this.config.platform}-${this.config.arch}`));
return body;
}
catch (err) {
if (err.statusCode === 403)
throw new Error(`HTTP 403: Invalid channel ${channel}`);
throw err;
}
}
async fetchVersion(download) {
const http = require('http-call').HTTP;
let v;
try {
if (!download)
v = await fs.readJSON(this.versionFile);
}
catch (err) {
if (err.code !== 'ENOENT')
throw err;
}
if (!v) {
debug('fetching latest %s version', this.channel);
let { body } = await http.get(this.s3url(this.channel, 'version'));
v = body;
await this._catch(() => fs.outputJSON(this.versionFile, v));
}
return v;
}
async warnIfUpdateAvailable() {
await this._catch(async () => {
if (!this.s3Host)
return;
let v = await this.fetchVersion(false);
if (util_1.minorVersionGreater(this.config.version, v.version)) {
cli_ux_1.cli.warn(`${this.name}: update available from ${this.config.version} to ${v.version}`);
}
if (v.message) {
cli_ux_1.cli.warn(`${this.name}: ${v.message}`);
}
});
}
async autoupdate(force = false) {
try {
await util_1.touch(this.lastrunfile);
const clientDir = path.join(this.clientRoot, this.config.version);
if (await fs.pathExists(clientDir)) {
await util_1.touch(clientDir);
}
await this.warnIfUpdateAvailable();
if (!force && !await this.autoupdateNeeded())
return;
debug('autoupdate running');
await fs.outputFile(this.autoupdatefile, '');
debug(`spawning autoupdate on ${this.binPath}`);
let fd = await fs.open(this.autoupdatelogfile, 'a');
// @ts-ignore
fs.write(fd, timestamp(`starting \`${this.binPath} update --autoupdate\` from ${process.argv.slice(1, 3).join(' ')}\n`));
spawn(this.binPath, ['update', '--autoupdate'], {
detached: !this.config.windows,
stdio: ['ignore', fd, fd],
env: this.autoupdateEnv,
})
.on('error', (e) => process.emitWarning(e))
.unref();
}
catch (e) {
process.emitWarning(e);
}
}
async update(manifest) {
async update({ version, url, sha256, channel }) {
if (!channel)
channel = 'stable';
cli_ux_1.cli.action.start(`${this.config.name}: Updating CLI from ${color_1.default.green(this.config.version)} to ${color_1.default.green(version)}${channel === 'stable' ? '' : ' (' + color_1.default.yellow(channel) + ')'}`);
const _ = require('lodash');
const http = require('http-call').HTTP;
const filesize = require('filesize');
let base = this.base(manifest);
const output = path.join(this.clientRoot, manifest.version);
const tmp = path.join(this.clientRoot, base);
if (!this.s3Host)
throw new Error('S3 host not defined');
let url = `https://${this.s3Host}/${this.name}/channels/${manifest.channel}/${base}.tar.gz`;
const output = path.join(this.clientRoot, version);
const tmp = path.join(this.clientRoot, this.config.bin);
let { response: stream } = await http.stream(url);
await fs.emptyDir(tmp);
let extraction = this.extract(stream, this.clientRoot, manifest.sha256gz);
let extraction = this.extract(stream, this.clientRoot, sha256);
// TODO: use cli.action.type

@@ -177,4 +79,4 @@ if (cli_ux_1.cli.action.frames) {

await util_1.touch(output);
await this._createBin(manifest);
await this.reexecUpdate();
await this._createBin(version);
await this.reexec();
}

@@ -205,2 +107,5 @@ async tidy() {

}
base(version) {
return `${this.config.bin}-v${version}-${this.config.platform}-${this.config.arch}`;
}
extract(stream, dir, sha) {

@@ -223,15 +128,20 @@ const zlib = require('zlib');

};
let hasher = crypto.createHash('sha256');
stream.on('error', fail);
stream.on('data', d => hasher.update(d));
stream.on('end', () => {
let shasum = hasher.digest('hex');
if (sha === shasum) {
shaValidated = true;
check();
}
else {
reject(new Error(`SHA mismatch: expected ${shasum} to be ${sha}`));
}
});
if (sha) {
let hasher = crypto.createHash('sha256');
stream.on('error', fail);
stream.on('data', d => hasher.update(d));
stream.on('end', () => {
let shasum = hasher.digest('hex');
if (sha === shasum) {
shaValidated = true;
check();
}
else {
reject(new Error(`SHA mismatch: expected ${shasum} to be ${sha}`));
}
});
}
else {
shaValidated = true;
}
let ignore = (_, header) => {

@@ -241,3 +151,3 @@ switch (header.type) {

case 'file':
if (process.env.CLI_ENGINE_DEBUG_UPDATE_FILES)
if (process.env.OCLIF_DEBUG_UPDATE_FILES)
debug(header.name);

@@ -262,32 +172,3 @@ return false;

}
base(manifest) {
return `${this.name}-v${manifest.version}-${this.config.platform}-${this.config.arch}`;
}
async autoupdateNeeded() {
try {
const m = await mtime(this.autoupdatefile);
return dateIsAfter(m, dateSubHours(new Date(), 5));
}
catch (err) {
if (err.code !== 'ENOENT')
cli_ux_1.cli.error(err.stack);
if (global.testing)
return false;
debug('autoupdate ENOENT');
return true;
}
}
get timestampEnvVar() {
// TODO: use function from @cli-engine/config
let bin = this.config.bin.replace('-', '_').toUpperCase();
return `${bin}_TIMESTAMPS`;
}
get skipAnalyticsEnvVar() {
let bin = this.config.bin.replace('-', '_').toUpperCase();
return `${bin}_SKIP_ANALYTICS`;
}
get autoupdateEnv() {
return Object.assign({}, process.env, { [this.timestampEnvVar]: '1', [this.skipAnalyticsEnvVar]: '1' });
}
async reexecUpdate() {
async reexec() {
cli_ux_1.cli.action.stop();

@@ -311,25 +192,34 @@ return new Promise((_, reject) => {

}
async _createBin(manifest) {
async _createBin(version) {
let dst = this.clientBin;
if (this.config.windows) {
let body = `@echo off
"%~dp0\\..\\${manifest.version}\\bin\\${this.config.bin}.cmd" %*
"%~dp0\\..\\${version}\\bin\\${this.config.bin}.cmd" %*
`;
await fs.outputFile(dst, body);
return;
}
let src = path.join('..', manifest.version, 'bin', this.config.bin);
await fs.mkdirp(path.dirname(dst));
await fs.remove(dst);
await fs.symlink(src, dst);
}
async _catch(fn) {
try {
return await Promise.resolve(fn());
else {
let body = `#!/usr/bin/env bash
set -e
get_script_dir () {
SOURCE="\${BASH_SOURCE[0]}"
# While $SOURCE is a symlink, resolve it
while [ -h "$SOURCE" ]; do
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$( readlink "$SOURCE" )"
# If $SOURCE was a relative symlink (so no "/" as prefix, need to resolve it relative to the symlink base directory
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
echo "$DIR"
}
DIR=$(get_script_dir)
HEROKU_CLI_REDIRECTED=1 "$DIR/../${version}/bin/${this.config.bin}" "$@"
`;
await fs.remove(dst);
await fs.outputFile(dst, body);
await fs.chmod(dst, 0o755);
}
catch (err) {
debug(err);
}
}
}
exports.Updater = Updater;
/// <reference types="node" />
import * as fs from 'fs-extra';
export declare function minorVersionGreater(fromString: string, toString: string): boolean;
export declare function touch(p: string): Promise<void>;

@@ -5,0 +4,0 @@ export declare function ls(dir: string): Promise<{

@@ -5,13 +5,2 @@ "use strict";

const path = require("path");
function minorVersionGreater(fromString, toString) {
const semver = require('semver');
const from = semver.parse(fromString);
const to = semver.parse(toString);
if (from.major < to.major)
return true;
if (from.major === to.major && from.minor < to.minor)
return true;
return false;
}
exports.minorVersionGreater = minorVersionGreater;
async function touch(p) {

@@ -18,0 +7,0 @@ try {

{
"name": "@oclif/plugin-update",
"version": "1.0.5",
"version": "1.1.0",
"author": "Jeff Dickey @jdxcode",

@@ -9,3 +9,3 @@ "bugs": "https://github.com/oclif/plugin-update/issues",

"@oclif/command": "^1.4.7",
"@oclif/config": "^1.3.66",
"@oclif/config": "^1.3.67",
"@oclif/errors": "^1.0.3",

@@ -26,3 +26,3 @@ "@types/semver": "^5.5.0",

"devDependencies": {
"@oclif/dev-cli": "^1.7.2",
"@oclif/dev-cli": "^1.7.3",
"@oclif/plugin-help": "^1.2.3",

@@ -59,7 +59,5 @@ "@oclif/test": "^1.0.4",

"oclif": {
"autoupdate": "github",
"commands": "./lib/commands",
"bin": "oclif-example",
"s3": {
"host": "cli-assets.heroku.com"
},
"hooks": {

@@ -66,0 +64,0 @@ "init": "./lib/hooks/init"

@@ -24,3 +24,3 @@ @oclif/plugin-update

$ oclif-example (-v|--version|version)
@oclif/plugin-update/1.0.5 linux-x64 node-v9.11.1
@oclif/plugin-update/1.1.0 linux-x64 node-v9.11.1
$ oclif-example --help [COMMAND]

@@ -45,3 +45,3 @@ USAGE

_See code: [src/commands/update.ts](https://github.com/oclif/plugin-update/blob/v1.0.5/src/commands/update.ts)_
_See code: [src/commands/update.ts](https://github.com/oclif/plugin-update/blob/v1.1.0/src/commands/update.ts)_
<!-- commandsstop -->
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc