Merge pull request #2685 from bbugh/add-releases-json

Add --json option to `release` and `releases`
This commit is contained in:
Otávio Jacobi 2023-10-20 10:44:52 -03:00 committed by GitHub
commit 87ba364f89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 24 deletions

View File

@ -1172,9 +1172,16 @@ created public/open fleet, or with fleets from other balena accounts that you
may be invited to join under any role. For this reason, fleet names are
especially discouraged in scripts (e.g. CI environments).
The --json option is recommended when scripting the output of this command,
because field names are less likely to change in JSON format and because it
better represents data types like arrays, empty strings and null values.
The 'jq' utility may be helpful for querying JSON fields in shell scripts
(https://stedolan.github.io/jq/manual/).
Examples:
$ balena releases myorg/myfleet
$ balena releases myorg/myfleet --json
### Arguments
@ -1184,14 +1191,23 @@ fleet name or slug (preferred)
### Options
#### -j, --json
produce JSON output instead of tabular output
## release <commitOrId>
The --json option is recommended when scripting the output of this command,
because field names are less likely to change in JSON format and because it
better represents data types like arrays, empty strings and null values.
The 'jq' utility may be helpful for querying JSON fields in shell scripts
(https://stedolan.github.io/jq/manual/).
Examples:
$ balena release a777f7345fe3d655c1c981aa642e5555
$ balena release 1234567
$ balena release d3f3151f5ad25ca6b070aa4d08296aca --json
### Arguments
@ -1201,6 +1217,10 @@ the commit or ID of the release to get information
### Options
#### -j, --json
produce JSON output instead of tabular output
#### -c, --composition
Return the release composition

View File

@ -15,30 +15,37 @@
* limitations under the License.
*/
import { Flags, Args } from '@oclif/core';
import { Flags, Args, type Interfaces } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
import type * as BalenaSdk from 'balena-sdk';
import jsyaml = require('js-yaml');
import { tryAsInteger } from '../../utils/validation';
import { jsonInfo } from '../../utils/messages';
export const commitOrIdArg = Args.custom({
parse: async (commitOrId: string) => tryAsInteger(commitOrId),
});
type FlagsDef = Interfaces.InferredFlags<typeof ReleaseCmd.flags>;
export default class ReleaseCmd extends Command {
public static description = stripIndent`
Get info for a release.
${jsonInfo.split('\n').join('\n\t\t')}
`;
public static examples = [
'$ balena release a777f7345fe3d655c1c981aa642e5555',
'$ balena release 1234567',
'$ balena release d3f3151f5ad25ca6b070aa4d08296aca --json',
];
public static usage = 'release <commitOrId>';
public static flags = {
json: cf.json,
help: cf.help,
composition: Flags.boolean({
default: false,
@ -63,7 +70,7 @@ export default class ReleaseCmd extends Command {
if (options.composition) {
await this.showComposition(params.commitOrId, balena);
} else {
await this.showReleaseInfo(params.commitOrId, balena);
await this.showReleaseInfo(params.commitOrId, balena, options);
}
}
@ -81,6 +88,7 @@ export default class ReleaseCmd extends Command {
async showReleaseInfo(
commitOrId: string | number,
balena: BalenaSdk.BalenaSDK,
options: FlagsDef,
) {
const fields: Array<keyof BalenaSdk.Release> = [
'id',
@ -95,7 +103,7 @@ export default class ReleaseCmd extends Command {
];
const release = await balena.models.release.get(commitOrId, {
$select: fields,
...(!options.json && { $select: fields }),
$expand: {
release_tag: {
$select: ['tag_key', 'value'],
@ -103,6 +111,9 @@ export default class ReleaseCmd extends Command {
},
});
if (options.json) {
console.log(JSON.stringify(release, null, 4));
} else {
const tagStr = release
.release_tag!.map((t) => `${t.tag_key}=${t.value}`)
.join('\n');
@ -117,3 +128,4 @@ export default class ReleaseCmd extends Command {
console.log(getVisuals().table.vertical(values, [...fields, 'tags']));
}
}
}

View File

@ -21,6 +21,7 @@ import * as cf from '../utils/common-flags';
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
import { applicationNameNote } from '../utils/messages';
import type * as BalenaSdk from 'balena-sdk';
import { jsonInfo } from '../utils/messages';
export default class ReleasesCmd extends Command {
public static description = stripIndent`
@ -29,12 +30,18 @@ export default class ReleasesCmd extends Command {
List all releases of the given fleet.
${applicationNameNote.split('\n').join('\n\t\t')}
${jsonInfo.split('\n').join('\n\t\t')}
`;
public static examples = ['$ balena releases myorg/myfleet'];
public static examples = [
'$ balena releases myorg/myfleet',
'$ balena releases myorg/myfleet --json',
];
public static usage = 'releases <fleet>';
public static flags = {
json: cf.json,
help: cf.help,
};
@ -48,7 +55,7 @@ export default class ReleasesCmd extends Command {
public static authenticated = true;
public async run() {
const { args: params } = await this.parse(ReleasesCmd);
const { args: params, flags: options } = await this.parse(ReleasesCmd);
const fields: Array<keyof BalenaSdk.Release> = [
'id',
@ -64,9 +71,20 @@ export default class ReleasesCmd extends Command {
const releases = await balena.models.release.getAllByApplication(
await getFleetSlug(balena, params.fleet),
{ $select: fields },
options.json
? {
$expand: {
release_tag: {
$select: ['tag_key', 'value'],
},
},
}
: { $select: fields },
);
if (options.json) {
console.log(JSON.stringify(releases, null, 4));
} else {
const _ = await import('lodash');
console.log(
getVisuals().table.horizontal(
@ -76,3 +94,4 @@ export default class ReleasesCmd extends Command {
);
}
}
}

View File

@ -56,6 +56,16 @@ describe('balena release', function () {
expect(lines[5]).to.be.equal('main:');
});
it('should print version information as JSON with the the -j/--json flag', async () => {
api.expectGetRelease();
const { err, out } = await runCommand('release 27fda508c --json');
expect(err).to.be.empty;
const json = JSON.parse(out.join(''));
expect(json.commit).to.equal('90247b54de4fa7a0a3cbc85e73c68039');
expect(json.release_tag[0].tag_key).to.equal('testtag1');
expect(json.composition.services.main.network_mode).to.equal('host');
});
it('should list releases', async () => {
api.expectGetRelease();
api.expectGetApplication();
@ -65,4 +75,17 @@ describe('balena release', function () {
expect(lines[1]).to.contain('142334');
expect(lines[1]).to.contain('90247b54de4fa7a0a3cbc85e73c68039');
});
it('should list releases as JSON with the -j/--json flag', async () => {
api.expectGetRelease();
api.expectGetApplication();
const { err, out } = await runCommand('releases someapp --json');
expect(err).to.be.empty;
const json = JSON.parse(out.join(''));
expect(json[0].commit).to.equal('90247b54de4fa7a0a3cbc85e73c68039');
expect(json[0].contains__image[0].image[0].start_timestamp).to.equal(
'2020-01-04T01:13:08.583Z',
);
expect(json[0].__metadata.uri).to.equal('/resin/release(@id)?@id=142334');
});
});