Migrate 'envs' and 'env rename' commands to oclif

Change-type: patch
Signed-off-by: Paulo Castro <paulo@balena.io>
This commit is contained in:
Paulo Castro 2019-09-06 14:43:53 +01:00
parent b3bef9e556
commit c07b28e694
14 changed files with 350 additions and 201 deletions

View File

@ -49,8 +49,9 @@ const capitanoDoc = {
{
title: 'Environment Variables',
files: [
'build/actions/environment-variables.js',
'build/actions-oclif/envs.js',
'build/actions-oclif/env/add.js',
'build/actions-oclif/env/rename.js',
'build/actions-oclif/env/rm.js',
],
},

View File

@ -572,16 +572,16 @@ confirm non interactively
## envs
Use this command to list the environment variables of an application
or device.
List the environment or config variables of an application or device,
as selected by the respective command-line options.
The --config option is used to list "config" variables that configure
balena features.
The --config option is used to list "configuration variables" that
control balena features.
Service-specific variables are not currently supported. The following
examples list variables that apply to all services in an app or device.
Example:
Examples:
$ balena envs --application MyApp
$ balena envs --application MyApp --config
@ -589,18 +589,22 @@ Example:
### Options
#### --application, -a, --app &#60;application&#62;
#### -a, --application APPLICATION
application name
#### --device, -d &#60;device&#62;
device uuid
#### --config, -c, -v, --verbose
#### -c, --config
show config variables
#### -d, --device DEVICE
device UUID
#### -v, --verbose
produce verbose output
## env rm ID
Remove an environment variable from an application or device, as selected
@ -624,7 +628,7 @@ Examples:
#### ID
environment variable id
environment variable numeric database ID
### Options
@ -678,12 +682,12 @@ device UUID
suppress warning messages
## env rename &#60;id&#62; &#60;value&#62;
## env rename ID VALUE
Use this command to change the value of an application or device
environment variable.
The --device option selects a device instead of an application.
Change the value of an environment variable for an application or device,
as selected by the '--device' option. The variable is identified by its
database ID, rather than its name. The 'balena envs' command can be used
to list the variable's ID.
Service-specific variables are not currently supported. The following
examples modify variables that apply to all services in an app or device.
@ -693,11 +697,21 @@ Examples:
$ balena env rename 376 emacs
$ balena env rename 376 emacs --device
### Arguments
#### ID
environment variable numeric database ID
#### VALUE
variable value; if omitted, use value from CLI's environment
### Options
#### --device, -d
#### -d, --device
device
select a device variable instead of an application variable
# Tags

96
lib/actions-oclif/env/rename.ts vendored Normal file
View File

@ -0,0 +1,96 @@
/**
* @license
* Copyright 2016-2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Command, flags } from '@oclif/command';
import { stripIndent } from 'common-tags';
import * as cf from '../../utils/common-flags';
import { CommandHelp } from '../../utils/oclif-utils';
type IArg<T> = import('@oclif/parser').args.IArg<T>;
interface FlagsDef {
device: boolean;
help: void;
}
interface ArgsDef {
id: number;
value: string;
}
export default class EnvRenameCmd extends Command {
public static description = stripIndent`
Change the value of an environment variable for an app or device.
Change the value of an environment variable for an application or device,
as selected by the '--device' option. The variable is identified by its
database ID, rather than its name. The 'balena envs' command can be used
to list the variable's ID.
Service-specific variables are not currently supported. The following
examples modify variables that apply to all services in an app or device.
`;
public static examples = [
'$ balena env rename 376 emacs',
'$ balena env rename 376 emacs --device',
];
public static args: Array<IArg<any>> = [
{
name: 'id',
required: true,
description: 'environment variable numeric database ID',
parse: input => parseInt(input, 10),
},
{
name: 'value',
required: true,
description:
"variable value; if omitted, use value from CLI's environment",
},
];
// hardcoded 'env add' to avoid oclif's 'env:add' topic syntax
public static usage =
'env rename ' + new CommandHelp({ args: EnvRenameCmd.args }).defaultUsage();
public static flags: flags.Input<FlagsDef> = {
device: flags.boolean({
char: 'd',
description:
'select a device variable instead of an application variable',
}),
help: cf.help,
};
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
EnvRenameCmd,
);
const balena = (await import('balena-sdk')).fromSharedOptions();
await balena.pine.patch({
resource: options.device
? 'device_environment_variable'
: 'application_environment_variable',
id: params.id,
body: {
value: params.value,
},
});
}
}

95
lib/actions-oclif/envs.ts Normal file
View File

@ -0,0 +1,95 @@
/**
* @license
* Copyright 2016-2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Command, flags } from '@oclif/command';
import { ApplicationVariable, DeviceVariable } from 'balena-sdk';
import { stripIndent } from 'common-tags';
import * as _ from 'lodash';
import * as cf from '../utils/common-flags';
import { CommandHelp } from '../utils/oclif-utils';
interface FlagsDef {
application?: string;
config: boolean;
device?: string;
help: void;
verbose: boolean;
}
export default class EnvsCmd extends Command {
public static description = stripIndent`
List the environment or config variables of an app or device.
List the environment or config variables of an application or device,
as selected by the respective command-line options.
The --config option is used to list "configuration variables" that
control balena features.
Service-specific variables are not currently supported. The following
examples list variables that apply to all services in an app or device.
`;
public static examples = [
'$ balena envs --application MyApp',
'$ balena envs --application MyApp --config',
'$ balena envs --device 7cf02a6',
];
public static usage = (
'envs ' + new CommandHelp({ args: EnvsCmd.args }).defaultUsage()
).trim();
public static flags: flags.Input<FlagsDef> = {
application: _.assign({ exclusive: ['device'] }, cf.application),
config: flags.boolean({
char: 'c',
description: 'show config variables',
}),
device: _.assign({ exclusive: ['application'] }, cf.device),
help: cf.help,
verbose: cf.verbose,
};
public async run() {
const { flags: options } = this.parse<FlagsDef, {}>(EnvsCmd);
const balena = (await import('balena-sdk')).fromSharedOptions();
const visuals = await import('resin-cli-visuals');
const { exitWithExpectedError } = await import('../utils/patterns');
const cmd = this;
let environmentVariables: ApplicationVariable[] | DeviceVariable[];
if (options.application) {
environmentVariables = await balena.models.application[
options.config ? 'configVar' : 'envVar'
].getAllByApplication(options.application);
} else if (options.device) {
environmentVariables = await balena.models.device[
options.config ? 'configVar' : 'envVar'
].getAllByDevice(options.device);
} else {
return exitWithExpectedError('You must specify an application or device');
}
if (_.isEmpty(environmentVariables)) {
return exitWithExpectedError('No environment variables found');
}
cmd.log(
visuals.table.horizontal(environmentVariables, ['id', 'name', 'value']),
);
}
}

View File

@ -1,154 +0,0 @@
/*
Copyright 2016-2017 Balena
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ApplicationVariable, DeviceVariable } from 'balena-sdk';
import * as Bluebird from 'bluebird';
import { CommandDefinition } from 'capitano';
import { stripIndent } from 'common-tags';
import { normalizeUuidProp } from '../utils/normalization';
import * as commandOptions from './command-options';
export const list: CommandDefinition<
{},
{
application?: string;
device?: string;
config: boolean;
}
> = {
signature: 'envs',
description: 'list all environment variables',
help: stripIndent`
Use this command to list the environment variables of an application
or device.
The --config option is used to list "config" variables that configure
balena features.
Service-specific variables are not currently supported. The following
examples list variables that apply to all services in an app or device.
Example:
$ balena envs --application MyApp
$ balena envs --application MyApp --config
$ balena envs --device 7cf02a6
`,
options: [
commandOptions.optionalApplication,
commandOptions.optionalDevice,
{
signature: 'config',
description: 'show config variables',
boolean: true,
alias: ['c', 'v', 'verbose'],
},
],
permission: 'user',
async action(_params, options, done) {
normalizeUuidProp(options, 'device');
const _ = await import('lodash');
const balena = (await import('balena-sdk')).fromSharedOptions();
const visuals = await import('resin-cli-visuals');
const { exitWithExpectedError } = await import('../utils/patterns');
return Bluebird.try(function(): Bluebird<
DeviceVariable[] | ApplicationVariable[]
> {
if (options.application) {
return balena.models.application[
options.config ? 'configVar' : 'envVar'
].getAllByApplication(options.application);
} else if (options.device) {
return balena.models.device[
options.config ? 'configVar' : 'envVar'
].getAllByDevice(options.device);
} else {
return exitWithExpectedError(
'You must specify an application or device',
);
}
})
.tap(function(environmentVariables) {
if (_.isEmpty(environmentVariables)) {
exitWithExpectedError('No environment variables found');
}
console.log(
visuals.table.horizontal(environmentVariables, [
'id',
'name',
'value',
]),
);
})
.nodeify(done);
},
};
export const rename: CommandDefinition<
{
id: number;
value: string;
},
{
device: boolean;
}
> = {
signature: 'env rename <id> <value>',
description: 'rename an environment variable',
help: stripIndent`
Use this command to change the value of an application or device
environment variable.
The --device option selects a device instead of an application.
Service-specific variables are not currently supported. The following
examples modify variables that apply to all services in an app or device.
Examples:
$ balena env rename 376 emacs
$ balena env rename 376 emacs --device
`,
permission: 'user',
options: [commandOptions.booleanDevice],
async action(params, options, done) {
const balena = (await import('balena-sdk')).fromSharedOptions();
return Bluebird.try(function() {
if (options.device) {
return balena.pine.patch({
resource: 'device_environment_variable',
id: params.id,
body: {
value: params.value,
},
});
} else {
return balena.pine.patch({
resource: 'application_environment_variable',
id: params.id,
body: {
value: params.value,
},
});
}
}).nodeify(done);
},
};

View File

@ -19,7 +19,6 @@ module.exports =
app: require('./app')
auth: require('./auth')
device: require('./device')
env: require('./environment-variables')
tags: require('./tags')
keys: require('./keys')
logs: require('./logs')

View File

@ -83,10 +83,6 @@ capitano.command(actions.keys.add)
capitano.command(actions.keys.info)
capitano.command(actions.keys.remove)
# ---------- Env Module ----------
capitano.command(actions.env.list)
capitano.command(actions.env.rename)
# ---------- Tags Module ----------
capitano.command(actions.tags.list)
capitano.command(actions.tags.set)

View File

@ -127,7 +127,13 @@ function checkDeletedCommand(argvSlice: string[]): void {
}
}
export const convertedCommands = ['env:add', 'env:rm', 'version'];
export const convertedCommands = [
'envs',
'env:add',
'env:rename',
'env:rm',
'version',
];
/**
* Determine whether the CLI command has been converted from Capitano to oclif.

26
npm-shrinkwrap.json generated
View File

@ -7394,6 +7394,15 @@
}
}
},
"intercept-stdout": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/intercept-stdout/-/intercept-stdout-0.1.2.tgz",
"integrity": "sha1-Emq/H65sUJpCipjGGmMVWQQq6f0=",
"dev": true,
"requires": {
"lodash.toarray": "^3.0.0"
}
},
"interpret": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
@ -8056,6 +8065,12 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
"lodash._arraycopy": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz",
"integrity": "sha1-due3wfH7klRzdIeKVi7Qaj5Q9uE=",
"dev": true
},
"lodash._basecopy": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
@ -8170,6 +8185,17 @@
"lodash._reinterpolate": "~3.0.0"
}
},
"lodash.toarray": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-3.0.2.tgz",
"integrity": "sha1-KyBPD6T1HChcbwDIHRzqWiMEEXk=",
"dev": true,
"requires": {
"lodash._arraycopy": "^3.0.0",
"lodash._basevalues": "^3.0.0",
"lodash.keys": "^3.0.0"
}
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",

View File

@ -125,6 +125,7 @@
"gulp-coffee": "^2.2.0",
"gulp-inline-source": "^2.1.0",
"gulp-shell": "^0.5.2",
"intercept-stdout": "^0.1.2",
"mocha": "^6.2.0",
"nock": "^10.0.6",
"parse-link-header": "~1.0.1",

34
tests/commands/env/rename.spec.ts vendored Normal file
View File

@ -0,0 +1,34 @@
/**
* @license
* Copyright 2016-2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as chai from 'chai';
import { balenaAPIMock, runCommand } from '../../helpers';
describe('balena env rename', function() {
it('should successfully rename an environment variable', async () => {
const mock = balenaAPIMock();
mock.patch(/device_environment_variable\(376\)/).reply(200, 'OK');
const { out, err } = await runCommand('env rename 376 emacs --device');
chai.expect(out.join('')).to.equal('');
chai.expect(err.join('')).to.equal('');
// @ts-ignore
mock.remove();
});
});

View File

@ -59,9 +59,9 @@ Additional commands:
device shutdown <uuid> shutdown a device
devices supported list all supported devices
env add name [value] add an environment or config variable to an application or device
env rename <id> <value> rename an environment variable
env rename id value change the value of an environment variable for an app or device
env rm id remove an environment variable from an application or device
envs list all environment variables
envs list the environment or config variables of an app or device
key <id> list a single ssh key
key add <name> [path] add a SSH key to balena
key rm <id> remove a ssh key

View File

@ -1,28 +1,42 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import intercept = require('intercept-stdout');
import * as nock from 'nock';
import * as path from 'path';
import * as balenaCLI from '../build/app';
export const runCommand = async (cmd: string) => {
const preArgs = [process.argv[0], path.join(process.cwd(), 'bin', 'balena')];
const oldStdOut = process.stdout.write;
const oldStdErr = process.stderr.write;
const err: string[] = [];
const out: string[] = [];
// @ts-ignore
process.stdout.write = (log: string) => {
const stdoutHook = (log: string | Buffer) => {
// Skip over debug messages
if (!log.startsWith('[debug]')) {
if (typeof log === 'string' && !log.startsWith('[debug]')) {
out.push(log);
}
oldStdOut(log);
};
// @ts-ignore
process.stderr.write = (log: string) => {
const stderrHook = (log: string | Buffer) => {
// Skip over debug messages
if (
typeof log === 'string' &&
!log.startsWith('[debug]') &&
// TODO stop this warning message from appearing when running
// sdk.setSharedOptions multiple times in the same process
@ -30,26 +44,19 @@ export const runCommand = async (cmd: string) => {
) {
err.push(log);
}
oldStdErr(log);
};
const unhookIntercept = intercept(stdoutHook, stderrHook);
try {
await balenaCLI.run(preArgs.concat(cmd.split(' ')), {
noFlush: true,
});
process.stdout.write = oldStdOut;
process.stderr.write = oldStdErr;
return {
err,
out,
};
} catch (err) {
process.stdout.write = oldStdOut;
process.stderr.write = oldStdErr;
throw err;
} finally {
unhookIntercept();
}
};

28
typings/intercept-stdout/index.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
/**
* @license
* Copyright 2019 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'intercept-stdout' {
type hookFunction = (txt: string) => string | void;
type unhookFunction = () => void;
function intercept(
stdoutIntercept: hookFunction,
stderrIntercept?: hookFunction,
): unhookFunction;
export = intercept;
}