Skip to content

Commit

Permalink
feat: update use command
Browse files Browse the repository at this point in the history
  • Loading branch information
Diana Sentoso committed May 31, 2021
1 parent a527b31 commit 598af92
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 71 deletions.
125 changes: 72 additions & 53 deletions src/commands/use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,86 @@ import * as semver from 'semver'
import UpdateCommand from './update'

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 channelUpdateRequested = ['alpha', 'beta', 'next', 'stable'].some(c => args.version === c)
this.channel = channelUpdateRequested ? args.version : await this.determineChannel()
// Check if this command is trying to update the channel. TODO: make this dynamic
const channelUpdateRequested = ['alpha', 'beta', 'next', 'stable'].some(
c => args.version === c,
)
this.channel = channelUpdateRequested ?
args.version :
await this.determineChannel()

const targetVersion = semver.clean(args.version) || args.version
const targetVersion = semver.clean(args.version) || args.version

// 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}`)
}
// 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}`)
}

await this.ensureClientDir()
this.debug(`Looking for locally installed versions at ${this.clientRoot}`)

// 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.')

if (versions.includes(targetVersion)) {
this.updateToExistingVersion(targetVersion)
} else if (channelUpdateRequested) {
// Begin prompt
cli.action.start(`${this.config.name}: Updating CLI`)

// Run pre-update hook
await this.config.runHook('preupdate', {channel: this.channel})
const manifest = await this.fetchManifest()

// Determine version differences
this.currentVersion = await this.determineCurrentVersion()
this.updatedVersion = (manifest as any).sha ? `${manifest.version}-${(manifest as any).sha}` : manifest.version

// Check if this update should be skipped
const reason = await this.skipUpdate()
if (reason) {
cli.action.stop(reason || 'done')
} else {
// Update using the new channel specification
await this.update(manifest, this.channel)
}

this.debug('tidy')
await this.tidy()
await this.config.runHook('update', {channel: this.channel})
await this.ensureClientDir()
this.debug(`Looking for locally installed versions at ${this.clientRoot}`)

// 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 => version.includes(targetVersion))
.sort((a, b) => semver.compare(b, a))

if (versions.includes(targetVersion) || matchingLocalVersions.length > 0) {
const target = versions.includes(targetVersion) ? targetVersion : matchingLocalVersions[0]
this.updateToExistingVersion(target)
this.currentVersion = await this.determineCurrentVersion()
this.updatedVersion = target
} else if (channelUpdateRequested) {
// Begin prompt
cli.action.start(`${this.config.name}: Updating CLI`)

// Run pre-update hook
await this.config.runHook('preupdate', {channel: this.channel})
const manifest = await this.fetchManifest()

// Determine version differences
this.currentVersion = await this.determineCurrentVersion()
this.updatedVersion = (manifest as any).sha ?
`${manifest.version}-${(manifest as any).sha}` :
manifest.version

// Check if this update should be skipped
const reason = await this.skipUpdate()
if (reason) {
cli.action.stop(reason || 'done')
} else {
throw new Error(`Requested version could not be found. Please try running \`${this.config.bin} install ${targetVersion}\``)
// Update using the new channel specification
await this.update(manifest, this.channel)
}

this.log()
this.log(`Updating to an already installed version will not update the channel. If autoupdate is enabled, the CLI will eventually be updated back to ${this.channel}.`)

this.debug('done')
cli.action.stop()
this.debug('tidy')
await this.tidy()
await this.config.runHook('update', {channel: this.channel})
} else {
throw new Error(
`Requested version could not be found. Please try running \`${this.config.bin} install ${targetVersion}\``,
)
}

this.log()
this.log(
`Updating to an already installed version will not update the channel. If autoupdate is enabled, the CLI will eventually be updated back to ${this.channel}.`,
)

this.debug('done')
cli.action.stop()
}
}
54 changes: 36 additions & 18 deletions test/commands/use.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import UseCommand from '../../src/commands/use'
import * as fs from 'fs-extra'
import {mocked} from 'ts-jest/utils'
import {IConfig} from '@oclif/config'
import {IManifest} from '@oclif/dev-cli'

jest.mock('fs-extra')
const mockFs = mocked(fs, true)
class MockedUseCommand extends UseCommand {
public currentVersion!: string;

public updatedVersion!: string;

public fetchManifest = jest.fn()
public fetchManifest = jest.fn();

public downloadAndExtract = jest.fn()
public downloadAndExtract = jest.fn();
}

describe('Use Command', () => {
Expand All @@ -36,46 +39,56 @@ describe('Use Command', () => {
dataDir: '',
pjson: {} as any,
root: '',
bin: '',
bin: 'cli',
scopedEnvVar: jest.fn(),
} as any
})

it.skip('will run an update', async () => {
commandInstance = new MockedUseCommand([], config)
await commandInstance.run()
})

it('when provided a channel, uses the latest version available locally', async () => {
mockFs.readdirSync.mockReturnValue(['1.0.0-next.2', '1.0.0-next.3', '1.0.1', '1.0.0-alpha.0'] as any)
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
'1.0.0-next.3',
'1.0.1',
'1.0.0-alpha.0',
] as any)

// oclif-example use next
commandInstance = new MockedUseCommand(['next'], config)

commandInstance.fetchManifest.mockResolvedValue({
sha: 'test',
})
commandInstance.fetchManifest.mockResolvedValue({})

await commandInstance.run()

expect(commandInstance.updatedVersion).toBe('1.0.0-next.3')
})

it('when provided a version, will directly switch to it locally', async () => {
mockFs.readdirSync.mockReturnValue(['1.0.0-next.2', '1.0.0-next.3', '1.0.1', '1.0.0-alpha.0'] as any)
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
'1.0.0-next.3',
'1.0.1',
'1.0.0-alpha.0',
] as any)

// oclif-example use next
commandInstance = new MockedUseCommand(['1.0.0-alpha.0'], config)

commandInstance.fetchManifest.mockResolvedValue({})
commandInstance.fetchManifest.mockResolvedValue({
channel: 'alpha',
} as IManifest)

await commandInstance.run()

expect(commandInstance.updatedVersion).toBe('1.0.0-alpha.0')
})

it('will print a warning when the requested static version is not available locally', async () => {
mockFs.readdirSync.mockReturnValue(['1.0.0-next.2', '1.0.0-next.3', '1.0.1', '1.0.0-alpha.0'] as any)
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
'1.0.0-next.3',
'1.0.1',
'1.0.0-alpha.0',
] as any)

// oclif-example use next
commandInstance = new MockedUseCommand(['1.0.0-alpha.3'], config)
Expand All @@ -90,11 +103,16 @@ describe('Use Command', () => {
err = error
}

expect(err).toBeDefined()
expect(err.message).toBe('Requested version could not be found. Please try running `cli install 1.0.0-alpha.3`')
})

it('will print a warning when the requested channel is not available locally', async () => {
mockFs.readdirSync.mockReturnValue(['1.0.0-next.2', '1.0.0-next.3', '1.0.1', '1.0.0-alpha.0'] as any)
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
'1.0.0-next.3',
'1.0.1',
'1.0.0-alpha.0',
] as any)

// oclif-example use next
commandInstance = new MockedUseCommand(['blah'], config)
Expand All @@ -109,6 +127,6 @@ describe('Use Command', () => {
err = error
}

expect(err).toBeDefined()
expect(err.message).toBe('Requested version could not be found. Please try running `cli install blah`')
})
})

0 comments on commit 598af92

Please sign in to comment.