@oclif/plugin-update 3.2.2 → 3.2.4-qa.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +14 -10
- package/dist/commands/update.d.ts +20 -0
- package/dist/commands/update.js +83 -0
- package/dist/hooks/init.js +67 -0
- package/dist/tar.d.ts +6 -0
- package/{lib → dist}/tar.js +34 -30
- package/dist/update.d.ts +26 -0
- package/dist/update.js +362 -0
- package/{lib → dist}/util.d.ts +3 -4
- package/dist/util.js +22 -0
- package/oclif.lock +7063 -0
- package/oclif.manifest.json +59 -46
- package/package.json +47 -35
- package/lib/commands/update.d.ts +0 -20
- package/lib/commands/update.js +0 -87
- package/lib/hooks/init.js +0 -67
- package/lib/tar.d.ts +0 -2
- package/lib/update.d.ts +0 -44
- package/lib/update.js +0 -388
- package/lib/util.js +0 -37
- /package/{lib → dist}/hooks/init.d.ts +0 -0
package/README.md
CHANGED
@@ -1,22 +1,25 @@
|
|
1
|
-
@oclif/plugin-update
|
2
|
-
====================
|
1
|
+
# @oclif/plugin-update
|
3
2
|
|
4
3
|
[![Version](https://img.shields.io/npm/v/@oclif/plugin-update.svg)](https://npmjs.org/package/@oclif/plugin-update)
|
5
|
-
[![CircleCI](https://circleci.com/gh/oclif/plugin-update/tree/main.svg?style=shield)](https://circleci.com/gh/oclif/plugin-update/tree/main)
|
6
|
-
[![Appveyor CI](https://ci.appveyor.com/api/projects/status/github/oclif/plugin-update?branch=main&svg=true)](https://ci.appveyor.com/project/oclif/plugin-update/branch/main)
|
7
4
|
[![Downloads/week](https://img.shields.io/npm/dw/@oclif/plugin-update.svg)](https://npmjs.org/package/@oclif/plugin-update)
|
8
5
|
[![License](https://img.shields.io/npm/l/@oclif/plugin-update.svg)](https://github.com/oclif/plugin-update/blob/main/package.json)
|
9
6
|
|
10
7
|
<!-- toc -->
|
11
|
-
|
12
|
-
|
8
|
+
|
9
|
+
- [@oclif/plugin-update](#oclifplugin-update)
|
10
|
+
- [Usage](#usage)
|
11
|
+
- [Commands](#commands)
|
13
12
|
<!-- tocstop -->
|
13
|
+
|
14
14
|
# Usage
|
15
|
+
|
15
16
|
See https://oclif.io/docs/releasing.html#autoupdater
|
16
17
|
|
17
18
|
# Commands
|
19
|
+
|
18
20
|
<!-- commands -->
|
19
|
-
|
21
|
+
|
22
|
+
- [`oclif-example update [CHANNEL]`](#oclif-example-update-channel)
|
20
23
|
|
21
24
|
## `oclif-example update [CHANNEL]`
|
22
25
|
|
@@ -24,10 +27,10 @@ update the oclif-example CLI
|
|
24
27
|
|
25
28
|
```
|
26
29
|
USAGE
|
27
|
-
$ oclif-example update [CHANNEL] [-a] [-v <value>
|
30
|
+
$ oclif-example update [CHANNEL] [-a] [--force] [-i | -v <value>]
|
28
31
|
|
29
32
|
FLAGS
|
30
|
-
-a, --available
|
33
|
+
-a, --available See available versions.
|
31
34
|
-i, --interactive Interactively select version to install. This is ignored if a channel is provided.
|
32
35
|
-v, --version=<value> Install a specific version.
|
33
36
|
--force Force a re-download of the requested version.
|
@@ -53,5 +56,6 @@ EXAMPLES
|
|
53
56
|
$ oclif-example update --available
|
54
57
|
```
|
55
58
|
|
56
|
-
_See code: [src/commands/update.ts](https://github.com/oclif/plugin-update/blob/
|
59
|
+
_See code: [src/commands/update.ts](https://github.com/oclif/plugin-update/blob/3.2.5-qa.0/src/commands/update.ts)_
|
60
|
+
|
57
61
|
<!-- commandsstop -->
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { Command } from '@oclif/core';
|
2
|
+
export default class UpdateCommand extends Command {
|
3
|
+
static args: {
|
4
|
+
channel: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
5
|
+
};
|
6
|
+
static description: string;
|
7
|
+
static examples: {
|
8
|
+
command: string;
|
9
|
+
description: string;
|
10
|
+
}[];
|
11
|
+
static flags: {
|
12
|
+
autoupdate: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
13
|
+
available: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
14
|
+
force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
15
|
+
interactive: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
16
|
+
version: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
17
|
+
};
|
18
|
+
run(): Promise<void>;
|
19
|
+
private promptForVersion;
|
20
|
+
}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import { Args, Command, Flags, ux } from '@oclif/core';
|
2
|
+
import inquirer from 'inquirer';
|
3
|
+
import { basename } from 'node:path';
|
4
|
+
import { sort } from 'semver';
|
5
|
+
import { Updater } from '../update.js';
|
6
|
+
export default class UpdateCommand extends Command {
|
7
|
+
static args = {
|
8
|
+
channel: Args.string({ optional: true }),
|
9
|
+
};
|
10
|
+
static description = 'update the <%= config.bin %> CLI';
|
11
|
+
static examples = [
|
12
|
+
{
|
13
|
+
command: '<%= config.bin %> <%= command.id %> stable',
|
14
|
+
description: 'Update to the stable channel:',
|
15
|
+
},
|
16
|
+
{
|
17
|
+
command: '<%= config.bin %> <%= command.id %> --version 1.0.0',
|
18
|
+
description: 'Update to a specific version:',
|
19
|
+
},
|
20
|
+
{
|
21
|
+
command: '<%= config.bin %> <%= command.id %> --interactive',
|
22
|
+
description: 'Interactively select version:',
|
23
|
+
},
|
24
|
+
{
|
25
|
+
command: '<%= config.bin %> <%= command.id %> --available',
|
26
|
+
description: 'See available versions:',
|
27
|
+
},
|
28
|
+
];
|
29
|
+
static flags = {
|
30
|
+
autoupdate: Flags.boolean({ hidden: true }),
|
31
|
+
available: Flags.boolean({
|
32
|
+
char: 'a',
|
33
|
+
description: 'See available versions.',
|
34
|
+
}),
|
35
|
+
force: Flags.boolean({
|
36
|
+
description: 'Force a re-download of the requested version.',
|
37
|
+
}),
|
38
|
+
interactive: Flags.boolean({
|
39
|
+
char: 'i',
|
40
|
+
description: 'Interactively select version to install. This is ignored if a channel is provided.',
|
41
|
+
exclusive: ['version'],
|
42
|
+
}),
|
43
|
+
version: Flags.string({
|
44
|
+
char: 'v',
|
45
|
+
description: 'Install a specific version.',
|
46
|
+
exclusive: ['interactive'],
|
47
|
+
}),
|
48
|
+
};
|
49
|
+
async run() {
|
50
|
+
const { args, flags } = await this.parse(UpdateCommand);
|
51
|
+
const updater = new Updater(this.config);
|
52
|
+
if (flags.available) {
|
53
|
+
const index = await updater.fetchVersionIndex();
|
54
|
+
const allVersions = sort(Object.keys(index)).reverse();
|
55
|
+
const localVersions = await updater.findLocalVersions();
|
56
|
+
const table = allVersions.map((version) => {
|
57
|
+
const location = localVersions.find((l) => basename(l).startsWith(version)) || index[version];
|
58
|
+
return { location, version };
|
59
|
+
});
|
60
|
+
ux.table(table, { location: {}, version: {} });
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
if (args.channel && flags.version) {
|
64
|
+
this.error('You cannot specify both a version and a channel.');
|
65
|
+
}
|
66
|
+
return updater.runUpdate({
|
67
|
+
autoUpdate: flags.autoupdate,
|
68
|
+
channel: args.channel,
|
69
|
+
force: flags.force,
|
70
|
+
version: flags.interactive ? await this.promptForVersion(updater) : flags.version,
|
71
|
+
});
|
72
|
+
}
|
73
|
+
async promptForVersion(updater) {
|
74
|
+
const choices = sort(Object.keys(await updater.fetchVersionIndex())).reverse();
|
75
|
+
const { version } = await inquirer.prompt({
|
76
|
+
choices: [...choices, new inquirer.Separator()],
|
77
|
+
message: 'Select a version to update to',
|
78
|
+
name: 'version',
|
79
|
+
type: 'list',
|
80
|
+
});
|
81
|
+
return version;
|
82
|
+
}
|
83
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import { spawn } from 'cross-spawn';
|
2
|
+
import makeDebug from 'debug';
|
3
|
+
import { existsSync } from 'node:fs';
|
4
|
+
import { open, stat, writeFile } from 'node:fs/promises';
|
5
|
+
import { join } from 'node:path';
|
6
|
+
import { touch } from '../util.js';
|
7
|
+
const debug = makeDebug('cli:updater');
|
8
|
+
function timestamp(msg) {
|
9
|
+
return `[${new Date().toISOString()}] ${msg}`;
|
10
|
+
}
|
11
|
+
async function mtime(f) {
|
12
|
+
const { mtime } = await stat(f);
|
13
|
+
return mtime;
|
14
|
+
}
|
15
|
+
export const init = async function (opts) {
|
16
|
+
if (opts.id === 'update')
|
17
|
+
return;
|
18
|
+
if (opts.config.scopedEnvVarTrue('DISABLE_AUTOUPDATE'))
|
19
|
+
return;
|
20
|
+
const { config, error: throwError } = this;
|
21
|
+
const binPath = config.binPath ?? config.bin;
|
22
|
+
const lastrunfile = join(config.cacheDir, 'lastrun');
|
23
|
+
const autoupdatefile = join(config.cacheDir, 'autoupdate');
|
24
|
+
const autoupdatelogfile = join(config.cacheDir, 'autoupdate.log');
|
25
|
+
const clientRoot = config.scopedEnvVar('OCLIF_CLIENT_HOME') ?? join(config.dataDir, 'client');
|
26
|
+
const autoupdateEnv = {
|
27
|
+
...process.env,
|
28
|
+
[config.scopedEnvVarKey('SKIP_ANALYTICS')]: '1',
|
29
|
+
[config.scopedEnvVarKey('TIMESTAMPS')]: '1',
|
30
|
+
};
|
31
|
+
async function autoupdateNeeded() {
|
32
|
+
try {
|
33
|
+
const m = await mtime(autoupdatefile);
|
34
|
+
let days = 1;
|
35
|
+
if (opts.config.channel === 'stable')
|
36
|
+
days = 14;
|
37
|
+
m.setHours(m.getHours() + days * 24);
|
38
|
+
return m < new Date();
|
39
|
+
}
|
40
|
+
catch (error) {
|
41
|
+
const err = error;
|
42
|
+
if (err.code !== 'ENOENT')
|
43
|
+
throwError(err.stack);
|
44
|
+
debug('autoupdate ENOENT');
|
45
|
+
return true;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
await touch(lastrunfile);
|
49
|
+
const clientDir = join(clientRoot, config.version);
|
50
|
+
if (existsSync(clientDir))
|
51
|
+
await touch(clientDir);
|
52
|
+
if (!(await autoupdateNeeded()))
|
53
|
+
return;
|
54
|
+
debug('autoupdate running');
|
55
|
+
await writeFile(autoupdatefile, '');
|
56
|
+
debug(`spawning autoupdate on ${binPath}`);
|
57
|
+
const fd = await open(autoupdatelogfile, 'a');
|
58
|
+
await writeFile(fd, timestamp(`starting \`${binPath} update --autoupdate\` from ${process.argv.slice(1, 3).join(' ')}\n`));
|
59
|
+
const stream = fd.createWriteStream();
|
60
|
+
spawn(binPath, ['update', '--autoupdate'], {
|
61
|
+
detached: !config.windows,
|
62
|
+
env: autoupdateEnv,
|
63
|
+
stdio: ['ignore', stream, stream],
|
64
|
+
})
|
65
|
+
.on('error', (e) => process.emitWarning(e))
|
66
|
+
.unref();
|
67
|
+
};
|
package/dist/tar.d.ts
ADDED
package/{lib → dist}/tar.js
RENAMED
@@ -1,42 +1,43 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
const
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
import makeDebug from 'debug';
|
2
|
+
import { existsSync } from 'node:fs';
|
3
|
+
import { cp, rename, rm } from 'node:fs/promises';
|
4
|
+
import { join } from 'node:path';
|
5
|
+
import { touch } from './util.js';
|
6
|
+
const debug = makeDebug('oclif-update');
|
7
|
+
import crypto from 'node:crypto';
|
8
|
+
import zlib from 'node:zlib';
|
9
|
+
import { extract as tarExtract } from 'tar-fs';
|
10
|
+
const ignore = (_name, header) => {
|
11
|
+
switch (header?.type) {
|
11
12
|
case 'directory':
|
12
|
-
case 'file':
|
13
|
+
case 'file': {
|
13
14
|
if (process.env.OCLIF_DEBUG_UPDATE_FILES)
|
14
15
|
debug(header.name);
|
15
16
|
return false;
|
16
|
-
|
17
|
+
}
|
18
|
+
case 'symlink': {
|
17
19
|
return true;
|
18
|
-
|
19
|
-
|
20
|
+
}
|
21
|
+
default: {
|
22
|
+
throw new Error(header?.type);
|
23
|
+
}
|
20
24
|
}
|
21
25
|
};
|
22
26
|
async function extract(stream, basename, output, sha) {
|
23
27
|
const getTmp = () => `${output}.partial.${Math.random().toString().split('.')[1].slice(0, 5)}`;
|
24
28
|
let tmp = getTmp();
|
25
|
-
if (
|
29
|
+
if (existsSync(tmp))
|
26
30
|
tmp = getTmp();
|
27
31
|
debug(`extracting to ${tmp}`);
|
28
32
|
try {
|
29
33
|
await new Promise((resolve, reject) => {
|
30
|
-
const zlib = require('zlib');
|
31
|
-
const tar = require('tar-fs');
|
32
|
-
const crypto = require('crypto');
|
33
34
|
let shaValidated = false;
|
34
35
|
let extracted = false;
|
35
36
|
const check = () => shaValidated && extracted && resolve(null);
|
36
37
|
if (sha) {
|
37
38
|
const hasher = crypto.createHash('sha256');
|
38
39
|
stream.on('error', reject);
|
39
|
-
stream.on('data', d => hasher.update(d));
|
40
|
+
stream.on('data', (d) => hasher.update(d));
|
40
41
|
stream.on('end', () => {
|
41
42
|
const shasum = hasher.digest('hex');
|
42
43
|
if (sha === shasum) {
|
@@ -50,7 +51,7 @@ async function extract(stream, basename, output, sha) {
|
|
50
51
|
}
|
51
52
|
else
|
52
53
|
shaValidated = true;
|
53
|
-
const extract =
|
54
|
+
const extract = tarExtract(tmp, { ignore });
|
54
55
|
extract.on('error', reject);
|
55
56
|
extract.on('finish', () => {
|
56
57
|
extracted = true;
|
@@ -60,27 +61,30 @@ async function extract(stream, basename, output, sha) {
|
|
60
61
|
gunzip.on('error', reject);
|
61
62
|
stream.pipe(gunzip).pipe(extract);
|
62
63
|
});
|
63
|
-
if (
|
64
|
+
if (existsSync(output)) {
|
64
65
|
try {
|
65
66
|
const tmp = getTmp();
|
66
|
-
await
|
67
|
-
await
|
67
|
+
await cp(output, tmp);
|
68
|
+
await rm(tmp, { force: true, recursive: true }).catch(debug);
|
68
69
|
}
|
69
70
|
catch (error) {
|
70
71
|
debug(error);
|
71
|
-
await
|
72
|
+
await rm(tmp, { force: true, recursive: true }).catch(debug);
|
72
73
|
}
|
73
74
|
}
|
74
|
-
const from =
|
75
|
+
const from = join(tmp, basename);
|
75
76
|
debug('moving %s to %s', from, output);
|
76
|
-
await
|
77
|
-
await
|
78
|
-
await
|
77
|
+
await rename(from, output);
|
78
|
+
await rm(tmp, { force: true, recursive: true }).catch(debug);
|
79
|
+
await touch(output);
|
79
80
|
debug('done extracting');
|
80
81
|
}
|
81
82
|
catch (error) {
|
82
|
-
await
|
83
|
+
await rm(tmp, { force: true, recursive: true }).catch(process.emitWarning);
|
83
84
|
throw error;
|
84
85
|
}
|
85
86
|
}
|
86
|
-
|
87
|
+
// This is done so that we can stub it in tests
|
88
|
+
export const Extractor = {
|
89
|
+
extract,
|
90
|
+
};
|
package/dist/update.d.ts
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
import { Config } from '@oclif/core';
|
2
|
+
type Options = {
|
3
|
+
autoUpdate: boolean;
|
4
|
+
channel?: string | undefined;
|
5
|
+
force?: boolean;
|
6
|
+
version?: string | undefined;
|
7
|
+
};
|
8
|
+
type VersionIndex = Record<string, string>;
|
9
|
+
export declare class Updater {
|
10
|
+
private config;
|
11
|
+
private readonly clientBin;
|
12
|
+
private readonly clientRoot;
|
13
|
+
constructor(config: Config);
|
14
|
+
fetchVersionIndex(): Promise<VersionIndex>;
|
15
|
+
findLocalVersions(): Promise<string[]>;
|
16
|
+
runUpdate(options: Options): Promise<void>;
|
17
|
+
private createBin;
|
18
|
+
private fetchVersionManifest;
|
19
|
+
private findLocalVersion;
|
20
|
+
private refreshConfig;
|
21
|
+
private tidy;
|
22
|
+
private touch;
|
23
|
+
private update;
|
24
|
+
private updateToExistingVersion;
|
25
|
+
}
|
26
|
+
export {};
|