@oclif/plugin-update 4.0.1-qa.0 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 -32
- package/{lib → dist}/update.d.ts +15 -16
- package/{lib → dist}/update.js +174 -185
- package/{lib → dist}/util.d.ts +3 -3
- package/dist/util.js +22 -0
- package/oclif.lock +7082 -0
- package/oclif.manifest.json +59 -46
- package/package.json +48 -34
- package/lib/commands/update.d.ts +0 -20
- package/lib/commands/update.js +0 -87
- package/lib/hooks/init.js +0 -73
- package/lib/tar.d.ts +0 -2
- package/lib/util.js +0 -29
- /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
|
[](https://npmjs.org/package/@oclif/plugin-update)
|
5
|
-
[](https://circleci.com/gh/oclif/plugin-update/tree/main)
|
6
|
-
[](https://ci.appveyor.com/project/oclif/plugin-update/branch/main)
|
7
4
|
[](https://npmjs.org/package/@oclif/plugin-update)
|
8
5
|
[](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/4.1.1/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,43 +1,43 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
const
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
const ignore = (
|
11
|
-
switch (header
|
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) {
|
12
12
|
case 'directory':
|
13
|
-
case 'file':
|
13
|
+
case 'file': {
|
14
14
|
if (process.env.OCLIF_DEBUG_UPDATE_FILES)
|
15
15
|
debug(header.name);
|
16
16
|
return false;
|
17
|
-
|
17
|
+
}
|
18
|
+
case 'symlink': {
|
18
19
|
return true;
|
19
|
-
|
20
|
-
|
20
|
+
}
|
21
|
+
default: {
|
22
|
+
throw new Error(header?.type);
|
23
|
+
}
|
21
24
|
}
|
22
25
|
};
|
23
26
|
async function extract(stream, basename, output, sha) {
|
24
27
|
const getTmp = () => `${output}.partial.${Math.random().toString().split('.')[1].slice(0, 5)}`;
|
25
28
|
let tmp = getTmp();
|
26
|
-
if (
|
29
|
+
if (existsSync(tmp))
|
27
30
|
tmp = getTmp();
|
28
31
|
debug(`extracting to ${tmp}`);
|
29
32
|
try {
|
30
33
|
await new Promise((resolve, reject) => {
|
31
|
-
const zlib = require('zlib');
|
32
|
-
const tar = require('tar-fs');
|
33
|
-
const crypto = require('crypto');
|
34
34
|
let shaValidated = false;
|
35
35
|
let extracted = false;
|
36
36
|
const check = () => shaValidated && extracted && resolve(null);
|
37
37
|
if (sha) {
|
38
38
|
const hasher = crypto.createHash('sha256');
|
39
39
|
stream.on('error', reject);
|
40
|
-
stream.on('data', d => hasher.update(d));
|
40
|
+
stream.on('data', (d) => hasher.update(d));
|
41
41
|
stream.on('end', () => {
|
42
42
|
const shasum = hasher.digest('hex');
|
43
43
|
if (sha === shasum) {
|
@@ -51,7 +51,7 @@ async function extract(stream, basename, output, sha) {
|
|
51
51
|
}
|
52
52
|
else
|
53
53
|
shaValidated = true;
|
54
|
-
const extract =
|
54
|
+
const extract = tarExtract(tmp, { ignore });
|
55
55
|
extract.on('error', reject);
|
56
56
|
extract.on('finish', () => {
|
57
57
|
extracted = true;
|
@@ -61,28 +61,30 @@ async function extract(stream, basename, output, sha) {
|
|
61
61
|
gunzip.on('error', reject);
|
62
62
|
stream.pipe(gunzip).pipe(extract);
|
63
63
|
});
|
64
|
-
if (
|
64
|
+
if (existsSync(output)) {
|
65
65
|
try {
|
66
66
|
const tmp = getTmp();
|
67
|
-
|
68
|
-
await
|
69
|
-
await fs.rm(tmp, { recursive: true, force: true }).catch(debug);
|
67
|
+
await cp(output, tmp);
|
68
|
+
await rm(tmp, { force: true, recursive: true }).catch(debug);
|
70
69
|
}
|
71
70
|
catch (error) {
|
72
71
|
debug(error);
|
73
|
-
await
|
72
|
+
await rm(tmp, { force: true, recursive: true }).catch(debug);
|
74
73
|
}
|
75
74
|
}
|
76
|
-
const from =
|
75
|
+
const from = join(tmp, basename);
|
77
76
|
debug('moving %s to %s', from, output);
|
78
|
-
await
|
79
|
-
await
|
80
|
-
await
|
77
|
+
await rename(from, output);
|
78
|
+
await rm(tmp, { force: true, recursive: true }).catch(debug);
|
79
|
+
await touch(output);
|
81
80
|
debug('done extracting');
|
82
81
|
}
|
83
82
|
catch (error) {
|
84
|
-
await
|
83
|
+
await rm(tmp, { force: true, recursive: true }).catch(process.emitWarning);
|
85
84
|
throw error;
|
86
85
|
}
|
87
86
|
}
|
88
|
-
|
87
|
+
// This is done so that we can stub it in tests
|
88
|
+
export const Extractor = {
|
89
|
+
extract,
|
90
|
+
};
|
package/{lib → dist}/update.d.ts
RENAMED
@@ -1,27 +1,26 @@
|
|
1
1
|
import { Config } from '@oclif/core';
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
type VersionIndex = Record<string, string>;
|
10
|
-
}
|
2
|
+
type Options = {
|
3
|
+
autoUpdate: boolean;
|
4
|
+
channel?: string | undefined;
|
5
|
+
force?: boolean;
|
6
|
+
version?: string | undefined;
|
7
|
+
};
|
8
|
+
type VersionIndex = Record<string, string>;
|
11
9
|
export declare class Updater {
|
12
10
|
private config;
|
13
|
-
private readonly clientRoot;
|
14
11
|
private readonly clientBin;
|
12
|
+
private readonly clientRoot;
|
15
13
|
constructor(config: Config);
|
16
|
-
|
14
|
+
fetchVersionIndex(): Promise<VersionIndex>;
|
17
15
|
findLocalVersions(): Promise<string[]>;
|
18
|
-
|
16
|
+
runUpdate(options: Options): Promise<void>;
|
17
|
+
private createBin;
|
19
18
|
private fetchVersionManifest;
|
20
|
-
private update;
|
21
|
-
private updateToExistingVersion;
|
22
19
|
private findLocalVersion;
|
20
|
+
private refreshConfig;
|
23
21
|
private tidy;
|
24
22
|
private touch;
|
25
|
-
private
|
26
|
-
private
|
23
|
+
private update;
|
24
|
+
private updateToExistingVersion;
|
27
25
|
}
|
26
|
+
export {};
|