forked from oclif/plugin-update
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
142ef3e
commit 2382a3e
Showing
10 changed files
with
671 additions
and
694 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,19 @@ | ||
const prettierConfig = require('./.prettierrc.js') | ||
module.exports = { | ||
root: true, | ||
parser: "@typescript-eslint/parser", | ||
plugins: ["@typescript-eslint", "prettier"], | ||
extends: [ | ||
"oclif", | ||
"oclif-typescript", | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"prettier", | ||
], | ||
rules: { | ||
"prettier/prettier": "error", | ||
"no-useless-constructor": "off", | ||
"@typescript-eslint/no-useless-constructor": "error", | ||
"@typescript-eslint/no-var-requires": "off", | ||
}, | ||
}; | ||
root: true, | ||
parser: '@typescript-eslint/parser', | ||
plugins: ['@typescript-eslint', 'prettier'], | ||
extends: [ | ||
'oclif', | ||
'oclif-typescript', | ||
'eslint:recommended', | ||
'plugin:@typescript-eslint/recommended', | ||
'prettier', | ||
], | ||
rules: { | ||
'prettier/prettier': ['error', prettierConfig], | ||
'no-useless-constructor': 'off', | ||
'@typescript-eslint/no-useless-constructor': 'error', | ||
'@typescript-eslint/no-var-requires': 'off', | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
module.exports = { | ||
trailingComma: "es5", | ||
tabWidth: 4, | ||
semi: false, | ||
singleQuote: true, | ||
}; | ||
trailingComma: 'all', | ||
arrowParens: 'always', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,104 +1,96 @@ | ||
import cli from 'cli-ux' | ||
import * as fs from 'fs-extra' | ||
import * as semver from 'semver' | ||
import cli from 'cli-ux'; | ||
import * as fs from 'fs-extra'; | ||
import * as semver from 'semver'; | ||
|
||
import UpdateCommand from './update' | ||
import UpdateCommand from './update'; | ||
|
||
const SEMVER_REGEX = | ||
/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?/ | ||
/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?/; | ||
|
||
export default class UseCommand extends UpdateCommand { | ||
static args = [{ name: 'version', optional: false }] | ||
static args = [{ name: 'version', optional: false }]; | ||
|
||
static flags = {} | ||
static flags = {}; | ||
|
||
async run() { | ||
const { args } = this.parse(UseCommand) | ||
async run() { | ||
const { args } = this.parse(UseCommand); | ||
|
||
// Check if this command is trying to update the channel. TODO: make this dynamic | ||
const prereleaseChannels = ['alpha', 'beta', 'next'] | ||
const isExplicitVersion = SEMVER_REGEX.test(args.version || '') | ||
const channelUpdateRequested = ['stable', ...prereleaseChannels].some( | ||
(c) => args.version === c | ||
) | ||
// Check if this command is trying to update the channel. TODO: make this dynamic | ||
const prereleaseChannels = ['alpha', 'beta', 'next']; | ||
const isExplicitVersion = SEMVER_REGEX.test(args.version || ''); | ||
const channelUpdateRequested = ['stable', ...prereleaseChannels].some( | ||
(c) => args.version === c, | ||
); | ||
|
||
if (!isExplicitVersion && !channelUpdateRequested) { | ||
throw new Error( | ||
`Invalid argument provided: ${args.version}. Please specify either a valid channel (alpha, beta, next, stable) or an explicit version (ex. 2.68.13)` | ||
) | ||
} | ||
|
||
this.channel = channelUpdateRequested | ||
? args.version | ||
: await this.determineChannel() | ||
|
||
const targetVersion = semver.clean(args.version || '') || args.version | ||
if (!isExplicitVersion && !channelUpdateRequested) { | ||
throw new Error( | ||
`Invalid argument provided: ${args.version}. Please specify either a valid channel (alpha, beta, next, stable) or an explicit version (ex. 2.68.13)`, | ||
); | ||
} | ||
|
||
// Determine if the version is from a different channel and update to account for it (ex. cli-example update 3.0.0-next.22 should update the channel to next as well.) | ||
const versionParts = targetVersion?.split('-') || ['', ''] | ||
if (versionParts && versionParts[1]) { | ||
this.channel = versionParts[1].substr( | ||
0, | ||
versionParts[1].indexOf('.') | ||
) | ||
this.debug(`Flag overriden target channel: ${this.channel}`) | ||
} | ||
this.channel = channelUpdateRequested | ||
? args.version | ||
: await this.determineChannel(); | ||
|
||
await this.ensureClientDir() | ||
this.debug( | ||
`Looking for locally installed versions at ${this.clientRoot}` | ||
) | ||
const targetVersion = semver.clean(args.version || '') || args.version; | ||
|
||
// Do not show known non-local version folder names, bin and current. | ||
const versions = fs | ||
.readdirSync(this.clientRoot) | ||
.filter( | ||
(dirOrFile) => dirOrFile !== 'bin' && dirOrFile !== 'current' | ||
) | ||
if (versions.length === 0) | ||
throw new Error('No locally installed versions found.') | ||
const matchingLocalVersions = versions | ||
.filter((version) => { | ||
// - If the version contains 'partial', ignore it | ||
if (version.includes('partial')) { | ||
return false | ||
} | ||
// - If we request stable, only provide standard versions... | ||
if (this.channel === 'stable') { | ||
return !prereleaseChannels.some((c) => version.includes(c)) | ||
} | ||
// - ... otherwise check if the version is contained | ||
return version.includes(targetVersion) | ||
}) | ||
.sort((a, b) => semver.compare(b, a)) | ||
// Determine if the version is from a different channel and update to account for it (ex. cli-example update 3.0.0-next.22 should update the channel to next as well.) | ||
const versionParts = targetVersion?.split('-') || ['', '']; | ||
if (versionParts && versionParts[1]) { | ||
this.channel = versionParts[1].substr(0, versionParts[1].indexOf('.')); | ||
this.debug(`Flag overriden target channel: ${this.channel}`); | ||
} | ||
|
||
if ( | ||
args.version && | ||
(versions.includes(targetVersion) || | ||
matchingLocalVersions.length > 0) | ||
) { | ||
const target = versions.includes(targetVersion) | ||
? targetVersion | ||
: matchingLocalVersions[0] | ||
await this.updateToExistingVersion(target) | ||
this.currentVersion = await this.determineCurrentVersion() | ||
this.updatedVersion = target | ||
if (channelUpdateRequested) { | ||
await this.setChannel() | ||
} | ||
this.log(`Success! You are now on ${target}!`) | ||
} else { | ||
const localVersionsMsg = `Locally installed versions available: \n${versions | ||
.map((version) => `\t${version}`) | ||
.join('\n')}\n` | ||
await this.ensureClientDir(); | ||
this.debug(`Looking for locally installed versions at ${this.clientRoot}`); | ||
|
||
throw new Error( | ||
`Requested version could not be found locally. ${localVersionsMsg}` | ||
) | ||
// Do not show known non-local version folder names, bin and current. | ||
const versions = fs | ||
.readdirSync(this.clientRoot) | ||
.filter((dirOrFile) => dirOrFile !== 'bin' && dirOrFile !== 'current'); | ||
if (versions.length === 0) | ||
throw new Error('No locally installed versions found.'); | ||
const matchingLocalVersions = versions | ||
.filter((version) => { | ||
// - If the version contains 'partial', ignore it | ||
if (version.includes('partial')) { | ||
return false; | ||
} | ||
// - If we request stable, only provide standard versions... | ||
if (this.channel === 'stable') { | ||
return !prereleaseChannels.some((c) => version.includes(c)); | ||
} | ||
// - ... otherwise check if the version is contained | ||
return version.includes(targetVersion); | ||
}) | ||
.sort((a, b) => semver.compare(b, a)); | ||
|
||
this.log() | ||
this.debug('done') | ||
cli.action.stop() | ||
if ( | ||
args.version && | ||
(versions.includes(targetVersion) || matchingLocalVersions.length > 0) | ||
) { | ||
const target = versions.includes(targetVersion) | ||
? targetVersion | ||
: matchingLocalVersions[0]; | ||
await this.updateToExistingVersion(target); | ||
this.currentVersion = await this.determineCurrentVersion(); | ||
this.updatedVersion = target; | ||
if (channelUpdateRequested) { | ||
await this.setChannel(); | ||
} | ||
this.log(`Success! You are now on ${target}!`); | ||
} else { | ||
const localVersionsMsg = `Locally installed versions available: \n${versions | ||
.map((version) => `\t${version}`) | ||
.join('\n')}\n`; | ||
|
||
throw new Error( | ||
`Requested version could not be found locally. ${localVersionsMsg}`, | ||
); | ||
} | ||
|
||
this.log(); | ||
this.debug('done'); | ||
cli.action.stop(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,79 @@ | ||
import * as Config from '@oclif/config' | ||
import cli from 'cli-ux' | ||
import * as spawn from 'cross-spawn' | ||
import * as fs from 'fs-extra' | ||
import * as path from 'path' | ||
import debugUtil from 'debug' | ||
import { touch } from '../util' | ||
import * as Config from '@oclif/config'; | ||
import cli from 'cli-ux'; | ||
import * as spawn from 'cross-spawn'; | ||
import * as fs from 'fs-extra'; | ||
import * as path from 'path'; | ||
import debugUtil from 'debug'; | ||
import { touch } from '../util'; | ||
|
||
const debug = debugUtil('cli:updater') | ||
const debug = debugUtil('cli:updater'); | ||
|
||
function timestamp(msg: string): string { | ||
return `[${new Date().toISOString()}] ${msg}` | ||
return `[${new Date().toISOString()}] ${msg}`; | ||
} | ||
|
||
async function mtime(f: string) { | ||
const { mtime } = await fs.stat(f) | ||
return mtime | ||
const { mtime } = await fs.stat(f); | ||
return mtime; | ||
} | ||
|
||
export const init: Config.Hook<'init'> = async function (opts) { | ||
if (opts.id === 'update') return | ||
if (opts.config.scopedEnvVarTrue('DISABLE_AUTOUPDATE')) return | ||
const binPath = this.config.binPath || this.config.bin | ||
const lastrunfile = path.join(this.config.cacheDir, 'lastrun') | ||
const autoupdatefile = path.join(this.config.cacheDir, 'autoupdate') | ||
const autoupdatelogfile = path.join(this.config.cacheDir, 'autoupdate.log') | ||
const clientRoot = | ||
this.config.scopedEnvVar('OCLIF_CLIENT_HOME') || | ||
path.join(this.config.dataDir, 'client') | ||
if (opts.id === 'update') return; | ||
if (opts.config.scopedEnvVarTrue('DISABLE_AUTOUPDATE')) return; | ||
const binPath = this.config.binPath || this.config.bin; | ||
const lastrunfile = path.join(this.config.cacheDir, 'lastrun'); | ||
const autoupdatefile = path.join(this.config.cacheDir, 'autoupdate'); | ||
const autoupdatelogfile = path.join(this.config.cacheDir, 'autoupdate.log'); | ||
const clientRoot = | ||
this.config.scopedEnvVar('OCLIF_CLIENT_HOME') || | ||
path.join(this.config.dataDir, 'client'); | ||
|
||
const autoupdateEnv = { | ||
...process.env, | ||
[this.config.scopedEnvVarKey('TIMESTAMPS')]: '1', | ||
[this.config.scopedEnvVarKey('SKIP_ANALYTICS')]: '1', | ||
} | ||
const autoupdateEnv = { | ||
...process.env, | ||
[this.config.scopedEnvVarKey('TIMESTAMPS')]: '1', | ||
[this.config.scopedEnvVarKey('SKIP_ANALYTICS')]: '1', | ||
}; | ||
|
||
async function autoupdateNeeded(): Promise<boolean> { | ||
try { | ||
const m = await mtime(autoupdatefile) | ||
let days = 1 | ||
if (opts.config.channel === 'stable') days = 14 | ||
m.setHours(m.getHours() + days * 24) | ||
return m < new Date() | ||
} catch (error) { | ||
if (error.code !== 'ENOENT') cli.error(error.stack) | ||
if ((global as any).testing) return false | ||
debug('autoupdate ENOENT') | ||
return true | ||
} | ||
async function autoupdateNeeded(): Promise<boolean> { | ||
try { | ||
const m = await mtime(autoupdatefile); | ||
let days = 1; | ||
if (opts.config.channel === 'stable') days = 14; | ||
m.setHours(m.getHours() + days * 24); | ||
return m < new Date(); | ||
} catch (error) { | ||
if (error.code !== 'ENOENT') cli.error(error.stack); | ||
if ((global as any).testing) return false; | ||
debug('autoupdate ENOENT'); | ||
return true; | ||
} | ||
} | ||
|
||
await touch(lastrunfile) | ||
const clientDir = path.join(clientRoot, this.config.version) | ||
if (await fs.pathExists(clientDir)) await touch(clientDir) | ||
if (!(await autoupdateNeeded())) return | ||
await touch(lastrunfile); | ||
const clientDir = path.join(clientRoot, this.config.version); | ||
if (await fs.pathExists(clientDir)) await touch(clientDir); | ||
if (!(await autoupdateNeeded())) return; | ||
|
||
debug('autoupdate running') | ||
await fs.outputFile(autoupdatefile, '') | ||
debug('autoupdate running'); | ||
await fs.outputFile(autoupdatefile, ''); | ||
|
||
debug(`spawning autoupdate on ${binPath}`) | ||
debug(`spawning autoupdate on ${binPath}`); | ||
|
||
const fd = await fs.open(autoupdatelogfile, 'a') | ||
fs.write( | ||
fd, | ||
timestamp( | ||
`starting \`${binPath} update --autoupdate\` from ${process.argv | ||
.slice(1, 3) | ||
.join(' ')}\n` | ||
) | ||
) | ||
const fd = await fs.open(autoupdatelogfile, 'a'); | ||
fs.write( | ||
fd, | ||
timestamp( | ||
`starting \`${binPath} update --autoupdate\` from ${process.argv | ||
.slice(1, 3) | ||
.join(' ')}\n`, | ||
), | ||
); | ||
|
||
spawn(binPath, ['update', '--autoupdate'], { | ||
detached: !this.config.windows, | ||
stdio: ['ignore', fd, fd], | ||
env: autoupdateEnv, | ||
}) | ||
.on('error', (e: Error) => process.emitWarning(e)) | ||
.unref() | ||
} | ||
spawn(binPath, ['update', '--autoupdate'], { | ||
detached: !this.config.windows, | ||
stdio: ['ignore', fd, fd], | ||
env: autoupdateEnv, | ||
}) | ||
.on('error', (e: Error) => process.emitWarning(e)) | ||
.unref(); | ||
}; |
Oops, something went wrong.