Skip to content

Commit

Permalink
feat: update install command
Browse files Browse the repository at this point in the history
  • Loading branch information
Diana Sentoso committed Jun 7, 2021
1 parent d6863fb commit 6cecef6
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 31 deletions.
52 changes: 52 additions & 0 deletions src/commands/install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import cli from 'cli-ux'
import * as semver from 'semver'
import * as fs from 'fs-extra'

import UpdateCommand from './update'

export default class InstallCommand extends UpdateCommand {
static args = [{name: 'version', optional: false}]

static flags = {}

async run() {
const {args} = this.parse(InstallCommand)

// 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 versions = fs
.readdirSync(this.clientRoot)
.filter(dirOrFile => dirOrFile !== 'bin' && dirOrFile !== 'current')

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

if (versions.includes(targetVersion)) {
await this.updateToExistingVersion(targetVersion)
this.currentVersion = await this.determineCurrentVersion()
this.updatedVersion = targetVersion
if (channelUpdateRequested) {
await this.setChannel()
}
} else {
cli.action.start(`${this.config.name}: Updating CLI`)
await this.config.runHook('preupdate', {channel: this.channel})
const manifest = await this.fetchManifest()
this.currentVersion = await this.determineCurrentVersion()

this.updatedVersion = (manifest as any).sha ? `${targetVersion}-${(manifest as any).sha}` : targetVersion
this.debug(`Updating to ${this.updatedVersion}`)
const reason = await this.skipUpdate()
if (reason) cli.action.stop(reason || 'done')
else await this.update(manifest, this.channel)
this.debug('tidy')
await this.tidy()
await this.config.runHook('update', {channel: this.channel})

this.debug('done')
cli.action.stop()
}


}
}
171 changes: 140 additions & 31 deletions test/commands/install.test.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,162 @@
import InstallCommand from '../../src/commands/use';
import * as fs from 'fs';
import { mocked } from 'ts-jest/utils';
import { IConfig } from '@oclif/config';
import InstallCommand from '../../src/commands/install'
import * as fs from 'fs-extra'
import {mocked} from 'ts-jest/utils'
import {IConfig} from '@oclif/config'

const mockFs = mocked(fs, true);
jest.mock('fs-extra')
jest.mock('http-call', () => ({
HTTP: {
get: jest.fn(),
},
}))
const mockFs = mocked(fs, true)

class MockedInstallCommand extends InstallCommand {
public fetchManifest = jest.fn();
public channel!: string;

public clientBin!: string;

public clientRoot!: string;

public currentVersion!: string;

public updatedVersion!: string;

public determineCurrentVersion = jest.fn();

public downloadAndExtract = jest.fn();

public reexec = jest.fn();

public updateToExistingVersion = jest.fn();
}

describe.skip('Install Command', () => {
let commandInstance: MockedInstallCommand;
let config: IConfig;
describe('Install Command', () => {
let commandInstance: MockedInstallCommand
let config: IConfig
const {HTTP: http} = require('http-call')
beforeEach(() => {
mockFs.existsSync.mockReturnValue(true);

config = {
name: 'test',
version: '',
channel: '',
version: '1.0.0',
channel: 'stable',
cacheDir: '',
commandIDs: [''],
runHook: jest.fn(),
topics: [],
valid: true,
arch: 'arm64',
platform: 'darwin',
plugins: [],
commands: [],
configDir: '',
pjson: {} as any,
dataDir: '',
root: '',
bin: '',
} as any;
});

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

await commandInstance.run();
});

it.todo(
'when requesting a channel, will fetch manifest to install the latest version',
);
it.todo(
'when requesting a version, will return the explicit version with appropriate URL',
);
it.todo('will handle an invalid version request');
it.todo('will handle an invalid channel request');
});
bin: 'cli',
binPath: 'cli',
pjson: {oclif: {update: {s3: './folder'}}},
scopedEnvVar: jest.fn(),
scopedEnvVarKey: jest.fn(),
scopedEnvVarTrue: jest.fn(),
s3Url: () => null,
s3Key: jest.fn(),
} as any
})

it('when requesting a channel, will fetch manifest to install the latest version', async () => {
mockFs.readdirSync.mockReturnValue([] as any)
commandInstance = new MockedInstallCommand(['next'], config)

http.get.mockResolvedValue({body: {
version: '1.0.0',
baseDir: 'test-cli',
channel: 'next',
gz: 'https://test-cli-oclif.s3.amazonaws.com/test-cli-v1.0.0/test-cli-v1.0.0.tar.gz',
xz: 'https://test-cli-oclif.s3.amazonaws.com/test-cli-v1.0.0/test-cli-v1.0.0.tar.xz',
sha256gz: 'cae9de53d3cb9bfdb43b5fd75b1d9f4655e07cf479a8d86658155ff66d618dbb',
node: {
compatible: '>=10',
recommended: '10.24.0',
},
}})

await commandInstance.run()

expect(commandInstance.downloadAndExtract).toBeCalled()
expect(commandInstance.updatedVersion).toBe('next')
})

it('when requesting a version, will return the explicit version with appropriate URL', async () => {
mockFs.readdirSync.mockReturnValue([] as any)
commandInstance = new MockedInstallCommand(['2.2.1'], config)

http.get.mockResolvedValue({body: {
version: '2.2.1',
baseDir: 'test-cli',
channel: 'next',
gz: 'https://test-cli-oclif.s3.amazonaws.com/test-cli-v2.2.1/test-cli-v2.2.1.tar.gz',
xz: 'https://test-cli-oclif.s3.amazonaws.com/test-cli-v2.2.1/test-cli-v2.2.1.tar.xz',
sha256gz: 'cae9de53d3cb9bfdb43b5fd75b1d9f4655e07cf479a8d86658155ff66d618dbb',
node: {
compatible: '>=10',
recommended: '10.24.0',
},
}})

await commandInstance.run()

expect(commandInstance.downloadAndExtract).toBeCalled()
expect(commandInstance.updatedVersion).toBe('2.2.1')
})

it('when requesting a version already available locally, will call updateToExistingVersion', async () => {
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
'1.0.0-next.3',
'1.0.1',
'1.0.0-alpha.0',
] as any)
commandInstance = new MockedInstallCommand(['1.0.0-next.3'], config)
await commandInstance.run()

expect(commandInstance.updateToExistingVersion).toBeCalled()
expect(commandInstance.downloadAndExtract).not.toBeCalled()
expect(commandInstance.updatedVersion).toBe('1.0.0-next.3')
})

it('will handle an invalid version request', async () => {
mockFs.readdirSync.mockReturnValue([] as any)
commandInstance = new MockedInstallCommand(['2.2.1'], {...config, scopedEnvVarTrue: () => false})
http.get.mockRejectedValue(new Error('unable to find version'))

let err

try {
await commandInstance.run()
} catch (error) {
err = error
}

expect(err.message).toBe('unable to find version')
})

it('will handle an invalid channel request', async () => {
mockFs.readdirSync.mockReturnValue([] as any)
commandInstance = new MockedInstallCommand(['2.2.1'], {...config, scopedEnvVarTrue: () => true})

http.get.mockRejectedValue({statusCode: 403})

let err

try {
await commandInstance.run()
} catch (error) {
err = error
}

expect(commandInstance.downloadAndExtract).not.toBeCalled()
expect(err.message).toBe('HTTP 403: Invalid channel undefined')
})
})

0 comments on commit 6cecef6

Please sign in to comment.