@oclif/plugin-update 4.0.1-qa.0 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
- * [Usage](#usage)
12
- * [Commands](#commands)
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
- * [`oclif-example update [CHANNEL]`](#oclif-example-update-channel)
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> | -i] [--force]
30
+ $ oclif-example update [CHANNEL] [-a] [--force] [-i | -v <value>]
28
31
 
29
32
  FLAGS
30
- -a, --available Install a specific version.
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/v3.0.0/src/commands/update.ts)_
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
@@ -0,0 +1,6 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ declare function extract(stream: NodeJS.ReadableStream, basename: string, output: string, sha?: string): Promise<void>;
3
+ export declare const Extractor: {
4
+ extract: typeof extract;
5
+ };
6
+ export {};
@@ -1,43 +1,43 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extract = void 0;
4
- const tslib_1 = require("tslib");
5
- const fs = tslib_1.__importStar(require("node:fs/promises"));
6
- const node_fs_1 = require("node:fs");
7
- const path = tslib_1.__importStar(require("path"));
8
- const util_1 = require("./util");
9
- const debug = require('debug')('oclif-update');
10
- const ignore = (_, header) => {
11
- switch (header.type) {
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
- case 'symlink':
17
+ }
18
+ case 'symlink': {
18
19
  return true;
19
- default:
20
- throw new Error(header.type);
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 ((0, node_fs_1.existsSync)(tmp))
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 = tar.extract(tmp, { ignore });
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 ((0, node_fs_1.existsSync)(output)) {
64
+ if (existsSync(output)) {
65
65
  try {
66
66
  const tmp = getTmp();
67
- const { move } = await Promise.resolve().then(() => tslib_1.__importStar(require('fs-extra')));
68
- await move(output, tmp);
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 fs.rm(tmp, { recursive: true, force: true }).catch(debug);
72
+ await rm(tmp, { force: true, recursive: true }).catch(debug);
74
73
  }
75
74
  }
76
- const from = path.join(tmp, basename);
75
+ const from = join(tmp, basename);
77
76
  debug('moving %s to %s', from, output);
78
- await fs.rename(from, output);
79
- await fs.rm(tmp, { recursive: true, force: true }).catch(debug);
80
- await (0, util_1.touch)(output);
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 fs.rm(tmp, { recursive: true, force: true }).catch(process.emitWarning);
83
+ await rm(tmp, { force: true, recursive: true }).catch(process.emitWarning);
85
84
  throw error;
86
85
  }
87
86
  }
88
- exports.extract = extract;
87
+ // This is done so that we can stub it in tests
88
+ export const Extractor = {
89
+ extract,
90
+ };
@@ -1,27 +1,26 @@
1
1
  import { Config } from '@oclif/core';
2
- export declare namespace Updater {
3
- type Options = {
4
- autoUpdate: boolean;
5
- channel?: string | undefined;
6
- version?: string | undefined;
7
- force?: boolean;
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
- runUpdate(options: Updater.Options): Promise<void>;
14
+ fetchVersionIndex(): Promise<VersionIndex>;
17
15
  findLocalVersions(): Promise<string[]>;
18
- fetchVersionIndex(): Promise<Updater.VersionIndex>;
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 refreshConfig;
26
- private createBin;
23
+ private update;
24
+ private updateToExistingVersion;
27
25
  }
26
+ export {};