@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 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 {};