Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@oclif/plugin-update

Package Overview
Dependencies
Maintainers
2
Versions
204
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.3 to 1.0.4

2

.oclif.manifest.json

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

{"version":"1.0.3","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.0.4","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,9 @@

<a name="1.0.4"></a>
## [1.0.4](https://github.com/oclif/plugin-update/compare/e8dd1e98a7806d1a67f5294034fb5b99d6012222...v1.0.4) (2018-04-07)
### Bug Fixes
* updated deps ([f652abd](https://github.com/oclif/plugin-update/commit/f652abd))
<a name="1.0.3"></a>

@@ -2,0 +10,0 @@ ## [1.0.3](https://github.com/oclif/plugin-update/compare/bd0256816d53dd4b56618871741d56c7b3b1d7ca...v1.0.3) (2018-02-28)

import Command from '@oclif/command';
import { Updater } from '../update';
import { Updater } from '..';
export default class UpdateCommand extends Command {

@@ -4,0 +4,0 @@ static description: string;

@@ -10,3 +10,3 @@ "use strict";

const path = require("path");
const update_1 = require("../update");
const __1 = require("..");
const util_1 = require("../util");

@@ -16,3 +16,3 @@ class UpdateCommand extends command_1.default {

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

@@ -19,0 +19,0 @@ async run() {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_ux_1 = require("cli-ux");
const update_1 = require("../update");
const __1 = require("..");
exports.init = async function (opts) {

@@ -9,4 +9,4 @@ cli_ux_1.default.config.errlog = opts.config.errlog;

return;
const updater = new update_1.Updater(opts.config);
const updater = new __1.Updater(opts.config);
await updater.autoupdate();
};

@@ -1,2 +0,45 @@

declare const _default: {};
export default _default;
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 {
config: Config.IConfig;
constructor(config: Config.IConfig);
readonly channel: string;
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>;
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);
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = {};
const cli_ux_1 = require("cli-ux");
const spawn = require("cross-spawn");
const dateIsAfter = require("date-fns/is_after");
const dateSubDays = require("date-fns/sub_days");
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');
async function mtime(f) {
const { mtime } = await fs.stat(f);
return mtime;
}
function timestamp(msg) {
return `[${new Date().toISOString()}] ${msg}`;
}
class Updater {
constructor(config) {
this.config = config;
this.config = config;
}
get channel() {
let pjson = this.config.pjson.oclif;
if (pjson.channel)
return pjson.channel;
return 'stable';
}
get reexecBin() {
return this.config.scopedEnvVar('CLI_BINPATH');
}
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() {
return path.join(this.config.dataDir, 'client');
}
get clientBin() {
let b = path.join(this.clientRoot, 'bin', this.config.bin);
return this.config.windows ? `${b}.cmd` : b;
}
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) {
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`;
let { response: stream } = await http.stream(url);
await fs.emptyDir(tmp);
let extraction = this.extract(stream, this.clientRoot, manifest.sha256gz);
// TODO: use cli.action.type
if (cli_ux_1.cli.action.frames) {
// if spinner action
let total = stream.headers['content-length'];
let current = 0;
const updateStatus = _.throttle((newStatus) => {
cli_ux_1.cli.action.status = newStatus;
}, 500, { leading: true, trailing: false });
stream.on('data', data => {
current += data.length;
updateStatus(`${filesize(current)}/${filesize(total)}`);
});
}
await extraction;
if (await fs.pathExists(output)) {
const old = `${output}.old`;
await fs.remove(old);
await fs.rename(output, old);
}
await fs.rename(tmp, output);
await util_1.touch(output);
await this._createBin(manifest);
await this.reexecUpdate();
}
async tidy() {
try {
if (!this.reexecBin)
return;
if (!this.reexecBin.includes(this.config.version))
return;
let root = this.clientRoot;
if (!await fs.pathExists(root))
return;
let files = await util_1.ls(root);
let promises = files.map(async (f) => {
if (['bin', this.config.version].includes(path.basename(f.path)))
return;
if (dateIsAfter(f.stat.mtime, dateSubDays(new Date(), 7))) {
await fs.remove(f.path);
}
});
for (let p of promises)
await p;
}
catch (err) {
cli_ux_1.cli.warn(err);
}
}
extract(stream, dir, sha) {
const zlib = require('zlib');
const tar = require('tar-fs');
const crypto = require('crypto');
return new Promise((resolve, reject) => {
let shaValidated = false;
let extracted = false;
let check = () => {
if (shaValidated && extracted) {
resolve();
}
};
let fail = (err) => {
fs.remove(dir)
.then(() => reject(err))
.catch(reject);
};
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}`));
}
});
let ignore = (_, header) => {
switch (header.type) {
case 'directory':
case 'file':
if (process.env.CLI_ENGINE_DEBUG_UPDATE_FILES)
debug(header.name);
return false;
case 'symlink':
return true;
default:
throw new Error(header.type);
}
};
let extract = tar.extract(dir, { ignore });
extract.on('error', fail);
extract.on('finish', () => {
extracted = true;
check();
});
let gunzip = zlib.createGunzip();
gunzip.on('error', fail);
stream.pipe(gunzip).pipe(extract);
});
}
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() {
cli_ux_1.cli.action.stop();
return new Promise((_, reject) => {
debug('restarting CLI after update', this.clientBin);
spawn(this.clientBin, ['update'], {
stdio: 'inherit',
env: Object.assign({}, process.env, { CLI_ENGINE_HIDE_UPDATED_MESSAGE: '1' }),
})
.on('error', reject)
.on('close', (status) => {
try {
cli_ux_1.cli.exit(status);
}
catch (err) {
reject(err);
}
});
});
}
async _createBin(manifest) {
let dst = this.clientBin;
if (this.config.windows) {
let body = `@echo off
"%~dp0\\..\\${manifest.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());
}
catch (err) {
debug(err);
}
}
}
exports.Updater = Updater;
{
"name": "@oclif/plugin-update",
"version": "1.0.3",
"version": "1.0.4",
"author": "Jeff Dickey @jdxcode",

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

"@heroku-cli/color": "^1.1.3",
"@oclif/command": "^1.4.1",
"@oclif/config": "^1.3.57",
"@oclif/errors": "^1.0.2",
"@oclif/command": "^1.4.7",
"@oclif/config": "^1.3.64",
"@oclif/errors": "^1.0.3",
"@types/semver": "^5.5.0",
"cli-ux": "^3.3.23",
"cross-spawn": "^6.0.4",
"cli-ux": "^3.3.27",
"cross-spawn": "^6.0.5",
"date-fns": "^1.29.0",
"debug": "^3.1.0",
"filesize": "^3.6.0",
"filesize": "^3.6.1",
"fs-extra": "^5.0.0",
"http-call": "^5.0.2",
"http-call": "^5.1.0",
"lodash": "^4.17.5",

@@ -26,19 +26,19 @@ "log-chopper": "^1.0.2",

"devDependencies": {
"@oclif/dev-cli": "^1.2.18",
"@oclif/plugin-help": "^1.1.5",
"@oclif/test": "^1.0.1",
"@oclif/tslint": "^1.0.2",
"@oclif/dev-cli": "^1.4.4",
"@oclif/plugin-help": "^1.2.3",
"@oclif/test": "^1.0.4",
"@oclif/tslint": "^1.1.0",
"@types/chai": "^4.1.2",
"@types/cross-spawn": "^6.0.0",
"@types/fs-extra": "^5.0.1",
"@types/lodash": "^4.14.104",
"@types/mocha": "^2.2.48",
"@types/node": "^9.4.6",
"@types/lodash": "^4.14.106",
"@types/mocha": "^5.0.0",
"@types/node": "^9.6.2",
"@types/supports-color": "^3.1.0",
"chai": "^4.1.2",
"globby": "^8.0.1",
"mocha": "^5.0.1",
"ts-node": "^5.0.0",
"mocha": "^5.0.5",
"ts-node": "^5.0.1",
"tslint": "^5.9.1",
"typescript": "^2.7.2"
"typescript": "^2.8.1"
},

@@ -45,0 +45,0 @@ "engines": {

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