mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-18 13:26:24 +00:00
Rename applications to fleets (stage 1). See: https://git.io/JRuZr
- Add fleet(s) commands and -f, --fleet flags as aliases to the app(s) commands and -a, --app, --application flags. - Conditionally rename column/row headers and JSON object properties from 'application' to 'fleet', with some variations. - Print warning messages regarding the renaming, provided that stderr is attached to an interactive terminal. Change-type: minor Resolves: #2302
This commit is contained in:
parent
c3406603db
commit
64a44e7a5f
10
.github/ISSUE_TEMPLATE.md
vendored
10
.github/ISSUE_TEMPLATE.md
vendored
@ -32,11 +32,11 @@ Please describe what actually happened instead:
|
||||
Examples:
|
||||
|
||||
```
|
||||
balena push myApp
|
||||
balena push myFleet
|
||||
balena push 192.168.0.12
|
||||
balena deploy myApp
|
||||
balena deploy myApp --build
|
||||
balena build . -a myApp
|
||||
balena deploy myFleet
|
||||
balena deploy myFleet --build
|
||||
balena build . -f myFleet
|
||||
balena build . -A armv7hf -d raspberrypi3
|
||||
```
|
||||
|
||||
@ -48,7 +48,7 @@ additional information. The `--logs` option reveals additional information for t
|
||||
|
||||
```
|
||||
balena build . --logs
|
||||
balena deploy myApp --build --logs
|
||||
balena deploy myFleet --build --logs
|
||||
```
|
||||
|
||||
# Steps to Reproduce the Problem
|
||||
|
@ -1,5 +1,4 @@
|
||||
module.exports = {
|
||||
spec: 'tests/commands/app/create.spec.ts',
|
||||
reporter: 'spec',
|
||||
require: 'ts-node/register/transpile-only',
|
||||
file: './tests/config-tests',
|
||||
|
@ -57,7 +57,7 @@ guide](https://docs.docker.com/compose/completion/) for system setup instruction
|
||||
## Logging in
|
||||
|
||||
Several CLI commands require access to your balenaCloud account, for example in order to push a
|
||||
new release to your application. Those commands require creating a CLI login session by running:
|
||||
new release to your fleet. Those commands require creating a CLI login session by running:
|
||||
|
||||
```sh
|
||||
$ balena login
|
||||
|
@ -34,15 +34,22 @@ const capitanoDoc = {
|
||||
files: ['build/commands/api-key/generate.js'],
|
||||
},
|
||||
{
|
||||
title: 'Application',
|
||||
title: 'Fleet',
|
||||
files: [
|
||||
'build/commands/apps.js',
|
||||
'build/commands/fleets.js',
|
||||
'build/commands/app/index.js',
|
||||
'build/commands/fleet/index.js',
|
||||
'build/commands/app/create.js',
|
||||
'build/commands/fleet/create.js',
|
||||
'build/commands/app/purge.js',
|
||||
'build/commands/fleet/purge.js',
|
||||
'build/commands/app/rename.js',
|
||||
'build/commands/fleet/rename.js',
|
||||
'build/commands/app/restart.js',
|
||||
'build/commands/fleet/restart.js',
|
||||
'build/commands/app/rm.js',
|
||||
'build/commands/fleet/rm.js',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -58,7 +58,7 @@ class FakeHelpCommand {
|
||||
|
||||
examples = [
|
||||
'$ balena help',
|
||||
'$ balena help apps',
|
||||
'$ balena help login',
|
||||
'$ balena help os download',
|
||||
];
|
||||
|
||||
|
@ -8,7 +8,7 @@ _balena() {
|
||||
local context state line curcontext="$curcontext"
|
||||
|
||||
# Valid top-level completions
|
||||
main_commands=( apps build deploy envs join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env internal key key local os tag util )
|
||||
main_commands=( apps build deploy envs fleets join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env fleet fleet internal key key local os tag util )
|
||||
# Sub-completions
|
||||
api_key_cmds=( generate )
|
||||
app_cmds=( create purge rename restart rm )
|
||||
@ -16,6 +16,7 @@ _balena() {
|
||||
device_cmds=( deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown )
|
||||
devices_cmds=( supported )
|
||||
env_cmds=( add rename rm )
|
||||
fleet_cmds=( create purge rename restart rm )
|
||||
internal_cmds=( osinit )
|
||||
key_cmds=( add rm )
|
||||
local_cmds=( configure flash )
|
||||
@ -57,6 +58,9 @@ _balena_sec_cmds() {
|
||||
"env")
|
||||
_describe -t env_cmds 'env_cmd' env_cmds "$@" && ret=0
|
||||
;;
|
||||
"fleet")
|
||||
_describe -t fleet_cmds 'fleet_cmd' fleet_cmds "$@" && ret=0
|
||||
;;
|
||||
"internal")
|
||||
_describe -t internal_cmds 'internal_cmd' internal_cmds "$@" && ret=0
|
||||
;;
|
||||
|
@ -7,7 +7,7 @@ _balena_complete()
|
||||
local cur prev
|
||||
|
||||
# Valid top-level completions
|
||||
main_commands="apps build deploy envs join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env internal key key local os tag util"
|
||||
main_commands="apps build deploy envs fleets join keys leave login logout logs note orgs preload push scan settings ssh support tags tunnel version whoami api-key app app config device device devices env fleet fleet internal key key local os tag util"
|
||||
# Sub-completions
|
||||
api_key_cmds="generate"
|
||||
app_cmds="create purge rename restart rm"
|
||||
@ -15,6 +15,7 @@ _balena_complete()
|
||||
device_cmds="deactivate identify init local-mode move os-update public-url purge reboot register rename restart rm shutdown"
|
||||
devices_cmds="supported"
|
||||
env_cmds="add rename rm"
|
||||
fleet_cmds="create purge rename restart rm"
|
||||
internal_cmds="osinit"
|
||||
key_cmds="add rm"
|
||||
local_cmds="configure flash"
|
||||
@ -51,6 +52,9 @@ _balena_complete()
|
||||
env)
|
||||
COMPREPLY=( $(compgen -W "$env_cmds" -- $cur) )
|
||||
;;
|
||||
fleet)
|
||||
COMPREPLY=( $(compgen -W "$fleet_cmds" -- $cur) )
|
||||
;;
|
||||
internal)
|
||||
COMPREPLY=( $(compgen -W "$internal_cmds" -- $cur) )
|
||||
;;
|
||||
|
@ -7,7 +7,7 @@ It requires collecting some preliminary information _once_.
|
||||
The final command to provision the device looks like this:
|
||||
|
||||
```bash
|
||||
balena device init --app APP_ID --os-version OS_VERSION --drive DRIVE --config CONFIG_FILE --yes
|
||||
balena device init --fleet FLEET_ID --os-version OS_VERSION --drive DRIVE --config CONFIG_FILE --yes
|
||||
|
||||
```
|
||||
|
||||
@ -24,7 +24,7 @@ But before you can run it you need to collect the parameters and build the confi
|
||||
```
|
||||
and find the _slug_ for your target device type, like _raspberrypi3_.
|
||||
|
||||
1. `APP_ID`. Create an application (`balena app create APP_NAME --type DEVICE_TYPE`) or find an existing one (`balena apps`) and notice its ID.
|
||||
1. `FLEET_ID`. Create a fleet (`balena fleet create FLEET_NAME --type DEVICE_TYPE`) or find an existing one (`balena fleets`) and notice its ID.
|
||||
|
||||
1. `OS_VERSION`. Run
|
||||
```bash
|
||||
|
1228
doc/cli.markdown
1228
doc/cli.markdown
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,11 +16,14 @@
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { Output as ParserOutput } from '@oclif/parser';
|
||||
import type { Application } from 'balena-sdk';
|
||||
|
||||
import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import type { Application } from 'balena-sdk';
|
||||
import { appToFleetCmdMsg, warnify } from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
organization?: string;
|
||||
@ -32,18 +35,18 @@ interface ArgsDef {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default class AppCreateCmd extends Command {
|
||||
export class FleetCreateCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Create an application.
|
||||
Create a fleet.
|
||||
|
||||
Create a new balena application.
|
||||
Create a new balena fleet.
|
||||
|
||||
You can specify the organization the application should belong to using
|
||||
You can specify the organization the fleet should belong to using
|
||||
the \`--organization\` option. The organization's handle, not its name,
|
||||
should be provided. Organization handles can be listed with the
|
||||
\`balena orgs\` command.
|
||||
|
||||
The application's default device type is specified with the \`--type\` option.
|
||||
The fleet's default device type is specified with the \`--type\` option.
|
||||
The \`balena devices supported\` command can be used to list the available
|
||||
device types.
|
||||
|
||||
@ -55,41 +58,39 @@ export default class AppCreateCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena app create MyApp',
|
||||
'$ balena app create MyApp --organization mmyorg',
|
||||
'$ balena app create MyApp -o myorg --type raspberry-pi',
|
||||
'$ balena fleet create MyFleet',
|
||||
'$ balena fleet create MyFleet --organization mmyorg',
|
||||
'$ balena fleet create MyFleet -o myorg --type raspberry-pi',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
{
|
||||
name: 'name',
|
||||
description: 'application name',
|
||||
description: 'fleet name',
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'app create <name>';
|
||||
public static usage = 'fleet create <name>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
organization: flags.string({
|
||||
char: 'o',
|
||||
description:
|
||||
'handle of the organization the application should belong to',
|
||||
description: 'handle of the organization the fleet should belong to',
|
||||
}),
|
||||
type: flags.string({
|
||||
char: 't',
|
||||
description:
|
||||
'application device type (Check available types with `balena devices supported`)',
|
||||
'fleet device type (Check available types with `balena devices supported`)',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
AppCreateCmd,
|
||||
);
|
||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
||||
const { args: params, flags: options } =
|
||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetCreateCmd);
|
||||
|
||||
// Ascertain device type
|
||||
const deviceType =
|
||||
@ -112,12 +113,12 @@ export default class AppCreateCmd extends Command {
|
||||
if ((err.message || '').toLowerCase().includes('unique')) {
|
||||
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
|
||||
throw new ExpectedError(
|
||||
`Error: application "${params.name}" already exists in organization "${organization}".`,
|
||||
`Error: fleet "${params.name}" already exists in organization "${organization}".`,
|
||||
);
|
||||
} else if ((err.message || '').toLowerCase().includes('unauthorized')) {
|
||||
// BalenaRequestError: Request error: Unauthorized
|
||||
throw new ExpectedError(
|
||||
`Error: You are not authorized to create applications in organization "${organization}".`,
|
||||
`Error: You are not authorized to create fleets in organization "${organization}".`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -128,8 +129,8 @@ export default class AppCreateCmd extends Command {
|
||||
const { isV13 } = await import('../../utils/version');
|
||||
console.log(
|
||||
isV13()
|
||||
? `Application created: slug "${application.slug}", device type "${deviceType}"`
|
||||
: `Application created: ${application.slug} (${deviceType}, id ${application.id})`,
|
||||
? `Fleet created: slug "${application.slug}", device type "${deviceType}"`
|
||||
: `Fleet created: ${application.slug} (${deviceType}, id ${application.id})`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -150,3 +151,31 @@ export default class AppCreateCmd extends Command {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class AppCreateCmd extends FleetCreateCmd {
|
||||
public static description = stripIndent`
|
||||
DEPRECATED alias for the 'fleet create' command
|
||||
|
||||
${appToFleetCmdMsg
|
||||
.split('\n')
|
||||
.map((l) => `\t\t${l}`)
|
||||
.join('\n')}
|
||||
|
||||
For command usage, see 'balena help fleet create'
|
||||
`;
|
||||
public static examples = [];
|
||||
public static usage = 'app create <name>';
|
||||
public static args = FleetCreateCmd.args;
|
||||
public static flags = FleetCreateCmd.flags;
|
||||
public static authenticated = FleetCreateCmd.authenticated;
|
||||
public static primary = FleetCreateCmd.primary;
|
||||
|
||||
public async run() {
|
||||
// call this.parse() before deprecation message to parse '-h'
|
||||
const parserOutput = this.parse<FlagsDef, ArgsDef>(AppCreateCmd);
|
||||
if (process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetCmdMsg));
|
||||
}
|
||||
await super.run(parserOutput);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,35 +15,44 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { flags } from '@oclif/command';
|
||||
import type { Output as ParserOutput } from '@oclif/parser';
|
||||
import type { Release } from 'balena-sdk';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import type { Release } from 'balena-sdk';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetCmdMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
application: string;
|
||||
fleet: string;
|
||||
}
|
||||
|
||||
export default class AppCmd extends Command {
|
||||
export class FleetCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Display information about a single application.
|
||||
Display information about a single fleet.
|
||||
|
||||
Display detailed information about a single balena application.
|
||||
Display detailed information about a single fleet.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
public static examples = ['$ balena app MyApp', '$ balena app myorg/myapp'];
|
||||
public static examples = [
|
||||
'$ balena fleet MyFleet',
|
||||
'$ balena fleet myorg/myfleet',
|
||||
];
|
||||
|
||||
public static args = [ca.applicationRequired];
|
||||
public static args = [ca.fleetRequired];
|
||||
|
||||
public static usage = 'app <nameOrSlug>';
|
||||
public static usage = 'fleet <fleet>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
@ -52,21 +61,18 @@ export default class AppCmd extends Command {
|
||||
public static authenticated = true;
|
||||
public static primary = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(AppCmd);
|
||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
||||
const { args: params } =
|
||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetCmd);
|
||||
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
|
||||
const application = (await getApplication(
|
||||
getBalenaSdk(),
|
||||
params.application,
|
||||
{
|
||||
$expand: {
|
||||
is_for__device_type: { $select: 'slug' },
|
||||
should_be_running__release: { $select: 'commit' },
|
||||
},
|
||||
const application = (await getApplication(getBalenaSdk(), params.fleet, {
|
||||
$expand: {
|
||||
is_for__device_type: { $select: 'slug' },
|
||||
should_be_running__release: { $select: 'commit' },
|
||||
},
|
||||
)) as ApplicationWithDeviceType & {
|
||||
})) as ApplicationWithDeviceType & {
|
||||
should_be_running__release: [Release?];
|
||||
// For display purposes:
|
||||
device_type: string;
|
||||
@ -88,3 +94,31 @@ export default class AppCmd extends Command {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class AppCmd extends FleetCmd {
|
||||
public static description = stripIndent`
|
||||
DEPRECATED alias for the 'fleet' command
|
||||
|
||||
${appToFleetCmdMsg
|
||||
.split('\n')
|
||||
.map((l) => `\t\t${l}`)
|
||||
.join('\n')}
|
||||
|
||||
For command usage, see 'balena help fleet'
|
||||
`;
|
||||
public static examples = [];
|
||||
public static usage = 'app <fleet>';
|
||||
public static args = FleetCmd.args;
|
||||
public static flags = FleetCmd.flags;
|
||||
public static authenticated = FleetCmd.authenticated;
|
||||
public static primary = FleetCmd.primary;
|
||||
|
||||
public async run() {
|
||||
// call this.parse() before deprecation message to parse '-h'
|
||||
const parserOutput = this.parse<FlagsDef, ArgsDef>(AppCmd);
|
||||
if (process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetCmdMsg));
|
||||
}
|
||||
await super.run(parserOutput);
|
||||
}
|
||||
}
|
||||
|
@ -15,39 +15,45 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { flags } from '@oclif/command';
|
||||
import type { Output as ParserOutput } from '@oclif/parser';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetCmdMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
application: string;
|
||||
fleet: string;
|
||||
}
|
||||
|
||||
export default class AppPurgeCmd extends Command {
|
||||
export class FleetPurgeCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Purge data from an application.
|
||||
Purge data from a fleet.
|
||||
|
||||
Purge data from all devices belonging to an application.
|
||||
This will clear the application's /data directory.
|
||||
Purge data from all devices belonging to a fleet.
|
||||
This will clear the fleet's '/data' directory.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena app purge MyApp',
|
||||
'$ balena app purge myorg/myapp',
|
||||
'$ balena fleet purge MyFleet',
|
||||
'$ balena fleet purge myorg/myfleet',
|
||||
];
|
||||
|
||||
public static args = [ca.applicationRequired];
|
||||
public static args = [ca.fleetRequired];
|
||||
|
||||
public static usage = 'app purge <application>';
|
||||
public static usage = 'fleet purge <fleet>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
@ -55,8 +61,9 @@ export default class AppPurgeCmd extends Command {
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(AppPurgeCmd);
|
||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
||||
const { args: params } =
|
||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetPurgeCmd);
|
||||
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
|
||||
@ -64,7 +71,7 @@ export default class AppPurgeCmd extends Command {
|
||||
|
||||
// balena.models.application.purge only accepts a numeric id
|
||||
// so we must first fetch the app to get it's id,
|
||||
const application = await getApplication(balena, params.application);
|
||||
const application = await getApplication(balena, params.fleet);
|
||||
|
||||
try {
|
||||
await balena.models.application.purge(application.id);
|
||||
@ -78,3 +85,31 @@ export default class AppPurgeCmd extends Command {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class AppPurgeCmd extends FleetPurgeCmd {
|
||||
public static description = stripIndent`
|
||||
DEPRECATED alias for the 'fleet purge' command
|
||||
|
||||
${appToFleetCmdMsg
|
||||
.split('\n')
|
||||
.map((l) => `\t\t${l}`)
|
||||
.join('\n')}
|
||||
|
||||
For command usage, see 'balena help fleet purge'
|
||||
`;
|
||||
public static examples = [];
|
||||
public static usage = 'app purge <fleet>';
|
||||
public static args = FleetPurgeCmd.args;
|
||||
public static flags = FleetPurgeCmd.flags;
|
||||
public static authenticated = FleetPurgeCmd.authenticated;
|
||||
public static primary = FleetPurgeCmd.primary;
|
||||
|
||||
public async run() {
|
||||
// call this.parse() before deprecation message to parse '-h'
|
||||
const parserOutput = this.parse<FlagsDef, ArgsDef>(AppPurgeCmd);
|
||||
if (process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetCmdMsg));
|
||||
}
|
||||
await super.run(parserOutput);
|
||||
}
|
||||
}
|
||||
|
@ -15,28 +15,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { flags } from '@oclif/command';
|
||||
import type { Output as ParserOutput } from '@oclif/parser';
|
||||
import type { ApplicationType } from 'balena-sdk';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import type { ApplicationType } from 'balena-sdk';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetCmdMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
application: string;
|
||||
fleet: string;
|
||||
newName?: string;
|
||||
}
|
||||
|
||||
export default class AppRenameCmd extends Command {
|
||||
export class FleetRenameCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Rename an application.
|
||||
Rename a fleet.
|
||||
|
||||
Rename an application.
|
||||
Rename a fleet.
|
||||
|
||||
Note, if the \`newName\` parameter is omitted, it will be
|
||||
prompted for interactively.
|
||||
@ -45,20 +51,20 @@ export default class AppRenameCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena app rename OldName',
|
||||
'$ balena app rename OldName NewName',
|
||||
'$ balena app rename myorg/oldname NewName',
|
||||
'$ balena fleet rename OldName',
|
||||
'$ balena fleet rename OldName NewName',
|
||||
'$ balena fleet rename myorg/oldname NewName',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
ca.applicationRequired,
|
||||
ca.fleetRequired,
|
||||
{
|
||||
name: 'newName',
|
||||
description: 'the new name for the application',
|
||||
description: 'the new name for the fleet',
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'app rename <application> [newName]';
|
||||
public static usage = 'fleet rename <fleet> [newName]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
@ -66,8 +72,9 @@ export default class AppRenameCmd extends Command {
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(AppRenameCmd);
|
||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
||||
const { args: params } =
|
||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetRenameCmd);
|
||||
|
||||
const { validateApplicationName } = await import('../../utils/validation');
|
||||
const { ExpectedError } = await import('../../errors');
|
||||
@ -76,7 +83,7 @@ export default class AppRenameCmd extends Command {
|
||||
|
||||
// Disambiguate target application (if params.params is a number, it could either be an ID or a numerical name)
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
const application = await getApplication(balena, params.application, {
|
||||
const application = await getApplication(balena, params.fleet, {
|
||||
$expand: {
|
||||
application_type: {
|
||||
$select: ['is_legacy'],
|
||||
@ -86,16 +93,14 @@ export default class AppRenameCmd extends Command {
|
||||
|
||||
// Check app exists
|
||||
if (!application) {
|
||||
throw new ExpectedError(
|
||||
'Error: application ${params.nameOrSlug} not found.',
|
||||
);
|
||||
throw new ExpectedError(`Error: fleet ${params.fleet} not found.`);
|
||||
}
|
||||
|
||||
// Check app supports renaming
|
||||
const appType = (application.application_type as ApplicationType[])?.[0];
|
||||
if (appType.is_legacy) {
|
||||
throw new ExpectedError(
|
||||
`Application ${params.application} is of 'legacy' type, and cannot be renamed.`,
|
||||
`Fleet ${params.fleet} is of 'legacy' type, and cannot be renamed.`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -103,7 +108,7 @@ export default class AppRenameCmd extends Command {
|
||||
const newName =
|
||||
params.newName ||
|
||||
(await getCliForm().ask({
|
||||
message: 'Please enter the new name for this application:',
|
||||
message: 'Please enter the new name for this fleet:',
|
||||
type: 'input',
|
||||
validate: validateApplicationName,
|
||||
})) ||
|
||||
@ -115,9 +120,7 @@ export default class AppRenameCmd extends Command {
|
||||
} catch (e) {
|
||||
// BalenaRequestError: Request error: "organization" and "app_name" must be unique.
|
||||
if ((e.message || '').toLowerCase().includes('unique')) {
|
||||
throw new ExpectedError(
|
||||
`Error: application ${params.application} already exists.`,
|
||||
);
|
||||
throw new ExpectedError(`Error: fleet ${params.fleet} already exists.`);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@ -128,7 +131,7 @@ export default class AppRenameCmd extends Command {
|
||||
);
|
||||
|
||||
// Output result
|
||||
console.log(`Application renamed`);
|
||||
console.log(`Fleet renamed`);
|
||||
console.log('From:');
|
||||
console.log(`\tname: ${application.app_name}`);
|
||||
console.log(`\tslug: ${application.slug}`);
|
||||
@ -137,3 +140,31 @@ export default class AppRenameCmd extends Command {
|
||||
console.log(`\tslug: ${renamedApplication.slug}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default class AppRenameCmd extends FleetRenameCmd {
|
||||
public static description = stripIndent`
|
||||
DEPRECATED alias for the 'fleet rename' command
|
||||
|
||||
${appToFleetCmdMsg
|
||||
.split('\n')
|
||||
.map((l) => `\t\t${l}`)
|
||||
.join('\n')}
|
||||
|
||||
For command usage, see 'balena help fleet rename'
|
||||
`;
|
||||
public static examples = [];
|
||||
public static usage = 'app rename <fleet> [newName]';
|
||||
public static args = FleetRenameCmd.args;
|
||||
public static flags = FleetRenameCmd.flags;
|
||||
public static authenticated = FleetRenameCmd.authenticated;
|
||||
public static primary = FleetRenameCmd.primary;
|
||||
|
||||
public async run() {
|
||||
// call this.parse() before deprecation message to parse '-h'
|
||||
const parserOutput = this.parse<FlagsDef, ArgsDef>(AppRenameCmd);
|
||||
if (process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetCmdMsg));
|
||||
}
|
||||
await super.run(parserOutput);
|
||||
}
|
||||
}
|
||||
|
@ -15,38 +15,44 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { flags } from '@oclif/command';
|
||||
import type { Output as ParserOutput } from '@oclif/parser';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetCmdMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
application: string;
|
||||
fleet: string;
|
||||
}
|
||||
|
||||
export default class AppRestartCmd extends Command {
|
||||
export class FleetRestartCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Restart an application.
|
||||
Restart a fleet.
|
||||
|
||||
Restart all devices belonging to an application.
|
||||
Restart all devices belonging to a fleet.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena app restart MyApp',
|
||||
'$ balena app restart myorg/myapp',
|
||||
'$ balena fleet restart MyFleet',
|
||||
'$ balena fleet restart myorg/myfleet',
|
||||
];
|
||||
|
||||
public static args = [ca.applicationRequired];
|
||||
public static args = [ca.fleetRequired];
|
||||
|
||||
public static usage = 'app restart <application>';
|
||||
public static usage = 'fleet restart <fleet>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
@ -54,16 +60,45 @@ export default class AppRestartCmd extends Command {
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(AppRestartCmd);
|
||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
||||
const { args: params } =
|
||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetRestartCmd);
|
||||
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
|
||||
const application = await getApplication(balena, params.application);
|
||||
const application = await getApplication(balena, params.fleet);
|
||||
|
||||
await balena.models.application.restart(application.id);
|
||||
}
|
||||
}
|
||||
|
||||
export default class AppRestartCmd extends FleetRestartCmd {
|
||||
public static description = stripIndent`
|
||||
DEPRECATED alias for the 'fleet restart' command
|
||||
|
||||
${appToFleetCmdMsg
|
||||
.split('\n')
|
||||
.map((l) => `\t\t${l}`)
|
||||
.join('\n')}
|
||||
|
||||
For command usage, see 'balena help fleet restart'
|
||||
`;
|
||||
public static examples = [];
|
||||
public static usage = 'app restart <fleet>';
|
||||
public static args = FleetRestartCmd.args;
|
||||
public static flags = FleetRestartCmd.flags;
|
||||
public static authenticated = FleetRestartCmd.authenticated;
|
||||
public static primary = FleetRestartCmd.primary;
|
||||
|
||||
public async run() {
|
||||
// call this.parse() before deprecation message to parse '-h'
|
||||
const parserOutput = this.parse<FlagsDef, ArgsDef>(AppRestartCmd);
|
||||
if (process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetCmdMsg));
|
||||
}
|
||||
await super.run(parserOutput);
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { flags } from '@oclif/command';
|
||||
import type { Output as ParserOutput } from '@oclif/parser';
|
||||
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import * as ca from '../../utils/common-args';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetCmdMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
|
||||
interface FlagsDef {
|
||||
yes: boolean;
|
||||
@ -28,14 +34,14 @@ interface FlagsDef {
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
application: string;
|
||||
fleet: string;
|
||||
}
|
||||
|
||||
export default class AppRmCmd extends Command {
|
||||
export class FleetRmCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Remove an application.
|
||||
Remove a fleet.
|
||||
|
||||
Permanently remove a balena application.
|
||||
Permanently remove a fleet.
|
||||
|
||||
The --yes option may be used to avoid interactive confirmation.
|
||||
|
||||
@ -43,14 +49,14 @@ export default class AppRmCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena app rm MyApp',
|
||||
'$ balena app rm MyApp --yes',
|
||||
'$ balena app rm myorg/myapp',
|
||||
'$ balena fleet rm MyFleet',
|
||||
'$ balena fleet rm MyFleet --yes',
|
||||
'$ balena fleet rm myorg/myfleet',
|
||||
];
|
||||
|
||||
public static args = [ca.applicationRequired];
|
||||
public static args = [ca.fleetRequired];
|
||||
|
||||
public static usage = 'app rm <application>';
|
||||
public static usage = 'fleet rm <fleet>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
yes: cf.yes,
|
||||
@ -59,10 +65,9 @@ export default class AppRmCmd extends Command {
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
AppRmCmd,
|
||||
);
|
||||
public async run(parserOutput?: ParserOutput<FlagsDef, ArgsDef>) {
|
||||
const { args: params, flags: options } =
|
||||
parserOutput || this.parse<FlagsDef, ArgsDef>(FleetRmCmd);
|
||||
|
||||
const { confirm } = await import('../../utils/patterns');
|
||||
const { getApplication } = await import('../../utils/sdk');
|
||||
@ -71,13 +76,41 @@ export default class AppRmCmd extends Command {
|
||||
// Confirm
|
||||
await confirm(
|
||||
options.yes ?? false,
|
||||
`Are you sure you want to delete application ${params.application}?`,
|
||||
`Are you sure you want to delete fleet ${params.fleet}?`,
|
||||
);
|
||||
|
||||
// Disambiguate application (if is a number, it could either be an ID or a numerical name)
|
||||
const application = await getApplication(balena, params.application);
|
||||
const application = await getApplication(balena, params.fleet);
|
||||
|
||||
// Remove
|
||||
await balena.models.application.remove(application.id);
|
||||
}
|
||||
}
|
||||
|
||||
export default class AppRmCmd extends FleetRmCmd {
|
||||
public static description = stripIndent`
|
||||
DEPRECATED alias for the 'fleet rm' command
|
||||
|
||||
${appToFleetCmdMsg
|
||||
.split('\n')
|
||||
.map((l) => `\t\t${l}`)
|
||||
.join('\n')}
|
||||
|
||||
For command usage, see 'balena help fleet rm'
|
||||
`;
|
||||
public static examples = [];
|
||||
public static usage = 'app rm <fleet>';
|
||||
public static args = FleetRmCmd.args;
|
||||
public static flags = FleetRmCmd.flags;
|
||||
public static authenticated = FleetRmCmd.authenticated;
|
||||
public static primary = FleetRmCmd.primary;
|
||||
|
||||
public async run() {
|
||||
// call this.parse() before deprecation message to parse '-h'
|
||||
const parserOutput = this.parse<FlagsDef, ArgsDef>(AppRmCmd);
|
||||
if (process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetCmdMsg));
|
||||
}
|
||||
await super.run(parserOutput);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2020 Balena Ltd.
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,9 +16,12 @@
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { Output as ParserOutput } from '@oclif/parser';
|
||||
|
||||
import Command from '../command';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import { appToFleetCmdMsg, warnify } from '../utils/messages';
|
||||
|
||||
interface ExtendedApplication extends ApplicationWithDeviceType {
|
||||
device_count?: number;
|
||||
@ -27,22 +30,22 @@ interface ExtendedApplication extends ApplicationWithDeviceType {
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
verbose: boolean;
|
||||
verbose?: boolean;
|
||||
}
|
||||
|
||||
export default class AppsCmd extends Command {
|
||||
export class FleetsCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
List all applications.
|
||||
List all fleets.
|
||||
|
||||
list all your balena applications.
|
||||
List all your balena fleets.
|
||||
|
||||
For detailed information on a particular application,
|
||||
use \`balena app <application>\` instead.
|
||||
For detailed information on a particular fleet, use
|
||||
\`balena fleet <fleet>\`
|
||||
`;
|
||||
|
||||
public static examples = ['$ balena apps'];
|
||||
public static examples = ['$ balena fleets'];
|
||||
|
||||
public static usage = 'apps';
|
||||
public static usage = 'fleets';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
@ -56,8 +59,10 @@ export default class AppsCmd extends Command {
|
||||
public static authenticated = true;
|
||||
public static primary = true;
|
||||
|
||||
public async run() {
|
||||
this.parse<FlagsDef, {}>(AppsCmd);
|
||||
protected useAppWord = false;
|
||||
|
||||
public async run(_parserOutput?: ParserOutput<FlagsDef, {}>) {
|
||||
_parserOutput ||= this.parse<FlagsDef, {}>(FleetsCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
@ -85,7 +90,7 @@ export default class AppsCmd extends Command {
|
||||
console.log(
|
||||
getVisuals().table.horizontal(applications, [
|
||||
'id',
|
||||
'app_name',
|
||||
this.useAppWord ? 'app_name' : 'app_name => NAME',
|
||||
'slug',
|
||||
'device_type',
|
||||
'online_devices',
|
||||
@ -94,3 +99,36 @@ export default class AppsCmd extends Command {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const appsToFleetsRenameMsg = appToFleetCmdMsg
|
||||
.replace(/'app'/g, "'apps'")
|
||||
.replace(/'fleet'/g, "'fleets'");
|
||||
|
||||
export default class AppsCmd extends FleetsCmd {
|
||||
public static description = stripIndent`
|
||||
DEPRECATED alias for the 'fleets' command
|
||||
|
||||
${appsToFleetsRenameMsg
|
||||
.split('\n')
|
||||
.map((l) => `\t\t${l}`)
|
||||
.join('\n')}
|
||||
|
||||
For command usage, see 'balena help fleets'
|
||||
`;
|
||||
public static examples = [];
|
||||
public static usage = 'apps';
|
||||
public static args = FleetsCmd.args;
|
||||
public static flags = FleetsCmd.flags;
|
||||
public static authenticated = FleetsCmd.authenticated;
|
||||
public static primary = FleetsCmd.primary;
|
||||
|
||||
public async run() {
|
||||
// call this.parse() before deprecation message to parse '-h'
|
||||
const parserOutput = this.parse<FlagsDef, {}>(AppsCmd);
|
||||
if (process.stderr.isTTY) {
|
||||
console.error(warnify(appsToFleetsRenameMsg));
|
||||
}
|
||||
this.useAppWord = true;
|
||||
await super.run(parserOutput);
|
||||
}
|
||||
}
|
||||
|
@ -18,22 +18,27 @@
|
||||
import { flags } from '@oclif/command';
|
||||
import Command from '../command';
|
||||
import { getBalenaSdk } from '../utils/lazy';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import * as compose from '../utils/compose';
|
||||
import type { Application, ApplicationType, BalenaSDK } from 'balena-sdk';
|
||||
import {
|
||||
appToFleetFlagMsg,
|
||||
buildArgDeprecation,
|
||||
dockerignoreHelp,
|
||||
registrySecretsHelp,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import type { ComposeCliFlags, ComposeOpts } from '../utils/compose-types';
|
||||
import { buildProject, composeCliFlags } from '../utils/compose_ts';
|
||||
import type { BuildOpts, DockerCliFlags } from '../utils/docker';
|
||||
import { dockerCliFlags } from '../utils/docker';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
|
||||
arch?: string;
|
||||
deviceType?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
source?: string; // Not part of command profile - source param copied here.
|
||||
help: void;
|
||||
}
|
||||
@ -51,7 +56,7 @@ the provided docker daemon in your development machine or balena device.
|
||||
(See also the \`balena push\` command for the option of building images in the
|
||||
balenaCloud build servers.)
|
||||
|
||||
You must provide either an application or a device-type/architecture pair.
|
||||
You must specify either a fleet, or the device type and architecture.
|
||||
|
||||
This command will look into the given source directory (or the current working
|
||||
directory if one isn't specified) for a docker-compose.yml file, and if found,
|
||||
@ -65,12 +70,12 @@ ${registrySecretsHelp}
|
||||
${dockerignoreHelp}
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena build --application myApp',
|
||||
'$ balena build ./source/ --application myApp',
|
||||
'$ balena build --fleet myFleet',
|
||||
'$ balena build ./source/ --fleet myorg/myfleet',
|
||||
'$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated',
|
||||
'$ balena build --docker /var/run/docker.sock --application myApp # Linux, Mac',
|
||||
'$ balena build --docker //./pipe/docker_engine --application myApp # Windows',
|
||||
'$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -a myApp',
|
||||
'$ balena build --docker /var/run/docker.sock --fleet myFleet # Linux, Mac',
|
||||
'$ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows',
|
||||
'$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem -f myFleet',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
@ -91,10 +96,8 @@ ${dockerignoreHelp}
|
||||
description: 'the type of device this build is for',
|
||||
char: 'd',
|
||||
}),
|
||||
application: flags.string({
|
||||
description: 'name of the target balena application this build is for',
|
||||
char: 'a',
|
||||
}),
|
||||
...(isV13() ? {} : { application: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
...composeCliFlags,
|
||||
...dockerCliFlags,
|
||||
// NOTE: Not supporting -h for help, because of clash with -h in DockerCliFlags
|
||||
@ -109,6 +112,11 @@ ${dockerignoreHelp}
|
||||
BuildCmd,
|
||||
);
|
||||
|
||||
if (options.application && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
|
||||
await Command.checkLoggedInIf(!!options.application);
|
||||
|
||||
(await import('events')).defaultMaxListeners = 1000;
|
||||
@ -160,7 +168,7 @@ ${dockerignoreHelp}
|
||||
) {
|
||||
const { ExpectedError } = await import('../errors');
|
||||
throw new ExpectedError(
|
||||
'You must specify either an application or an arch/deviceType pair to build for',
|
||||
'You must specify either a fleet (-f), or the device type (-d) and architecture (-A)',
|
||||
);
|
||||
}
|
||||
|
||||
@ -240,7 +248,7 @@ ${dockerignoreHelp}
|
||||
!appType.supports_multicontainer
|
||||
) {
|
||||
logger.logWarn(
|
||||
'Target application does not support multiple containers.\n' +
|
||||
'Target fleet does not support multiple containers.\n' +
|
||||
'Continuing with build, but you will not be able to deploy.',
|
||||
);
|
||||
}
|
||||
|
@ -19,13 +19,19 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
import type { PineDeferred } from 'balena-sdk';
|
||||
|
||||
interface FlagsDef {
|
||||
version: string; // OS version
|
||||
application?: string;
|
||||
app?: string; // application alias
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
deviceApiKey?: string;
|
||||
deviceType?: string;
|
||||
@ -43,16 +49,15 @@ export default class ConfigGenerateCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Generate a config.json file.
|
||||
|
||||
Generate a config.json file for a device or application.
|
||||
Generate a config.json file for a device or fleet.
|
||||
|
||||
Calling this command with the exact version number of the targeted image is required.
|
||||
The target balenaOS version must be specified with the --version option.
|
||||
|
||||
This command is interactive by default, but you can do this automatically without interactivity
|
||||
by specifying an option for each question on the command line, if you know the questions
|
||||
that will be asked for the relevant device type.
|
||||
To configure an image for a fleet of mixed device types, use the --fleet option
|
||||
alongside the --deviceType option to specify the target device type.
|
||||
|
||||
In case that you want to configure an image for an application with mixed device types,
|
||||
you can pass the --deviceType argument along with --application to specify the target device type.
|
||||
To avoid interactive questions, specify a command line option for each question that
|
||||
would otherwise be asked.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
@ -62,11 +67,11 @@ export default class ConfigGenerateCmd extends Command {
|
||||
'$ balena config generate --device 7cf02a6 --version 2.12.7 --generate-device-api-key',
|
||||
'$ balena config generate --device 7cf02a6 --version 2.12.7 --device-api-key <existingDeviceKey>',
|
||||
'$ balena config generate --device 7cf02a6 --version 2.12.7 --output config.json',
|
||||
'$ balena config generate --app MyApp --version 2.12.7',
|
||||
'$ balena config generate --app myorg/myapp --version 2.12.7',
|
||||
'$ balena config generate --app MyApp --version 2.12.7 --deviceType fincm3',
|
||||
'$ balena config generate --app MyApp --version 2.12.7 --output config.json',
|
||||
'$ balena config generate --app MyApp --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1',
|
||||
'$ balena config generate --fleet MyFleet --version 2.12.7',
|
||||
'$ balena config generate --fleet myorg/myfleet --version 2.12.7',
|
||||
'$ balena config generate --fleet MyFleet --version 2.12.7 --deviceType fincm3',
|
||||
'$ balena config generate --fleet MyFleet --version 2.12.7 --output config.json',
|
||||
'$ balena config generate --fleet MyFleet --version 2.12.7 --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 15',
|
||||
];
|
||||
|
||||
public static usage = 'config generate';
|
||||
@ -76,20 +81,28 @@ export default class ConfigGenerateCmd extends Command {
|
||||
description: 'a balenaOS version',
|
||||
required: true,
|
||||
}),
|
||||
application: { ...cf.application, exclusive: ['app', 'device'] },
|
||||
app: { ...cf.app, exclusive: ['application', 'device'] },
|
||||
device: flags.string({
|
||||
description: 'device uuid',
|
||||
char: 'd',
|
||||
exclusive: ['application', 'app'],
|
||||
}),
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device'],
|
||||
},
|
||||
app: { ...cf.app, exclusive: ['application', 'fleet', 'device'] },
|
||||
appUpdatePollInterval: flags.string({
|
||||
description: 'DEPRECATED alias for --updatePollInterval',
|
||||
}),
|
||||
}),
|
||||
fleet: { ...cf.fleet, exclusive: ['application', 'app', 'device'] },
|
||||
device: { ...cf.device, exclusive: ['application', 'app', 'fleet'] },
|
||||
deviceApiKey: flags.string({
|
||||
description:
|
||||
'custom device key - note that this is only supported on balenaOS 2.0.3+',
|
||||
char: 'k',
|
||||
}),
|
||||
deviceType: flags.string({
|
||||
description: 'device type slug',
|
||||
description:
|
||||
"device type slug (run 'balena devices supported' for possible values)",
|
||||
}),
|
||||
'generate-device-api-key': flags.boolean({
|
||||
description: 'generate a fresh device key for the device',
|
||||
@ -113,7 +126,7 @@ export default class ConfigGenerateCmd extends Command {
|
||||
}),
|
||||
appUpdatePollInterval: flags.string({
|
||||
description:
|
||||
'how frequently (in minutes) to poll for application updates',
|
||||
'supervisor cloud polling interval in minutes (e.g. for variable updates)',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
@ -143,8 +156,8 @@ export default class ConfigGenerateCmd extends Command {
|
||||
if (!rawDevice.belongs_to__application) {
|
||||
const { ExpectedError } = await import('../../errors');
|
||||
throw new ExpectedError(stripIndent`
|
||||
Device ${options.device} does not appear to belong to an accessible application.
|
||||
Try with a different device, or use '--application' instead of '--device'.`);
|
||||
Device ${options.device} does not appear to belong to an accessible fleet.
|
||||
Try with a different device, or use '--fleet' instead of '--device'.`);
|
||||
}
|
||||
device = rawDevice as DeviceWithDeviceType & {
|
||||
belongs_to__application: PineDeferred;
|
||||
@ -177,7 +190,7 @@ export default class ConfigGenerateCmd extends Command {
|
||||
!helpers.areDeviceTypesCompatible(appDeviceManifest, deviceManifest)
|
||||
) {
|
||||
throw new balena.errors.BalenaInvalidDeviceType(
|
||||
`Device type ${options.deviceType} is incompatible with application ${options.application}`,
|
||||
`Device type ${options.deviceType} is incompatible with fleet ${options.application}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -218,7 +231,7 @@ export default class ConfigGenerateCmd extends Command {
|
||||
}
|
||||
|
||||
protected readonly missingDeviceOrAppMessage = stripIndent`
|
||||
Either a device or an application must be specified.
|
||||
Either a device or a fleet must be specified.
|
||||
|
||||
See the help page for examples:
|
||||
|
||||
@ -226,13 +239,16 @@ export default class ConfigGenerateCmd extends Command {
|
||||
`;
|
||||
|
||||
protected readonly deviceTypeNotAllowedMessage =
|
||||
'The --deviceType option can only be used alongside the --application option';
|
||||
'The --deviceType option can only be used alongside the --fleet option';
|
||||
|
||||
protected async validateOptions(options: FlagsDef) {
|
||||
const { ExpectedError } = await import('../../errors');
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
// Prefer options.application over options.app
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
|
||||
if (options.device == null && options.application == null) {
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
registrySecretsHelp,
|
||||
buildArgDeprecation,
|
||||
} from '../utils/messages';
|
||||
import * as ca from '../utils/common-args';
|
||||
import * as compose from '../utils/compose';
|
||||
import type {
|
||||
BuiltImage,
|
||||
@ -62,18 +63,18 @@ interface FlagsDef extends ComposeCliFlags, DockerCliFlags {
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
appName: string;
|
||||
fleet: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export default class DeployCmd extends Command {
|
||||
public static description = `\
|
||||
Deploy a single image or a multicontainer project to a balena application.
|
||||
Deploy a single image or a multicontainer project to a balena fleet.
|
||||
|
||||
Usage: \`deploy <appName> ([image] | --build [--source build-dir])\`
|
||||
Usage: \`deploy <fleet> ([image] | --build [--source build-dir])\`
|
||||
|
||||
Use this command to deploy an image or a complete multicontainer project to an
|
||||
application, optionally building it first. The source images are searched for
|
||||
Use this command to deploy an image or a complete multicontainer project to a
|
||||
fleet, optionally building it first. The source images are searched for
|
||||
(and optionally built) using the docker daemon in your development machine or
|
||||
balena device. (See also the \`balena push\` command for the option of building
|
||||
the image in the balenaCloud build servers.)
|
||||
@ -88,8 +89,8 @@ If a compose file isn't found, the command will look for a Dockerfile[.template]
|
||||
file (or alternative Dockerfile specified with the \`-f\` option), and if yet
|
||||
that isn't found, it will try to generate one.
|
||||
|
||||
To deploy to an app on which you're a collaborator, use
|
||||
\`balena deploy <appOwnerUsername>/<appName>\`.
|
||||
To deploy to a fleet where you are a collaborator, use fleet slug including the
|
||||
organization: \`balena deploy <organization>/<fleet>\`.
|
||||
|
||||
${registrySecretsHelp}
|
||||
|
||||
@ -97,25 +98,21 @@ ${dockerignoreHelp}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena deploy myApp',
|
||||
'$ balena deploy myApp --build --source myBuildDir/',
|
||||
'$ balena deploy myApp myApp/myImage',
|
||||
'$ balena deploy myApp myApp/myImage --release-tag key1 "" key2 "value2 with spaces"',
|
||||
'$ balena deploy myFleet',
|
||||
'$ balena deploy myorg/myfleet --build --source myBuildDir/',
|
||||
'$ balena deploy myorg/myfleet myRepo/myImage',
|
||||
'$ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces"',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
{
|
||||
name: 'appName',
|
||||
description: 'the name of the application to deploy to',
|
||||
required: true,
|
||||
},
|
||||
ca.fleetRequired,
|
||||
{
|
||||
name: 'image',
|
||||
description: 'the image to deploy',
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'deploy <appName> [image]';
|
||||
public static usage = 'deploy <fleet> [image]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
source: flags.string({
|
||||
@ -160,7 +157,7 @@ ${dockerignoreHelp}
|
||||
const logger = await Command.getLogger();
|
||||
logger.logDebug('Parsing input...');
|
||||
|
||||
const { appName, image } = params;
|
||||
const { fleet, image } = params;
|
||||
|
||||
// Build args are under consideration for removal - warn user
|
||||
if (options.buildArg) {
|
||||
@ -200,7 +197,7 @@ ${dockerignoreHelp}
|
||||
}
|
||||
|
||||
const helpers = await import('../utils/helpers');
|
||||
const app = await helpers.getAppWithArch(appName);
|
||||
const app = await helpers.getAppWithArch(fleet);
|
||||
|
||||
const dockerUtils = await import('../utils/docker');
|
||||
const [docker, buildOpts, composeOpts] = await Promise.all([
|
||||
@ -211,7 +208,7 @@ ${dockerignoreHelp}
|
||||
|
||||
const release = await this.deployProject(docker, logger, composeOpts, {
|
||||
app,
|
||||
appName, // may be prefixed by 'owner/', unlike app.app_name
|
||||
appName: fleet, // may be prefixed by 'owner/', unlike app.app_name
|
||||
image,
|
||||
shouldPerformBuild: !!options.build,
|
||||
shouldUploadLogs: !options.nologupload,
|
||||
@ -254,7 +251,7 @@ ${dockerignoreHelp}
|
||||
const project = await loadProject(logger, composeOpts, opts.image);
|
||||
if (project.descriptors.length > 1 && !appType?.supports_multicontainer) {
|
||||
throw new ExpectedError(
|
||||
'Target application does not support multiple containers. Aborting!',
|
||||
'Target fleet does not support multiple containers. Aborting!',
|
||||
);
|
||||
}
|
||||
|
||||
@ -326,7 +323,7 @@ ${dockerignoreHelp}
|
||||
const { deployLegacy } = require('../utils/deploy-legacy');
|
||||
|
||||
const msg = getChalk().yellow(
|
||||
'Target application requires legacy deploy method.',
|
||||
'Target fleet requires legacy deploy method.',
|
||||
);
|
||||
logger.logWarn(msg);
|
||||
|
||||
|
@ -21,7 +21,10 @@ import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { appToFleetOutputMsg, warnify } from '../../utils/messages';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
import type { Application, Release } from 'balena-sdk';
|
||||
|
||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
@ -43,6 +46,7 @@ interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
v13: boolean;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
@ -70,13 +74,17 @@ export default class DeviceCmd extends Command {
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
v13: cf.v13,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
public static primary = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceCmd);
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceCmd,
|
||||
);
|
||||
const useAppWord = !options.v13 && !isV13();
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
@ -162,6 +170,10 @@ export default class DeviceCmd extends Command {
|
||||
);
|
||||
}
|
||||
|
||||
if (useAppWord && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetOutputMsg));
|
||||
}
|
||||
|
||||
console.log(
|
||||
getVisuals().table.vertical(device, [
|
||||
`$${device.device_name}$`,
|
||||
@ -172,7 +184,7 @@ export default class DeviceCmd extends Command {
|
||||
'ip_address',
|
||||
'public_address',
|
||||
'mac_address',
|
||||
'application_name',
|
||||
useAppWord ? 'application_name' : 'application_name => FLEET',
|
||||
'last_seen',
|
||||
'uuid',
|
||||
'commit',
|
||||
|
@ -19,12 +19,18 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { runCommand } from '../../utils/helpers';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
yes: boolean;
|
||||
advanced: boolean;
|
||||
'os-version'?: string;
|
||||
@ -37,26 +43,30 @@ export default class DeviceInitCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Initialize a device with balenaOS.
|
||||
|
||||
Initialize a device by downloading the OS image of a certain application
|
||||
Initialize a device by downloading the OS image of the specified fleet
|
||||
and writing it to an SD Card.
|
||||
|
||||
Note, if the application option is omitted it will be prompted
|
||||
for interactively.
|
||||
If the --fleet option is omitted, it will be prompted for interactively.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena device init',
|
||||
'$ balena device init --application MyApp',
|
||||
'$ balena device init -a myorg/myapp',
|
||||
'$ balena device init --fleet MyFleet',
|
||||
'$ balena device init -f myorg/myfleet',
|
||||
];
|
||||
|
||||
public static usage = 'device init';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: cf.application,
|
||||
app: cf.app,
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: cf.application,
|
||||
app: cf.app,
|
||||
}),
|
||||
fleet: cf.fleet,
|
||||
yes: cf.yes,
|
||||
advanced: flags.boolean({
|
||||
char: 'v',
|
||||
@ -95,8 +105,11 @@ export default class DeviceInitCmd extends Command {
|
||||
const logger = await Command.getLogger();
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
// Consolidate application options
|
||||
options.application = options.application || options.app;
|
||||
options.application ||= options.app || options.fleet;
|
||||
delete options.app;
|
||||
|
||||
// Get application and
|
||||
|
@ -15,14 +15,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import type { flags } from '@oclif/command';
|
||||
import type { IArg } from '@oclif/parser/lib/args';
|
||||
import type { Application, BalenaSDK } from 'balena-sdk';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
application_name?: string;
|
||||
@ -31,6 +36,7 @@ interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
help: void;
|
||||
}
|
||||
|
||||
@ -40,12 +46,11 @@ interface ArgsDef {
|
||||
|
||||
export default class DeviceMoveCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Move one or more devices to another application.
|
||||
Move one or more devices to another fleet.
|
||||
|
||||
Move one or more devices to another application.
|
||||
Move one or more devices to another fleet.
|
||||
|
||||
Note, if the application option is omitted it will be prompted
|
||||
for interactively.
|
||||
If --fleet is omitted, the fleet will be prompted for interactively.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
@ -53,8 +58,8 @@ export default class DeviceMoveCmd extends Command {
|
||||
public static examples = [
|
||||
'$ balena device move 7cf02a6',
|
||||
'$ balena device move 7cf02a6,dc39e52',
|
||||
'$ balena device move 7cf02a6 --application MyNewApp',
|
||||
'$ balena device move 7cf02a6 -a myorg/mynewapp',
|
||||
'$ balena device move 7cf02a6 --fleet MyNewFleet',
|
||||
'$ balena device move 7cf02a6 -f myorg/mynewfleet',
|
||||
];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
@ -69,8 +74,8 @@ export default class DeviceMoveCmd extends Command {
|
||||
public static usage = 'device move <uuid(s)>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: cf.application,
|
||||
app: cf.app,
|
||||
...(isV13() ? {} : { app: cf.app, application: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
@ -81,14 +86,16 @@ export default class DeviceMoveCmd extends Command {
|
||||
DeviceMoveCmd,
|
||||
);
|
||||
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
const { tryAsInteger } = await import('../../utils/validation');
|
||||
const { expandForAppName } = await import('../../utils/helpers');
|
||||
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
|
||||
// Parse ids string into array of correct types
|
||||
const deviceIds: Array<string | number> = params.uuid
|
||||
.split(',')
|
||||
@ -126,9 +133,7 @@ export default class DeviceMoveCmd extends Command {
|
||||
for (const uuid of deviceIds) {
|
||||
try {
|
||||
await balena.models.device.move(uuid, application.id);
|
||||
console.info(
|
||||
`Device ${uuid} was moved to application ${application.slug}`,
|
||||
);
|
||||
console.info(`Device ${uuid} was moved to fleet ${application.slug}`);
|
||||
} catch (err) {
|
||||
console.info(`${err.message}, uuid: ${uuid}`);
|
||||
process.exitCode = 1;
|
||||
|
@ -31,10 +31,10 @@ interface ArgsDef {
|
||||
|
||||
export default class DevicePurgeCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Purge application data from a device.
|
||||
Purge data from a device.
|
||||
|
||||
Purge application data from a device.
|
||||
This will clear the application's /data directory.
|
||||
Purge data from a device.
|
||||
This will clear the device's "/data" directory.
|
||||
|
||||
Multiple devices may be specified with a comma-separated list
|
||||
of values (no spaces).
|
||||
|
@ -29,27 +29,29 @@ interface FlagsDef {
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
application: string;
|
||||
fleet: string;
|
||||
}
|
||||
|
||||
export default class DeviceRegisterCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Register a device.
|
||||
Register a new device.
|
||||
|
||||
Register a device to an application.
|
||||
Register a new device with a balena fleet.
|
||||
|
||||
If --uuid is not provided, a new UUID will be automatically assigned.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena device register MyApp',
|
||||
'$ balena device register MyApp --uuid <uuid>',
|
||||
'$ balena device register myorg/myapp --uuid <uuid>',
|
||||
'$ balena device register MyFleet',
|
||||
'$ balena device register MyFleet --uuid <uuid>',
|
||||
'$ balena device register myorg/myfleet --uuid <uuid>',
|
||||
];
|
||||
|
||||
public static args: Array<IArg<any>> = [ca.applicationRequired];
|
||||
public static args: Array<IArg<any>> = [ca.fleetRequired];
|
||||
|
||||
public static usage = 'device register <application>';
|
||||
public static usage = 'device register <fleet>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
uuid: flags.string({
|
||||
@ -70,7 +72,7 @@ export default class DeviceRegisterCmd extends Command {
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
const application = await getApplication(balena, params.application);
|
||||
const application = await getApplication(balena, params.fleet);
|
||||
const uuid = options.uuid ?? balena.models.device.generateUniqueKey();
|
||||
|
||||
console.info(`Registering to ${application.app_name}: ${uuid}`);
|
||||
|
@ -20,7 +20,15 @@ import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo, jsonInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
appToFleetOutputMsg,
|
||||
jsonInfo,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
import type { Application } from 'balena-sdk';
|
||||
|
||||
interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
@ -32,17 +40,19 @@ interface ExtendedDevice extends DeviceWithDeviceType {
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
help: void;
|
||||
json: boolean;
|
||||
v13: boolean;
|
||||
}
|
||||
|
||||
export default class DevicesCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
List all devices.
|
||||
|
||||
list all devices that belong to you.
|
||||
List all of your devices.
|
||||
|
||||
You can filter the devices by application by using the \`--application\` option.
|
||||
Devices can be filtered by fleet with the \`--fleet\` option.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
|
||||
@ -50,33 +60,51 @@ export default class DevicesCmd extends Command {
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena devices',
|
||||
'$ balena devices --application MyApp',
|
||||
'$ balena devices --app MyApp',
|
||||
'$ balena devices -a MyApp',
|
||||
'$ balena devices -a myorg/myapp',
|
||||
'$ balena devices --fleet MyFleet',
|
||||
'$ balena devices -f myorg/myfleet',
|
||||
];
|
||||
|
||||
public static usage = 'devices';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: cf.application,
|
||||
app: cf.app,
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'v13'],
|
||||
},
|
||||
app: { ...cf.app, exclusive: ['application', 'fleet', 'v13'] },
|
||||
}),
|
||||
fleet: { ...cf.fleet, exclusive: ['app', 'application'] },
|
||||
json: cf.json,
|
||||
help: cf.help,
|
||||
v13: cf.v13,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
protected useAppWord = false;
|
||||
protected hasWarned = false;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(DevicesCmd);
|
||||
this.useAppWord = !options.fleet && !options.v13 && !isV13();
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
if (
|
||||
(options.application || options.app) &&
|
||||
!options.json &&
|
||||
process.stderr.isTTY
|
||||
) {
|
||||
this.hasWarned = true;
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
// Consolidate application options
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
let devices;
|
||||
|
||||
@ -106,28 +134,32 @@ export default class DevicesCmd extends Command {
|
||||
return device;
|
||||
});
|
||||
|
||||
const jName = this.useAppWord ? 'application_name' : 'fleet_name';
|
||||
const tName = this.useAppWord ? 'APPLICATION NAME' : 'FLEET';
|
||||
const fields = [
|
||||
'id',
|
||||
'uuid',
|
||||
'device_name',
|
||||
'device_type',
|
||||
'application_name',
|
||||
options.json
|
||||
? `application_name => ${jName}`
|
||||
: `application_name => ${tName}`,
|
||||
'status',
|
||||
'is_online',
|
||||
'supervisor_version',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
];
|
||||
const _ = await import('lodash');
|
||||
|
||||
if (options.json) {
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
devices.map((device) => _.pick(device, fields)),
|
||||
null,
|
||||
4,
|
||||
),
|
||||
);
|
||||
const { pickAndRename } = await import('../../utils/helpers');
|
||||
const mapped = devices.map((device) => pickAndRename(device, fields));
|
||||
console.log(JSON.stringify(mapped, null, 4));
|
||||
} else {
|
||||
if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetOutputMsg));
|
||||
}
|
||||
const _ = await import('lodash');
|
||||
console.log(
|
||||
getVisuals().table.horizontal(
|
||||
devices.map((dev) => _.mapValues(dev, (val) => val ?? 'N/a')),
|
||||
|
61
lib/commands/env/add.ts
vendored
61
lib/commands/env/add.ts
vendored
@ -21,10 +21,16 @@ import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string; // device UUID
|
||||
help: void;
|
||||
quiet: boolean;
|
||||
@ -38,18 +44,17 @@ interface ArgsDef {
|
||||
|
||||
export default class EnvAddCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Add env or config variable to application(s), device(s) or service(s).
|
||||
Add env or config variable to fleets, devices or services.
|
||||
|
||||
Add an environment or config variable to one or more applications, devices
|
||||
or services, as selected by the respective command-line options. Either the
|
||||
--application or the --device option must be provided, and either may be be
|
||||
Add an environment or config variable to one or more fleets, devices or
|
||||
services, as selected by the respective command-line options. Either the
|
||||
--fleet or the --device option must be provided, and either may be be
|
||||
used alongside the --service option to define a service-specific variable.
|
||||
(A service is an application container in a "microservices" application.)
|
||||
(A service corresponds to a Docker image/container in a microservices fleet.)
|
||||
When the --service option is used in conjunction with the --device option,
|
||||
the service variable applies to the selected device only. Otherwise, it
|
||||
applies to all devices of the selected application (i.e., the application's
|
||||
fleet). If the --service option is omitted, the variable applies to all
|
||||
services.
|
||||
the service variable applies to the selected device only. Otherwise, it
|
||||
applies to all devices of the selected fleet. If the --service option is
|
||||
omitted, the variable applies to all services.
|
||||
|
||||
If VALUE is omitted, the CLI will attempt to use the value of the environment
|
||||
variable of same name in the CLI process' environment. In this case, a warning
|
||||
@ -61,19 +66,18 @@ export default class EnvAddCmd extends Command {
|
||||
running on devices. They are also stored differently in the balenaCloud API
|
||||
database. Configuration variables cannot be set for specific services,
|
||||
therefore the --service option cannot be used when the variable name starts
|
||||
with a reserved prefix. When defining custom application variables, please
|
||||
avoid the reserved prefixes.
|
||||
with a reserved prefix. When defining custom fleet variables, please avoid
|
||||
these reserved prefixes.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena env add TERM --application MyApp',
|
||||
'$ balena env add EDITOR vim --application MyApp',
|
||||
'$ balena env add EDITOR vim -a myorg/myapp',
|
||||
'$ balena env add EDITOR vim --application MyApp,MyApp2',
|
||||
'$ balena env add EDITOR vim --application MyApp --service MyService',
|
||||
'$ balena env add EDITOR vim --application MyApp,MyApp2 --service MyService,MyService2',
|
||||
'$ balena env add TERM --fleet MyFleet',
|
||||
'$ balena env add EDITOR vim -f myorg/myfleet',
|
||||
'$ balena env add EDITOR vim --fleet MyFleet,MyFleet2',
|
||||
'$ balena env add EDITOR vim --fleet MyFleet --service MyService',
|
||||
'$ balena env add EDITOR vim --fleet MyFleet,MyFleet2 --service MyService,MyService2',
|
||||
'$ balena env add EDITOR vim --device 7cf02a6',
|
||||
'$ balena env add EDITOR vim --device 7cf02a6,d6f1433',
|
||||
'$ balena env add EDITOR vim --device 7cf02a6 --service MyService',
|
||||
@ -97,8 +101,11 @@ export default class EnvAddCmd extends Command {
|
||||
public static usage = 'env add <name> [value]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: { ...cf.application, exclusive: ['device'] },
|
||||
device: { ...cf.device, exclusive: ['application'] },
|
||||
...(isV13()
|
||||
? {}
|
||||
: { application: { ...cf.application, exclusive: ['fleet', 'device'] } }),
|
||||
fleet: { ...cf.fleet, exclusive: ['application', 'device'] },
|
||||
device: { ...cf.device, exclusive: ['application', 'fleet'] },
|
||||
help: cf.help,
|
||||
quiet: cf.quiet,
|
||||
service: cf.service,
|
||||
@ -110,9 +117,13 @@ export default class EnvAddCmd extends Command {
|
||||
);
|
||||
const cmd = this;
|
||||
|
||||
if (options.application && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
if (!options.application && !options.device) {
|
||||
throw new ExpectedError(
|
||||
'Either the --application or the --device option must be specified',
|
||||
'Either the --fleet or the --device option must be specified',
|
||||
);
|
||||
}
|
||||
|
||||
@ -161,7 +172,7 @@ export default class EnvAddCmd extends Command {
|
||||
params.value,
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(`${err.message}, app: ${app}`);
|
||||
console.error(`${err.message}, fleet: ${app}`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
@ -183,7 +194,7 @@ export default class EnvAddCmd extends Command {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add service variables for a device or application.
|
||||
* Add service variables for a device or fleet.
|
||||
*/
|
||||
async function setServiceVars(
|
||||
sdk: BalenaSdk.BalenaSDK,
|
||||
@ -201,7 +212,7 @@ async function setServiceVars(
|
||||
params.value!,
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(`${err.message}, application: ${app}`);
|
||||
console.error(`${err.message}, fleet: ${app}`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
@ -262,7 +273,7 @@ async function getServiceIdForApp(
|
||||
}
|
||||
if (serviceId === undefined) {
|
||||
throw new ExpectedError(
|
||||
`Cannot find service ${serviceName} for application ${appName}`,
|
||||
`Cannot find service ${serviceName} for fleet ${appName}`,
|
||||
);
|
||||
}
|
||||
return serviceId;
|
||||
|
4
lib/commands/env/rename.ts
vendored
4
lib/commands/env/rename.ts
vendored
@ -38,9 +38,9 @@ interface ArgsDef {
|
||||
|
||||
export default class EnvRenameCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Change the value of a config or env var for an app, device or service.
|
||||
Change the value of a config or env var for a fleet, device or service.
|
||||
|
||||
Change the value of a configuration or environment variable for an application,
|
||||
Change the value of a configuration or environment variable for a fleet,
|
||||
device or service, as selected by command-line options.
|
||||
|
||||
${ec.rmRenameHelp.split('\n').join('\n\t\t')}
|
||||
|
4
lib/commands/env/rm.ts
vendored
4
lib/commands/env/rm.ts
vendored
@ -37,9 +37,9 @@ interface ArgsDef {
|
||||
|
||||
export default class EnvRmCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Remove a config or env var from an application, device or service.
|
||||
Remove a config or env var from a fleet, device or service.
|
||||
|
||||
Remove a configuration or environment variable from an application, device
|
||||
Remove a configuration or environment variable from a fleet, device
|
||||
or service, as selected by command-line options.
|
||||
|
||||
${ec.rmRenameHelp.split('\n').join('\n\t\t')}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2019 Balena Ltd.
|
||||
* Copyright 2016-2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,17 +21,24 @@ import Command from '../command';
|
||||
import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
appToFleetOutputMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
config: boolean;
|
||||
device?: string; // device UUID
|
||||
json: boolean;
|
||||
help: void;
|
||||
service?: string; // service name
|
||||
verbose: boolean;
|
||||
v13: boolean;
|
||||
}
|
||||
|
||||
interface EnvironmentVariableInfo extends SDK.EnvironmentVariableBase {
|
||||
@ -56,20 +63,20 @@ interface ServiceEnvironmentVariableInfo
|
||||
|
||||
export default class EnvsCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
List the environment or config variables of an application, device or service.
|
||||
List the environment or config variables of a fleet, device or service.
|
||||
|
||||
List the environment or configuration variables of an application, device or
|
||||
service, as selected by the respective command-line options. (A service is
|
||||
an application container in a "microservices" application.)
|
||||
List the environment or configuration variables of a fleet, device or
|
||||
service, as selected by the respective command-line options. (A service
|
||||
corresponds to a Docker image/container in a microservices fleet.)
|
||||
|
||||
The results include application-wide (fleet), device-wide (multiple services on
|
||||
a device) and service-specific variables that apply to the selected application,
|
||||
device or service. It can be thought of as including "inherited" variables;
|
||||
for example, a service inherits device-wide variables, and a device inherits
|
||||
application-wide variables.
|
||||
The results include fleet-wide (multiple devices), device-specific (multiple
|
||||
services on a specific device) and service-specific variables that apply to the
|
||||
selected fleet, device or service. It can be thought of as including inherited
|
||||
variables; for example, a service inherits device-wide variables, and a device
|
||||
inherits fleet-wide variables.
|
||||
|
||||
The printed output may include DEVICE and/or SERVICE columns to distinguish
|
||||
between application-wide, device-specific and service-specific variables.
|
||||
between fleet-wide, device-specific and service-specific variables.
|
||||
An asterisk in these columns indicates that the variable applies to
|
||||
"all devices" or "all services".
|
||||
|
||||
@ -83,22 +90,22 @@ export default class EnvsCmd extends Command {
|
||||
types like lists and empty strings. The 'jq' utility may be helpful in shell
|
||||
scripts (https://stedolan.github.io/jq/manual/). When --json is used, an empty
|
||||
JSON array ([]) is printed instead of an error message when no variables exist
|
||||
for the given query. When querying variables for a device, note that the
|
||||
application name may be null in JSON output (or 'N/A' in tabular output) if the
|
||||
application linked to the device is no longer accessible by the current user
|
||||
(for example, in case the current user has been removed from the application
|
||||
by its owner).
|
||||
for the given query. When querying variables for a device, note that the fleet
|
||||
name may be null in JSON output (or 'N/A' in tabular output) if the fleet that
|
||||
the device belonged to is no longer accessible by the current user (for example,
|
||||
in case the current user was removed from the fleet by the fleet's owner).
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
|
||||
${appToFleetOutputMsg.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena envs --application MyApp',
|
||||
'$ balena envs --application myorg/myapp',
|
||||
'$ balena envs --application MyApp --json',
|
||||
'$ balena envs --application MyApp --service MyService',
|
||||
'$ balena envs --application MyApp --service MyService',
|
||||
'$ balena envs --application MyApp --config',
|
||||
'$ balena envs --fleet myorg/myfleet',
|
||||
'$ balena envs --fleet MyFleet --json',
|
||||
'$ balena envs --fleet MyFleet --service MyService',
|
||||
'$ balena envs --fleet MyFleet --service MyService',
|
||||
'$ balena envs --fleet MyFleet --config',
|
||||
'$ balena envs --device 7cf02a6',
|
||||
'$ balena envs --device 7cf02a6 --json',
|
||||
'$ balena envs --device 7cf02a6 --config --json',
|
||||
@ -113,34 +120,47 @@ export default class EnvsCmd extends Command {
|
||||
: {
|
||||
all: flags.boolean({
|
||||
default: false,
|
||||
description: stripIndent`
|
||||
No-op since balena CLI v12.0.0.`,
|
||||
description: 'No-op since balena CLI v12.0.0.',
|
||||
hidden: true,
|
||||
}),
|
||||
application: {
|
||||
exclusive: ['device', 'fleet', 'v13'],
|
||||
...cf.application,
|
||||
},
|
||||
}),
|
||||
|
||||
application: { exclusive: ['device'], ...cf.application },
|
||||
fleet: { exclusive: ['device', 'application'], ...cf.fleet },
|
||||
config: flags.boolean({
|
||||
default: false,
|
||||
char: 'c',
|
||||
description: 'show configuration variables only',
|
||||
exclusive: ['service'],
|
||||
}),
|
||||
device: { exclusive: ['application'], ...cf.device },
|
||||
device: { exclusive: ['fleet', 'application'], ...cf.device },
|
||||
help: cf.help,
|
||||
json: cf.json,
|
||||
verbose: cf.verbose,
|
||||
service: { exclusive: ['config'], ...cf.service },
|
||||
v13: cf.v13,
|
||||
};
|
||||
|
||||
protected useAppWord = false;
|
||||
protected hasWarned = false;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(EnvsCmd);
|
||||
this.useAppWord = !options.fleet && !options.v13 && !isV13();
|
||||
|
||||
const variables: EnvironmentVariableInfo[] = [];
|
||||
|
||||
await Command.checkLoggedIn();
|
||||
|
||||
if (options.application && !options.json && process.stderr.isTTY) {
|
||||
this.hasWarned = true;
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
if (!options.application && !options.device) {
|
||||
throw new ExpectedError('You must specify an application or device');
|
||||
throw new ExpectedError('Missing --fleet or --device option');
|
||||
}
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
@ -174,7 +194,7 @@ export default class EnvsCmd extends Command {
|
||||
const target =
|
||||
(options.service ? `service "${options.service}" of ` : '') +
|
||||
(options.application
|
||||
? `application "${options.application}"`
|
||||
? `fleet "${options.application}"`
|
||||
: `device "${options.device}"`);
|
||||
throw new ExpectedError(`No environment variables found for ${target}`);
|
||||
}
|
||||
@ -194,7 +214,9 @@ export default class EnvsCmd extends Command {
|
||||
return i;
|
||||
});
|
||||
|
||||
fields.push(options.json ? 'appName' : 'appName => APPLICATION');
|
||||
const jName = this.useAppWord ? 'appName' : 'fleetName';
|
||||
const tName = this.useAppWord ? 'APPLICATION' : 'FLEET';
|
||||
fields.push(options.json ? `appName => ${jName}` : `appName => ${tName}`);
|
||||
if (options.device) {
|
||||
fields.push(options.json ? 'deviceUUID' : 'deviceUUID => DEVICE');
|
||||
}
|
||||
@ -203,10 +225,13 @@ export default class EnvsCmd extends Command {
|
||||
}
|
||||
|
||||
if (options.json) {
|
||||
this.log(
|
||||
stringifyVarArray<SDK.EnvironmentVariableBase>(varArray, fields),
|
||||
);
|
||||
const { pickAndRename } = await import('../utils/helpers');
|
||||
const mapped = varArray.map((o) => pickAndRename(o, fields));
|
||||
this.log(JSON.stringify(mapped, null, 4));
|
||||
} else {
|
||||
if (!this.hasWarned && this.useAppWord && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetOutputMsg));
|
||||
}
|
||||
this.log(
|
||||
getVisuals().table.horizontal(
|
||||
_.sortBy(varArray, (v: SDK.EnvironmentVariableBase) => v.name),
|
||||
@ -227,7 +252,7 @@ async function validateServiceName(
|
||||
});
|
||||
if (services.length === 0) {
|
||||
throw new ExpectedError(
|
||||
`Service "${serviceName}" not found for application "${appName}"`,
|
||||
`Service "${serviceName}" not found for fleet "${appName}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -354,25 +379,3 @@ function fillInInfoFields(
|
||||
envVar.deviceUUID = deviceUUID || '*';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform each object (item) of varArray to preserve only the
|
||||
* fields (keys) listed in the fields argument.
|
||||
*/
|
||||
function stringifyVarArray<T = Dictionary<any>>(
|
||||
varArray: T[],
|
||||
fields: string[],
|
||||
): string {
|
||||
const transformed = varArray.map((o: Dictionary<any>) =>
|
||||
_.transform(
|
||||
o,
|
||||
(result, value, key) => {
|
||||
if (fields.includes(key)) {
|
||||
result[key] = value;
|
||||
}
|
||||
},
|
||||
{} as Dictionary<any>,
|
||||
),
|
||||
);
|
||||
return JSON.stringify(transformed, null, 4);
|
||||
}
|
||||
|
20
lib/commands/fleet/create.ts
Normal file
20
lib/commands/fleet/create.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 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 { FleetCreateCmd } from '../app/create';
|
||||
|
||||
export default FleetCreateCmd;
|
20
lib/commands/fleet/index.ts
Normal file
20
lib/commands/fleet/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 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 { FleetCmd } from '../app';
|
||||
|
||||
export default FleetCmd;
|
20
lib/commands/fleet/purge.ts
Normal file
20
lib/commands/fleet/purge.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 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 { FleetPurgeCmd } from '../app/purge';
|
||||
|
||||
export default FleetPurgeCmd;
|
20
lib/commands/fleet/rename.ts
Normal file
20
lib/commands/fleet/rename.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 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 { FleetRenameCmd } from '../app/rename';
|
||||
|
||||
export default FleetRenameCmd;
|
20
lib/commands/fleet/restart.ts
Normal file
20
lib/commands/fleet/restart.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 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 { FleetRestartCmd } from '../app/restart';
|
||||
|
||||
export default FleetRestartCmd;
|
20
lib/commands/fleet/rm.ts
Normal file
20
lib/commands/fleet/rm.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 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 { FleetRmCmd } from '../app/rm';
|
||||
|
||||
export default FleetRmCmd;
|
20
lib/commands/fleets.ts
Normal file
20
lib/commands/fleets.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016-2021 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 { FleetsCmd } from './apps';
|
||||
|
||||
export default FleetsCmd;
|
@ -21,9 +21,11 @@ import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../utils/lazy';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import { parseAsLocalHostnameOrIp } from '../utils/validation';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
pollInterval?: number;
|
||||
help?: void;
|
||||
}
|
||||
@ -34,22 +36,22 @@ interface ArgsDef {
|
||||
|
||||
export default class JoinCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Move a local device to an application on another balena server.
|
||||
Move a local device to a fleet on another balena server.
|
||||
|
||||
Move a local device to an application on another balena server, causing
|
||||
Move a local device to a fleet on another balena server, causing
|
||||
the device to "join" the new server. The device must be running balenaOS.
|
||||
|
||||
For example, you could provision a device against an openBalena installation
|
||||
where you perform end-to-end tests and then move it to balenaCloud when it's
|
||||
ready for production.
|
||||
|
||||
To move a device between applications on the same server, use the
|
||||
To move a device between fleets on the same server, use the
|
||||
\`balena device move\` command instead of \`balena join\`.
|
||||
|
||||
If you don't specify a device hostname or IP, this command will automatically
|
||||
scan the local network for balenaOS devices and prompt you to select one
|
||||
from an interactive picker. This may require administrator/root privileges.
|
||||
Likewise, if the application flag is not provided then a picker will be shown.
|
||||
Likewise, if the fleet option is not provided then a picker will be shown.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
@ -57,10 +59,10 @@ export default class JoinCmd extends Command {
|
||||
public static examples = [
|
||||
'$ balena join',
|
||||
'$ balena join balena.local',
|
||||
'$ balena join balena.local --application MyApp',
|
||||
'$ balena join balena.local -a myorg/myapp',
|
||||
'$ balena join balena.local --fleet MyFleet',
|
||||
'$ balena join balena.local -f myorg/myfleet',
|
||||
'$ balena join 192.168.1.25',
|
||||
'$ balena join 192.168.1.25 --application MyApp',
|
||||
'$ balena join 192.168.1.25 --fleet MyFleet',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
@ -75,7 +77,8 @@ export default class JoinCmd extends Command {
|
||||
public static usage = 'join [deviceIpOrHostname]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: cf.application,
|
||||
...(isV13() ? {} : { application: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
pollInterval: flags.integer({
|
||||
description: 'the interval in minutes to check for updates',
|
||||
char: 'i',
|
||||
@ -98,7 +101,7 @@ export default class JoinCmd extends Command {
|
||||
logger,
|
||||
sdk,
|
||||
params.deviceIpOrHostname,
|
||||
options.application,
|
||||
options.application || options.fleet,
|
||||
options.pollInterval,
|
||||
);
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ interface ArgsDef {
|
||||
|
||||
export default class LeaveCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Remove a local device from its balena application.
|
||||
Remove a local device from its balena fleet.
|
||||
|
||||
Remove a local device from its balena application, causing the device to
|
||||
Remove a local device from its balena fleet, causing the device to
|
||||
"leave" the server it is provisioned on. This effectively makes the device
|
||||
"unmanaged". The device must be running balenaOS.
|
||||
|
||||
|
@ -23,7 +23,12 @@ import Command from '../../command';
|
||||
import { ExpectedError } from '../../errors';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent, getCliForm } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
const CONNECTIONS_FOLDER = '/system-connections';
|
||||
|
||||
@ -31,6 +36,7 @@ interface FlagsDef {
|
||||
advanced?: boolean;
|
||||
application?: string;
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
config?: string;
|
||||
'config-app-update-poll-interval'?: number;
|
||||
'config-network'?: string;
|
||||
@ -66,8 +72,8 @@ export default class OsConfigureCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Configure a previously downloaded balenaOS image.
|
||||
|
||||
Configure a previously downloaded balenaOS image for a specific device type or
|
||||
balena application.
|
||||
Configure a previously downloaded balenaOS image for a specific device type
|
||||
or fleet.
|
||||
|
||||
Configuration settings such as WiFi authentication will be taken from the
|
||||
following sources, in precedence order:
|
||||
@ -75,8 +81,8 @@ export default class OsConfigureCmd extends Command {
|
||||
2. A given \`config.json\` file specified with the \`--config\` option.
|
||||
3. User input through interactive prompts (text menus).
|
||||
|
||||
The --device-type option may be used to override the application's default
|
||||
device type, in case of an application with mixed device types.
|
||||
The --device-type option may be used to override the fleet's default device
|
||||
type, in case of a fleet with mixed device types.
|
||||
|
||||
The --system-connection (-c) option can be used to inject NetworkManager connection
|
||||
profiles for additional network interfaces, such as cellular/GSM or additional
|
||||
@ -98,11 +104,10 @@ export default class OsConfigureCmd extends Command {
|
||||
public static examples = [
|
||||
'$ balena os configure ../path/rpi3.img --device 7cf02a6',
|
||||
'$ balena os configure ../path/rpi3.img --device 7cf02a6 --device-api-key <existingDeviceKey>',
|
||||
'$ balena os configure ../path/rpi3.img --app MyApp',
|
||||
'$ balena os configure ../path/rpi3.img -a myorg/myapp',
|
||||
'$ balena os configure ../path/rpi3.img --app MyApp --version 2.12.7',
|
||||
'$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3',
|
||||
'$ balena os configure ../path/rpi3.img --app MyFinApp --device-type raspberrypi3 --config myWifiConfig.json',
|
||||
'$ balena os configure ../path/rpi3.img --fleet myorg/myfleet',
|
||||
'$ balena os configure ../path/rpi3.img --fleet MyFleet --version 2.12.7',
|
||||
'$ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3',
|
||||
'$ balena os configure ../path/rpi3.img -f MyFinFleet --device-type raspberrypi3 --config myWifiConfig.json',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
@ -121,15 +126,29 @@ export default class OsConfigureCmd extends Command {
|
||||
description:
|
||||
'ask advanced configuration questions (when in interactive mode)',
|
||||
}),
|
||||
application: { ...cf.application, exclusive: ['app', 'device'] },
|
||||
app: { ...cf.app, exclusive: ['application', 'device'] },
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device'],
|
||||
},
|
||||
config: flags.string({
|
||||
description:
|
||||
'path to a pre-generated config.json file to be injected in the OS image',
|
||||
}),
|
||||
'config-app-update-poll-interval': flags.integer({
|
||||
description:
|
||||
'interval (in minutes) for the on-device balena supervisor periodic app update check',
|
||||
'supervisor cloud polling interval in minutes (e.g. for variable updates)',
|
||||
}),
|
||||
'config-network': flags.string({
|
||||
description: 'device network type (non-interactive configuration)',
|
||||
@ -141,7 +160,7 @@ export default class OsConfigureCmd extends Command {
|
||||
'config-wifi-ssid': flags.string({
|
||||
description: 'WiFi SSID (network name) (non-interactive configuration)',
|
||||
}),
|
||||
device: { exclusive: ['app', 'application'], ...cf.device },
|
||||
device: { exclusive: ['app', 'application', 'fleet'], ...cf.device },
|
||||
'device-api-key': flags.string({
|
||||
char: 'k',
|
||||
description:
|
||||
@ -149,7 +168,7 @@ export default class OsConfigureCmd extends Command {
|
||||
}),
|
||||
'device-type': flags.string({
|
||||
description:
|
||||
'device type slug (e.g. "raspberrypi3") to override the application device type',
|
||||
'device type slug (e.g. "raspberrypi3") to override the fleet device type',
|
||||
}),
|
||||
'initial-device-name': flags.string({
|
||||
description:
|
||||
@ -172,9 +191,10 @@ export default class OsConfigureCmd extends Command {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
OsConfigureCmd,
|
||||
);
|
||||
// Prefer options.application over options.app
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
await validateOptions(options);
|
||||
|
||||
@ -302,12 +322,12 @@ async function validateOptions(options: FlagsDef) {
|
||||
// flag definitions above, so oclif will enforce that they are not both used together.
|
||||
if (!options.device && !options.application) {
|
||||
throw new ExpectedError(
|
||||
"Either the '--device' or the '--application' option must be provided",
|
||||
"Either the '--device' or the '--fleet' option must be provided",
|
||||
);
|
||||
}
|
||||
if (!options.application && options['device-type']) {
|
||||
throw new ExpectedError(
|
||||
"The '--device-type' option can only be used in conjunction with the '--application' option",
|
||||
"The '--device-type' option can only be used in conjunction with the '--fleet' option",
|
||||
);
|
||||
}
|
||||
if (options['device-api-key']) {
|
||||
@ -366,7 +386,7 @@ async function checkDeviceTypeCompatibility(
|
||||
const helpers = await import('../../utils/helpers');
|
||||
if (!helpers.areDeviceTypesCompatible(appDeviceType, optionDeviceType)) {
|
||||
throw new ExpectedError(
|
||||
`Device type ${options['device-type']} is incompatible with application ${options.application}`,
|
||||
`Device type ${options['device-type']} is incompatible with fleet ${options.application}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import Command from '../command';
|
||||
import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import {
|
||||
getBalenaSdk,
|
||||
@ -24,9 +24,17 @@ import {
|
||||
getVisuals,
|
||||
stripIndent,
|
||||
} from '../utils/lazy';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import type { DockerConnectionCliFlags } from '../utils/docker';
|
||||
import { dockerConnectionCliFlags } from '../utils/docker';
|
||||
import { parseAsInteger } from '../utils/validation';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import * as _ from 'lodash';
|
||||
import type {
|
||||
Application,
|
||||
@ -36,11 +44,10 @@ import type {
|
||||
Release,
|
||||
} from 'balena-sdk';
|
||||
import type { Preloader } from 'balena-preload';
|
||||
import { parseAsInteger } from '../utils/validation';
|
||||
import { ExpectedError } from '../errors';
|
||||
|
||||
interface FlagsDef extends DockerConnectionCliFlags {
|
||||
app?: string;
|
||||
fleet?: string;
|
||||
commit?: string;
|
||||
'splash-image'?: string;
|
||||
'dont-check-arch': boolean;
|
||||
@ -56,16 +63,16 @@ interface ArgsDef {
|
||||
|
||||
export default class PreloadCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Preload an app on a disk image (or Edison zip archive).
|
||||
Preload a release on a disk image (or Edison zip archive).
|
||||
|
||||
Preload a balena application release (app images/containers), and optionally
|
||||
Preload a release (service images/containers) from a balena fleet, and optionally
|
||||
a balenaOS splash screen, in a previously downloaded '.img' balenaOS image file
|
||||
in the local disk (a zip file is only accepted for the Intel Edison device type).
|
||||
After preloading, the balenaOS image file can be flashed to a device's SD card.
|
||||
When the device boots, it will not need to download the application, as it was
|
||||
When the device boots, it will not need to download the release, as it was
|
||||
preloaded. This is usually combined with release pinning
|
||||
(https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/)
|
||||
to avoid the device dowloading a newer release straight away, if one is available.
|
||||
to avoid the device downloading a newer release straight away, if available.
|
||||
Check also the Preloading and Preregistering section of the balena CLI's advanced
|
||||
masterclass document:
|
||||
https://www.balena.io/docs/learn/more/masterclasses/advanced-cli/#5-preloading-and-preregistering
|
||||
@ -82,8 +89,8 @@ export default class PreloadCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena preload balena.img --app MyApp --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0',
|
||||
'$ balena preload balena.img --app myorg/myapp --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image image.png',
|
||||
'$ balena preload balena.img --fleet MyFleet --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0',
|
||||
'$ balena preload balena.img --fleet myorg/myfleet --splash-image image.png',
|
||||
'$ balena preload balena.img',
|
||||
];
|
||||
|
||||
@ -98,13 +105,16 @@ export default class PreloadCmd extends Command {
|
||||
public static usage = 'preload <image>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
// TODO: Replace with application/a in #v13?
|
||||
app: cf.application,
|
||||
...(isV13() ? {} : { app: cf.application }),
|
||||
fleet: cf.fleet,
|
||||
commit: flags.string({
|
||||
description: `\
|
||||
The commit hash for a specific application release to preload, use "current" to specify the current
|
||||
release (ignored if no appId is given). The current release is usually also the latest, but can be
|
||||
manually pinned using https://github.com/balena-io-projects/staged-releases .\
|
||||
The commit hash of the release to preload. Use "current" to specify the current
|
||||
release (ignored if no appId is given). The current release is usually also the
|
||||
latest, but can be pinned to a specific release. See:
|
||||
https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/
|
||||
https://www.balena.io/docs/learn/more/masterclasses/fleet-management/#63-pin-using-the-api
|
||||
https://github.com/balena-io-examples/staged-releases\
|
||||
`,
|
||||
char: 'c',
|
||||
}),
|
||||
@ -115,7 +125,7 @@ manually pinned using https://github.com/balena-io-projects/staged-releases .\
|
||||
'dont-check-arch': flags.boolean({
|
||||
default: false,
|
||||
description:
|
||||
'disables check for matching architecture in image and application',
|
||||
'disable architecture compatibility check between image and fleet',
|
||||
}),
|
||||
'pin-device-to-release': flags.boolean({
|
||||
default: false,
|
||||
@ -159,6 +169,11 @@ Can be repeated to add multiple certificates.\
|
||||
PreloadCmd,
|
||||
);
|
||||
|
||||
if (options.app && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.app ||= options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const balenaPreload = await import('balena-preload');
|
||||
const visuals = getVisuals();
|
||||
@ -190,7 +205,7 @@ Can be repeated to add multiple certificates.\
|
||||
const { getApplication } = await import('../utils/sdk');
|
||||
const application = await getApplication(balena, options.app);
|
||||
if (!application) {
|
||||
throw new ExpectedError(`Application not found: ${options.app}`);
|
||||
throw new ExpectedError(`Fleet not found: ${options.app}`);
|
||||
}
|
||||
options.app = application.slug;
|
||||
}
|
||||
@ -239,7 +254,7 @@ Can be repeated to add multiple certificates.\
|
||||
|
||||
if (dontCheckArch && !appId) {
|
||||
throw new ExpectedError(
|
||||
'You need to specify an application if you disable the architecture check.',
|
||||
'You need to specify a fleet if you disable the architecture check.',
|
||||
);
|
||||
}
|
||||
|
||||
@ -417,11 +432,11 @@ Can be repeated to add multiple certificates.\
|
||||
applicationInfoSpinner.stop();
|
||||
if (applications.length === 0) {
|
||||
throw new ExpectedError(
|
||||
`You have no apps with successful releases for a '${deviceTypeSlug}' device type.`,
|
||||
`No fleets found with successful releases for device type '${deviceTypeSlug}'`,
|
||||
);
|
||||
}
|
||||
return getCliForm().ask({
|
||||
message: 'Select an application',
|
||||
message: 'Select a fleet',
|
||||
type: 'list',
|
||||
choices: applications.map((app) => ({
|
||||
name: app.app_name,
|
||||
@ -432,7 +447,7 @@ Can be repeated to add multiple certificates.\
|
||||
|
||||
selectApplicationCommit(releases: Release[]) {
|
||||
if (releases.length === 0) {
|
||||
throw new ExpectedError('This application has no successful releases.');
|
||||
throw new ExpectedError('This fleet has no successful releases.');
|
||||
}
|
||||
const DEFAULT_CHOICE = { name: 'current', value: 'current' };
|
||||
const choices = [DEFAULT_CHOICE].concat(
|
||||
@ -465,23 +480,23 @@ Can be repeated to add multiple certificates.\
|
||||
}
|
||||
const message = `\
|
||||
|
||||
This application is set to track the latest release, and non-pinned devices
|
||||
This fleet is set to track the latest release, and non-pinned devices
|
||||
are automatically updated when a new release is available. This may lead to
|
||||
unexpected behavior: The preloaded device will download and install the latest
|
||||
release once it is online.
|
||||
|
||||
This prompt gives you the opportunity to disable automatic updates for this
|
||||
application now. Note that this would result in the application being pinned
|
||||
to the current latest release, rather than some other release that may have
|
||||
This prompt gives you the opportunity to disable automatic updates for
|
||||
this fleet now. Note that this would result in the fleet being pinned to
|
||||
the current latest release, rather than some other release that may have
|
||||
been selected for preloading. The pinned released may be further managed
|
||||
through the web dashboard or programatically through the balena API / SDK.
|
||||
Documentation about release policies and app/device pinning can be found at:
|
||||
Documentation about release policies and pinning can be found at:
|
||||
https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/
|
||||
|
||||
Alternatively, the --pin-device-to-release flag may be used to pin only the
|
||||
preloaded device to the selected release.
|
||||
|
||||
Would you like to disable automatic updates for this application now?\
|
||||
Would you like to disable automatic updates for this fleet now?\
|
||||
`;
|
||||
const update = await getCliForm().ask({
|
||||
message,
|
||||
@ -531,7 +546,7 @@ Would you like to disable automatic updates for this application now?\
|
||||
if (this.isCurrentCommit(options.commit)) {
|
||||
if (!appCommit) {
|
||||
throw new Error(
|
||||
`Unexpected empty commit hash for app ID "${application.id}"`,
|
||||
`Unexpected empty commit hash for fleet ID "${application.id}"`,
|
||||
);
|
||||
}
|
||||
// handle `--commit current` (and its `--commit latest` synonym)
|
||||
|
@ -36,7 +36,7 @@ enum BuildTarget {
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
applicationOrDevice: string;
|
||||
fleetOrDevice: string;
|
||||
}
|
||||
|
||||
interface FlagsDef {
|
||||
@ -63,9 +63,9 @@ interface FlagsDef {
|
||||
|
||||
export default class PushCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Start a build on the remote balenaCloud build servers, or a local mode device.
|
||||
Build release images on balenaCloud servers or on a local mode device.
|
||||
|
||||
Start a build on the remote balenaCloud build servers, or a local mode device.
|
||||
Build release images on balenaCloud servers or on a local mode device.
|
||||
|
||||
When building on the balenaCloud servers, the given source directory will be
|
||||
sent to the remote server. This can be used as a drop-in replacement for the
|
||||
@ -92,16 +92,16 @@ export default class PushCmd extends Command {
|
||||
|
||||
${dockerignoreHelp.split('\n').join('\n\t\t')}
|
||||
|
||||
Note: the --service and --env flags must come after the applicationOrDevice
|
||||
Note: the --service and --env flags must come after the fleetOrDevice
|
||||
parameter, as per examples.
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena push myApp',
|
||||
'$ balena push myApp --source <source directory>',
|
||||
'$ balena push myApp -s <source directory>',
|
||||
'$ balena push myApp --release-tag key1 "" key2 "value2 with spaces"',
|
||||
'$ balena push myorg/myapp',
|
||||
'$ balena push myFleet',
|
||||
'$ balena push myFleet --source <source directory>',
|
||||
'$ balena push myFleet -s <source directory>',
|
||||
'$ balena push myFleet --release-tag key1 "" key2 "value2 with spaces"',
|
||||
'$ balena push myorg/myfleet',
|
||||
'',
|
||||
'$ balena push 10.0.0.1',
|
||||
'$ balena push 10.0.0.1 --source <source directory>',
|
||||
@ -115,15 +115,15 @@ export default class PushCmd extends Command {
|
||||
|
||||
public static args = [
|
||||
{
|
||||
name: 'applicationOrDevice',
|
||||
name: 'fleetOrDevice',
|
||||
description:
|
||||
'application name or slug, or local device IP address or hostname',
|
||||
'fleet name or slug, or local device IP address or ".local" hostname',
|
||||
required: true,
|
||||
parse: lowercaseIfSlug,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'push <applicationOrDevice>';
|
||||
public static usage = 'push <fleetOrDevice>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
source: flags.string({
|
||||
@ -188,7 +188,7 @@ export default class PushCmd extends Command {
|
||||
When pushing to the cloud, this option will cause the build to start, then
|
||||
return execution back to the shell, with the status and release ID (if
|
||||
applicable). When pushing to a local mode device, this option will cause
|
||||
the command to not tail application logs when the build has completed.`,
|
||||
the command to not tail logs when the build has completed.`,
|
||||
char: 'd',
|
||||
default: false,
|
||||
}),
|
||||
@ -260,7 +260,7 @@ export default class PushCmd extends Command {
|
||||
}),
|
||||
'release-tag': flags.string({
|
||||
description: stripIndent`
|
||||
Set release tags if the push to a cloud application is successful. Multiple
|
||||
Set release tags if the image build is successful (balenaCloud only). Multiple
|
||||
arguments may be provided, alternating tag keys and values (see examples).
|
||||
Hint: Empty values may be specified with "" (bash, cmd.exe) or '""' (PowerShell).
|
||||
`,
|
||||
@ -292,14 +292,12 @@ export default class PushCmd extends Command {
|
||||
},
|
||||
);
|
||||
|
||||
switch (await this.getBuildTarget(params.applicationOrDevice)) {
|
||||
switch (await this.getBuildTarget(params.fleetOrDevice)) {
|
||||
case BuildTarget.Cloud:
|
||||
logger.logDebug(
|
||||
`Pushing to cloud for application: ${params.applicationOrDevice}`,
|
||||
);
|
||||
logger.logDebug(`Pushing to cloud for fleet: ${params.fleetOrDevice}`);
|
||||
|
||||
await this.pushToCloud(
|
||||
params.applicationOrDevice,
|
||||
params.fleetOrDevice,
|
||||
options,
|
||||
sdk,
|
||||
dockerfilePath,
|
||||
@ -308,11 +306,9 @@ export default class PushCmd extends Command {
|
||||
break;
|
||||
|
||||
case BuildTarget.Device:
|
||||
logger.logDebug(
|
||||
`Pushing to local device: ${params.applicationOrDevice}`,
|
||||
);
|
||||
logger.logDebug(`Pushing to local device: ${params.fleetOrDevice}`);
|
||||
await this.pushToDevice(
|
||||
params.applicationOrDevice,
|
||||
params.fleetOrDevice,
|
||||
options,
|
||||
dockerfilePath,
|
||||
registrySecrets,
|
||||
@ -402,7 +398,7 @@ export default class PushCmd extends Command {
|
||||
this.checkInvalidOptions(
|
||||
remoteOnlyOptions,
|
||||
options,
|
||||
'is only valid when pushing to an application',
|
||||
'is only valid when pushing to a fleet',
|
||||
);
|
||||
|
||||
const deviceDeploy = await import('../utils/device/deploy');
|
||||
|
@ -31,20 +31,20 @@ interface FlagsDef {
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
applicationOrDevice: string;
|
||||
fleetOrDevice: string;
|
||||
service?: string;
|
||||
}
|
||||
|
||||
export default class SshCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
SSH into the host or application container of a device.
|
||||
Open a SSH prompt on a device's host OS or service container.
|
||||
|
||||
Start a shell on a local or remote device. If a service name is not provided,
|
||||
a shell will be opened on the host OS.
|
||||
|
||||
If an application is provided, an interactive menu will be presented
|
||||
for the selection of an online device. A shell will then be opened for the
|
||||
host OS or service container of the chosen device.
|
||||
If a fleet is provided, an interactive menu will be presented for the selection
|
||||
of an online device. A shell will then be opened for the host OS or service
|
||||
container of the chosen device.
|
||||
|
||||
For local devices, the IP address and .local domain name are supported.
|
||||
If the device is referenced by IP or \`.local\` address, the connection
|
||||
@ -64,7 +64,7 @@ export default class SshCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena ssh MyApp',
|
||||
'$ balena ssh MyFleet',
|
||||
'$ balena ssh f49cefd',
|
||||
'$ balena ssh f49cefd my-service',
|
||||
'$ balena ssh f49cefd --port <port>',
|
||||
@ -76,9 +76,9 @@ export default class SshCmd extends Command {
|
||||
|
||||
public static args = [
|
||||
{
|
||||
name: 'applicationOrDevice',
|
||||
name: 'fleetOrDevice',
|
||||
description:
|
||||
'application name/slug/id, device uuid, or address of local device',
|
||||
'fleet name/slug/id, device uuid, or address of local device',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@ -88,7 +88,7 @@ export default class SshCmd extends Command {
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'ssh <applicationOrDevice> [service]';
|
||||
public static usage = 'ssh <fleetOrDevice> [service]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
port: flags.integer({
|
||||
@ -124,10 +124,10 @@ export default class SshCmd extends Command {
|
||||
);
|
||||
|
||||
// Local connection
|
||||
if (validateLocalHostnameOrIp(params.applicationOrDevice)) {
|
||||
if (validateLocalHostnameOrIp(params.fleetOrDevice)) {
|
||||
const { performLocalDeviceSSH } = await import('../utils/device/ssh');
|
||||
return await performLocalDeviceSSH({
|
||||
address: params.applicationOrDevice,
|
||||
address: params.fleetOrDevice,
|
||||
port: options.port,
|
||||
forceTTY: options.tty,
|
||||
verbose: options.verbose,
|
||||
@ -147,7 +147,7 @@ export default class SshCmd extends Command {
|
||||
await Command.checkLoggedIn();
|
||||
const deviceUuid = await getOnlineTargetDeviceUuid(
|
||||
sdk,
|
||||
params.applicationOrDevice,
|
||||
params.fleetOrDevice,
|
||||
);
|
||||
|
||||
const device = await sdk.models.device.get(deviceUuid, {
|
||||
|
@ -20,10 +20,16 @@ import Command from '../command';
|
||||
import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getCliUx, stripIndent } from '../utils/lazy';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
duration?: string;
|
||||
help: void;
|
||||
@ -35,16 +41,16 @@ interface ArgsDef {
|
||||
|
||||
export default class SupportCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Grant or revoke support access for devices and applications.
|
||||
Grant or revoke support access for devices or fleets.
|
||||
|
||||
Grant or revoke balena support agent access to devices and applications
|
||||
Grant or revoke balena support agent access to devices or fleets
|
||||
on balenaCloud. (This command does not apply to openBalena.)
|
||||
Access will be automatically revoked once the specified duration has elapsed.
|
||||
|
||||
Duration defaults to 24h, but can be specified using --duration flag in days
|
||||
or hours, e.g. '12h', '2d'.
|
||||
|
||||
Both --device and --application flags accept multiple values, specified as
|
||||
Both --device and --fleet flags accept multiple values, specified as
|
||||
a comma-separated list (with no spaces).
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
@ -52,8 +58,8 @@ export default class SupportCmd extends Command {
|
||||
|
||||
public static examples = [
|
||||
'balena support enable --device ab346f,cd457a --duration 3d',
|
||||
'balena support enable --application app3 --duration 12h',
|
||||
'balena support disable -a myorg/myapp',
|
||||
'balena support enable --fleet myFleet --duration 12h',
|
||||
'balena support disable -f myorg/myfleet',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
@ -71,10 +77,11 @@ export default class SupportCmd extends Command {
|
||||
description: 'comma-separated list (no spaces) of device UUIDs',
|
||||
char: 'd',
|
||||
}),
|
||||
application: {
|
||||
...cf.application,
|
||||
...(isV13() ? {} : { application: cf.application }),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
description:
|
||||
'comma-separated list (no spaces) of application names or org/name slugs',
|
||||
'comma-separated list (no spaces) of fleet names or org/name slugs',
|
||||
},
|
||||
duration: flags.string({
|
||||
description:
|
||||
@ -91,6 +98,11 @@ export default class SupportCmd extends Command {
|
||||
SupportCmd,
|
||||
);
|
||||
|
||||
if (options.application && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const ux = getCliUx();
|
||||
|
||||
@ -98,9 +110,7 @@ export default class SupportCmd extends Command {
|
||||
|
||||
// Validation
|
||||
if (!options.device && !options.application) {
|
||||
throw new ExpectedError(
|
||||
'At least one device or application must be specified',
|
||||
);
|
||||
throw new ExpectedError('At least one device or fleet must be specified');
|
||||
}
|
||||
|
||||
if (options.duration != null && !enabling) {
|
||||
@ -135,10 +145,10 @@ export default class SupportCmd extends Command {
|
||||
// Process applications
|
||||
for (const appName of appNames) {
|
||||
if (enabling) {
|
||||
ux.action.start(`${enablingMessage} application ${appName}`);
|
||||
ux.action.start(`${enablingMessage} fleet ${appName}`);
|
||||
await balena.models.application.grantSupportAccess(appName, expiryTs);
|
||||
} else if (params.action === 'disable') {
|
||||
ux.action.start(`${disablingMessage} application ${appName}`);
|
||||
ux.action.start(`${disablingMessage} fleet ${appName}`);
|
||||
await balena.models.application.revokeSupportAccess(appName);
|
||||
}
|
||||
ux.action.stop();
|
||||
|
@ -19,14 +19,20 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
app?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
release?: string;
|
||||
help: void;
|
||||
app?: string;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
@ -35,16 +41,16 @@ interface ArgsDef {
|
||||
|
||||
export default class TagRmCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Remove a tag from an application, device or release.
|
||||
Remove a tag from a fleet, device or release.
|
||||
|
||||
Remove a tag from an application, device or release.
|
||||
Remove a tag from a fleet, device or release.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena tag rm myTagKey --application MyApp',
|
||||
'$ balena tag rm myTagKey -a myorg/myapp',
|
||||
'$ balena tag rm myTagKey --fleet MyFleet',
|
||||
'$ balena tag rm myTagKey -f myorg/myfleet',
|
||||
'$ balena tag rm myTagKey --device 7cf02a6',
|
||||
'$ balena tag rm myTagKey --release 1234',
|
||||
'$ balena tag rm myTagKey --release b376b0e544e9429483b656490e5b9443b4349bd6',
|
||||
@ -61,21 +67,29 @@ export default class TagRmCmd extends Command {
|
||||
public static usage = 'tag rm <tagKey>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'device', 'release'],
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device', 'release'],
|
||||
},
|
||||
device: {
|
||||
...cf.device,
|
||||
exclusive: ['app', 'application', 'release'],
|
||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
||||
},
|
||||
release: {
|
||||
...cf.release,
|
||||
exclusive: ['app', 'application', 'device'],
|
||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
||||
},
|
||||
help: cf.help,
|
||||
};
|
||||
@ -87,9 +101,10 @@ export default class TagRmCmd extends Command {
|
||||
TagRmCmd,
|
||||
);
|
||||
|
||||
// Prefer options.application over options.app
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
@ -130,9 +145,9 @@ export default class TagRmCmd extends Command {
|
||||
protected static missingResourceMessage = stripIndent`
|
||||
To remove a resource tag, you must provide exactly one of:
|
||||
|
||||
* An application, with --application <appNameOrSlug>
|
||||
* A device, with --device <uuid>
|
||||
* A release, with --release <id or commit>
|
||||
* A fleet, with --fleet <fleetNameOrSlug>
|
||||
* A device, with --device <UUID>
|
||||
* A release, with --release <ID or commit>
|
||||
|
||||
See the help page for examples:
|
||||
|
||||
|
@ -19,14 +19,20 @@ import { flags } from '@oclif/command';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
|
||||
import { applicationIdInfo } from '../../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../../utils/messages';
|
||||
import { isV13 } from '../../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
app?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
release?: string;
|
||||
help: void;
|
||||
app?: string;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
@ -36,9 +42,9 @@ interface ArgsDef {
|
||||
|
||||
export default class TagSetCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Set a tag on an application, device or release.
|
||||
Set a tag on a fleet, device or release.
|
||||
|
||||
Set a tag on an application, device or release.
|
||||
Set a tag on a fleet, device or release.
|
||||
|
||||
You can optionally provide a value to be associated with the created
|
||||
tag, as an extra argument after the tag key. If a value isn't
|
||||
@ -48,9 +54,9 @@ export default class TagSetCmd extends Command {
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena tag set mySimpleTag --application MyApp',
|
||||
'$ balena tag set mySimpleTag -a myorg/myapp',
|
||||
'$ balena tag set myCompositeTag myTagValue --application MyApp',
|
||||
'$ balena tag set mySimpleTag --fleet MyFleet',
|
||||
'$ balena tag set mySimpleTag -f myorg/myfleet',
|
||||
'$ balena tag set myCompositeTag myTagValue --fleet MyFleet',
|
||||
'$ balena tag set myCompositeTag myTagValue --device 7cf02a6',
|
||||
'$ balena tag set myCompositeTag "my tag value with whitespaces" --device 7cf02a6',
|
||||
'$ balena tag set myCompositeTag myTagValue --release 1234',
|
||||
@ -74,21 +80,29 @@ export default class TagSetCmd extends Command {
|
||||
public static usage = 'tag set <tagKey> [value]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'device', 'release'],
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device', 'release'],
|
||||
},
|
||||
device: {
|
||||
...cf.device,
|
||||
exclusive: ['app', 'application', 'release'],
|
||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
||||
},
|
||||
release: {
|
||||
...cf.release,
|
||||
exclusive: ['app', 'application', 'device'],
|
||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
||||
},
|
||||
help: cf.help,
|
||||
};
|
||||
@ -100,9 +114,10 @@ export default class TagSetCmd extends Command {
|
||||
TagSetCmd,
|
||||
);
|
||||
|
||||
// Prefer options.application over options.app
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
@ -151,9 +166,9 @@ export default class TagSetCmd extends Command {
|
||||
protected static missingResourceMessage = stripIndent`
|
||||
To set a resource tag, you must provide exactly one of:
|
||||
|
||||
* An application, with --application <appNameOrSlug>
|
||||
* A device, with --device <uuid>
|
||||
* A release, with --release <id or commit>
|
||||
* A fleet, with --fleet <fleetNameOrSlug>
|
||||
* A device, with --device <UUID>
|
||||
* A release, with --release <ID or commit>
|
||||
|
||||
See the help page for examples:
|
||||
|
||||
|
@ -20,29 +20,34 @@ import Command from '../command';
|
||||
import { ExpectedError } from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, getVisuals, stripIndent } from '../utils/lazy';
|
||||
import { applicationIdInfo } from '../utils/messages';
|
||||
import {
|
||||
applicationIdInfo,
|
||||
appToFleetFlagMsg,
|
||||
warnify,
|
||||
} from '../utils/messages';
|
||||
import { isV13 } from '../utils/version';
|
||||
|
||||
interface FlagsDef {
|
||||
app?: string;
|
||||
application?: string;
|
||||
fleet?: string;
|
||||
device?: string;
|
||||
release?: string;
|
||||
help: void;
|
||||
app?: string;
|
||||
}
|
||||
|
||||
export default class TagsCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
List all tags for an application, device or release.
|
||||
List all tags for a fleet, device or release.
|
||||
|
||||
List all tags and their values for a particular application,
|
||||
device or release.
|
||||
List all tags and their values for the specified fleet, device or release.
|
||||
|
||||
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena tags --application MyApp',
|
||||
'$ balena tags -a myorg/myapp',
|
||||
'$ balena tags --fleet MyFleet',
|
||||
'$ balena tags -f myorg/myfleet',
|
||||
'$ balena tags --device 7cf02a6',
|
||||
'$ balena tags --release 1234',
|
||||
'$ balena tags --release b376b0e544e9429483b656490e5b9443b4349bd6',
|
||||
@ -51,21 +56,29 @@ export default class TagsCmd extends Command {
|
||||
public static usage = 'tags';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'device', 'release'],
|
||||
...(isV13()
|
||||
? {}
|
||||
: {
|
||||
application: {
|
||||
...cf.application,
|
||||
exclusive: ['app', 'fleet', 'device', 'release'],
|
||||
},
|
||||
app: {
|
||||
...cf.app,
|
||||
exclusive: ['application', 'fleet', 'device', 'release'],
|
||||
},
|
||||
}),
|
||||
fleet: {
|
||||
...cf.fleet,
|
||||
exclusive: ['app', 'application', 'device', 'release'],
|
||||
},
|
||||
device: {
|
||||
...cf.device,
|
||||
exclusive: ['app', 'application', 'release'],
|
||||
exclusive: ['app', 'application', 'fleet', 'release'],
|
||||
},
|
||||
release: {
|
||||
...cf.release,
|
||||
exclusive: ['app', 'application', 'device'],
|
||||
exclusive: ['app', 'application', 'fleet', 'device'],
|
||||
},
|
||||
help: cf.help,
|
||||
};
|
||||
@ -75,9 +88,10 @@ export default class TagsCmd extends Command {
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(TagsCmd);
|
||||
|
||||
// Prefer options.application over options.app
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
if ((options.application || options.app) && process.stderr.isTTY) {
|
||||
console.error(warnify(appToFleetFlagMsg));
|
||||
}
|
||||
options.application ||= options.app || options.fleet;
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
@ -123,7 +137,7 @@ export default class TagsCmd extends Command {
|
||||
protected missingResourceMessage = stripIndent`
|
||||
To list tags for a resource, you must provide exactly one of:
|
||||
|
||||
* An application, with --application <appNameOrSlug>
|
||||
* A fleet, with --fleet <fleetNameOrSlug>
|
||||
* A device, with --device <uuid>
|
||||
* A release, with --release <id or commit>
|
||||
|
||||
|
@ -24,6 +24,8 @@ import {
|
||||
} from '../errors';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getBalenaSdk, stripIndent } from '../utils/lazy';
|
||||
import { lowercaseIfSlug } from '../utils/normalization';
|
||||
|
||||
import type { Server, Socket } from 'net';
|
||||
|
||||
interface FlagsDef {
|
||||
@ -32,7 +34,7 @@ interface FlagsDef {
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
deviceOrApplication: string;
|
||||
deviceOrFleet: string;
|
||||
}
|
||||
|
||||
export default class TunnelCmd extends Command {
|
||||
@ -62,7 +64,7 @@ export default class TunnelCmd extends Command {
|
||||
|
||||
public static examples = [
|
||||
'# map remote port 22222 to localhost:22222',
|
||||
'$ balena tunnel myApp -p 22222',
|
||||
'$ balena tunnel myFleet -p 22222',
|
||||
'',
|
||||
'# map remote port 22222 to localhost:222',
|
||||
'$ balena tunnel 2ead211 -p 22222:222',
|
||||
@ -71,21 +73,22 @@ export default class TunnelCmd extends Command {
|
||||
'$ balena tunnel 1546690 -p 22222:0.0.0.0',
|
||||
'',
|
||||
'# map remote port 22222 to any address on your host machine, port 222',
|
||||
'$ balena tunnel myApp -p 22222:0.0.0.0:222',
|
||||
'$ balena tunnel myFleet -p 22222:0.0.0.0:222',
|
||||
'',
|
||||
'# multiple port tunnels can be specified at any one time',
|
||||
'$ balena tunnel myApp -p 8080:3000 -p 8081:9000',
|
||||
'$ balena tunnel myFleet -p 8080:3000 -p 8081:9000',
|
||||
];
|
||||
|
||||
public static args = [
|
||||
{
|
||||
name: 'deviceOrApplication',
|
||||
description: 'device uuid or application name/slug/id',
|
||||
name: 'deviceOrFleet',
|
||||
description: 'device UUID or fleet name/slug/ID',
|
||||
required: true,
|
||||
parse: lowercaseIfSlug,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'tunnel <deviceOrApplication>';
|
||||
public static usage = 'tunnel <deviceOrFleet>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
port: flags.string({
|
||||
@ -132,10 +135,7 @@ export default class TunnelCmd extends Command {
|
||||
|
||||
// Ascertain device uuid
|
||||
const { getOnlineTargetDeviceUuid } = await import('../utils/patterns');
|
||||
const uuid = await getOnlineTargetDeviceUuid(
|
||||
sdk,
|
||||
params.deviceOrApplication,
|
||||
);
|
||||
const uuid = await getOnlineTargetDeviceUuid(sdk, params.deviceOrFleet);
|
||||
const device = await sdk.models.device.get(uuid);
|
||||
logger.logInfo(`Opening a tunnel to ${device.uuid}...`);
|
||||
|
||||
|
@ -37,9 +37,9 @@ interface CachedUsername {
|
||||
* Mixpanel.com analytics tracking (information on balena CLI usage).
|
||||
*
|
||||
* @param commandSignature A string like, for example:
|
||||
* "push <applicationOrDevice>"
|
||||
* That's literally so: "applicationOrDevice" is NOT replaced with the actual
|
||||
* application ID or device ID. The purpose is to find out the most / least
|
||||
* "push <fleetOrDevice>"
|
||||
* That's literally so: "fleetOrDevice" is NOT replaced with the actual
|
||||
* fleet ID or device ID. The purpose is to find out the most / least
|
||||
* used command verbs, so we can focus our development effort where it is most
|
||||
* beneficial to end users.
|
||||
*
|
||||
|
@ -182,8 +182,8 @@ export default class BalenaHelp extends Help {
|
||||
'push',
|
||||
'logs',
|
||||
'ssh',
|
||||
'apps',
|
||||
'app',
|
||||
'fleets',
|
||||
'fleet',
|
||||
'devices',
|
||||
'device',
|
||||
'tunnel',
|
||||
|
@ -64,8 +64,8 @@ export const getDeviceAndAppFromUUID = _.memoize(
|
||||
);
|
||||
if (app == null) {
|
||||
throw new ExpectedError(stripIndent`
|
||||
Unable to access the application that device ${deviceUUID} belongs to.
|
||||
Hint: check whether the application owner might have withdrawn access to it.
|
||||
Unable to access the fleet that device ${deviceUUID} belongs to.
|
||||
Hint: check whether the fleet owner withdrew access to it.
|
||||
`);
|
||||
}
|
||||
return [device, app];
|
||||
|
@ -16,9 +16,9 @@
|
||||
*/
|
||||
import { lowercaseIfSlug } from './normalization';
|
||||
|
||||
export const applicationRequired = {
|
||||
name: 'application',
|
||||
description: 'application name, slug (preferred), or numeric ID (deprecated)',
|
||||
export const fleetRequired = {
|
||||
name: 'fleet',
|
||||
description: 'fleet name, slug (preferred), or numeric ID (deprecated)',
|
||||
required: true,
|
||||
parse: lowercaseIfSlug,
|
||||
};
|
||||
|
@ -20,15 +20,33 @@ import { flags } from '@oclif/command';
|
||||
import type { IBooleanFlag } from '@oclif/parser/lib/flags';
|
||||
import { stripIndent } from './lazy';
|
||||
import { lowercaseIfSlug } from './normalization';
|
||||
import { isV13 } from './version';
|
||||
|
||||
export const v13: IBooleanFlag<boolean> = flags.boolean({
|
||||
description: stripIndent`\
|
||||
enable selected balena CLI v13 pre-release features, like the renaming
|
||||
from "application" to "fleet" in command output`,
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const application = flags.string({
|
||||
char: 'a',
|
||||
description: 'application name, slug (preferred), or numeric ID (deprecated)',
|
||||
description: 'DEPRECATED alias for -f, --fleet',
|
||||
parse: lowercaseIfSlug,
|
||||
});
|
||||
// TODO: Consider remove second alias 'app' when we can, to simplify.
|
||||
export const app = flags.string({
|
||||
description: "same as '--application'",
|
||||
description: 'DEPRECATED alias for -f, --fleet',
|
||||
parse: lowercaseIfSlug,
|
||||
});
|
||||
export const fleet = flags.string({
|
||||
char: 'f',
|
||||
description: isV13()
|
||||
? 'fleet name, slug (preferred), or numeric ID (deprecated)'
|
||||
: // avoid the '(deprecated)' remark in v12 while cf.application and
|
||||
// cf.app are also described as deprecated, to avoid the impression
|
||||
// that cf.fleet is deprecated as well.
|
||||
'fleet name, slug (preferred), or numeric ID',
|
||||
parse: lowercaseIfSlug,
|
||||
});
|
||||
|
||||
|
@ -824,16 +824,16 @@ function printDockerignoreWarn(
|
||||
);
|
||||
if (multiDockerignore) {
|
||||
msg.push(stripIndent`
|
||||
When --multi-dockerignore (-m) is used, only .dockerignore files at the root of
|
||||
each service's build context (in a microservices/multicontainer application),
|
||||
plus a .dockerignore file at the overall project root, are used.
|
||||
When --multi-dockerignore (-m) is used, only .dockerignore files at the
|
||||
root of each service's build context (in a microservices/multicontainer
|
||||
fleet), plus a .dockerignore file at the overall project root, are used.
|
||||
See "balena help ${Logger.command}" for more details.`);
|
||||
} else {
|
||||
msg.push(stripIndent`
|
||||
By default, only one .dockerignore file at the source folder (project root)
|
||||
is used. Microservices (multicontainer) applications may use a separate
|
||||
.dockerignore file for each service with the --multi-dockerignore (-m) option.
|
||||
See "balena help ${Logger.command}" for more details.`);
|
||||
By default, only one .dockerignore file at the source folder (project
|
||||
root) is used. Microservices (multicontainer) fleets may use a separate
|
||||
.dockerignore file for each service with the --multi-dockerignore (-m)
|
||||
option. See "balena help ${Logger.command}" for more details.`);
|
||||
}
|
||||
}
|
||||
// No unused .dockerignore files. Print info-level advice in some cases.
|
||||
@ -853,7 +853,7 @@ function printDockerignoreWarn(
|
||||
msg.push(
|
||||
stripIndent`
|
||||
The --multi-dockerignore (-m) option was specified, but it has no effect for
|
||||
single-container (non-microservices) apps. Only one .dockerignore file at the
|
||||
single-container (non-microservices) fleets. Only one .dockerignore file at the
|
||||
project source (root) directory, if any, is used. See "balena help ${Logger.command}".`,
|
||||
);
|
||||
}
|
||||
|
@ -32,8 +32,7 @@ export const booleanConfig: IBooleanFlag<boolean> = flags.boolean({
|
||||
|
||||
export const booleanDevice: IBooleanFlag<boolean> = flags.boolean({
|
||||
char: 'd',
|
||||
description:
|
||||
'select a device-specific variable instead of an application (fleet) variable',
|
||||
description: 'select a device-specific variable instead of a fleet variable',
|
||||
default: false,
|
||||
});
|
||||
|
||||
@ -49,23 +48,23 @@ export const rmRenameHelp = stripIndent`
|
||||
Variables are selected by their database ID (as reported by the 'balena envs'
|
||||
command) and one of six database "resource types":
|
||||
|
||||
- application (fleet) environment variable
|
||||
- application (fleet) configuration variable (--config)
|
||||
- application (fleet) service variable (--service)
|
||||
- fleet environment variable
|
||||
- fleet configuration variable (--config)
|
||||
- fleet service variable (--service)
|
||||
- device environment variable (--device)
|
||||
- device configuration variable (--device --config)
|
||||
- device service variable (--device --service)
|
||||
|
||||
The --device option selects a device-specific variable instead of an application
|
||||
(fleet) variable.
|
||||
The --device option selects a device-specific variable instead of a fleet
|
||||
variable.
|
||||
|
||||
The --config option selects a configuration variable. Configuration variable
|
||||
names typically start with the 'BALENA_' or 'RESIN_' prefixes and are used to
|
||||
configure balena platform features.
|
||||
|
||||
The --service option selects a service variable, which is an environment variable
|
||||
that applies to a specifc service (application container) in a microservices
|
||||
(multicontainer) application.
|
||||
that applies to a specifc service (container) in a microservices (multicontainer)
|
||||
fleet.
|
||||
|
||||
The --service and --config options cannot be used together, but they can be
|
||||
used alongside the --device option to select a device-specific service or
|
||||
|
@ -160,7 +160,7 @@ export function getAppWithArch(
|
||||
});
|
||||
|
||||
if (!config) {
|
||||
throw new Error('Could not read application information!');
|
||||
throw new Error(`balena API request failed for fleet ${applicationName}`);
|
||||
}
|
||||
|
||||
return { ...app, arch: config.arch };
|
||||
@ -530,3 +530,31 @@ export async function awaitInterruptibleTask<
|
||||
process.removeListener('SIGINT', sigintHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick object fields like lodash _.pick(), but also interpret "renaming
|
||||
* specifications" for object keys such as "appName => fleetName" as used
|
||||
* by the 'resin-cli-visuals' package.
|
||||
*
|
||||
* Sample input: ({ 'a': 1, 'b': 2, 'c': 3 }, ['b => x', 'c', 'd'])
|
||||
* Sample output: { 'x': 2, 'c': 3 }
|
||||
*/
|
||||
export function pickAndRename<T extends Dictionary<any>>(
|
||||
obj: T,
|
||||
fields: string[],
|
||||
): Dictionary<any> {
|
||||
const rename: Dictionary<any> = {};
|
||||
// map 'a => b' to 'a' and setup rename['a'] = 'b'
|
||||
fields = fields.map((f) => {
|
||||
let renameFrom = f;
|
||||
let renameTo = f;
|
||||
const match = f.match(/(?<from>\S+)\s+=>\s+(?<to>\S+)/);
|
||||
if (match && match.groups) {
|
||||
renameFrom = match.groups.from;
|
||||
renameTo = match.groups.to;
|
||||
}
|
||||
rename[renameFrom] = renameTo;
|
||||
return renameFrom;
|
||||
});
|
||||
return _.mapKeys(_.pick(obj, fields), (_val, key) => rename[key]);
|
||||
}
|
||||
|
@ -30,6 +30,24 @@ export const help = reachingOut;
|
||||
// is parsed, so its evaluation cannot happen at module loading time.
|
||||
export const getHelp = () => (process.env.DEBUG ? '' : debugHint) + help;
|
||||
|
||||
/**
|
||||
* Take a multiline string like:
|
||||
* Line One
|
||||
* Line Two
|
||||
* and return a string like:
|
||||
* ---------------
|
||||
* [Warn] Line One
|
||||
* [Warn] Line Two
|
||||
* ---------------
|
||||
* where the length of the dash rows matches the length of the longest line.
|
||||
*/
|
||||
export function warnify(msg: string) {
|
||||
const lines = msg.split('\n').map((l) => `[Warn] ${l}`);
|
||||
const maxLength = Math.max(...lines.map((l) => l.length));
|
||||
const hr = '-'.repeat(maxLength);
|
||||
return [hr, ...lines, hr].join('\n');
|
||||
}
|
||||
|
||||
export const balenaAsciiArt = `\
|
||||
_ _
|
||||
| |__ __ _ | | ____ _ __ __ _
|
||||
@ -66,12 +84,12 @@ export const dockerignoreHelp =
|
||||
`By default, the balena CLI will use a single ".dockerignore" file (if any) at
|
||||
the project root (--source directory) in order to decide which source files to
|
||||
exclude from the "build context" (tar stream) sent to balenaCloud, Docker
|
||||
daemon or balenaEngine. In a microservices (multicontainer) application, the
|
||||
daemon or balenaEngine. In a microservices (multicontainer) fleet, the
|
||||
source directory is the directory that contains the "docker-compose.yml" file.
|
||||
|
||||
The --multi-dockerignore (-m) option may be used with microservices
|
||||
(multicontainer) applications that define a docker-compose.yml file. When
|
||||
this option is used, each service subdirectory (defined by the \`build\` or
|
||||
(multicontainer) fleets that define a docker-compose.yml file. When this
|
||||
option is used, each service subdirectory (defined by the \`build\` or
|
||||
\`build.context\` service properties in the docker-compose.yml file) is
|
||||
filtered separately according to a .dockerignore file defined in the service
|
||||
subdirectory. If no .dockerignore file exists in a service subdirectory, then
|
||||
@ -115,18 +133,17 @@ adding counter patterns to the applicable .dockerignore file(s), for example
|
||||
- https://www.npmjs.com/package/@balena/dockerignore`;
|
||||
|
||||
export const applicationIdInfo = `\
|
||||
Applications may be specified by app name, slug, or numeric ID. App slugs
|
||||
are the recommended option, as they are unique and unambiguous. Slugs
|
||||
can be listed with the \`balena apps\` command. Note that slugs may change
|
||||
if the application is renamed.
|
||||
App names are not unique and may result in "Application is ambiguous" errors
|
||||
at any time (even if it "used to work in the past"), for example if the name
|
||||
clashes with a newly created public application, or with apps from other balena
|
||||
accounts that you may have been invited to as a member. For this reason, app
|
||||
names are especially discouraged in scripts (e.g. CI environments).
|
||||
Numeric app IDs are deprecated because they consist of an implementation detail
|
||||
of the balena backend. We intend to remove support for numeric IDs at some point
|
||||
in the future.`;
|
||||
Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are
|
||||
the recommended option, as they are unique and unambiguous. Slugs can be
|
||||
listed with the \`balena fleets\` command. Note that slugs may change if the
|
||||
fleet is renamed. Fleet names are not unique and may result in "Fleet is
|
||||
ambiguous" errors at any time (even if it "used to work in the past"), for
|
||||
example if the name clashes with a newly created public 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). Numeric fleet IDs are deprecated because they consist of an
|
||||
implementation detail of the balena backend. We intend to remove support for
|
||||
numeric IDs at some point in the future.`;
|
||||
|
||||
export const jsonInfo = `\
|
||||
The --json option is recommended when scripting the output of this command,
|
||||
@ -143,3 +160,27 @@ https://www.balena.io/docs/learn/deploy/deployment/#build-time-secrets-and-varia
|
||||
If you have a particular use for buildArg, which is not satisfied by build-time
|
||||
secrets, please contact us via support or the forums: https://forums.balena.io/
|
||||
\n`;
|
||||
|
||||
// Note: if you edit this message, check that the regex replace
|
||||
// logic in lib/commands/apps.ts still works.
|
||||
export const appToFleetCmdMsg = `\
|
||||
Renaming notice: The 'app' command was renamed to 'fleet', and 'app'
|
||||
is now an alias. THE ALIAS WILL BE REMOVED in the next major version
|
||||
of the balena CLI (so that a different 'app' command can be implemented
|
||||
in the future). Use 'fleet' instead of 'app' to avoid this warning.
|
||||
Find out more at: https://git.io/JRuZr`;
|
||||
|
||||
export const appToFleetFlagMsg = `\
|
||||
Renaming notice: The '-a', '--app' or '--application' options are now
|
||||
aliases for the '-f' or '--fleet' options. THE ALIASES WILL BE REMOVED
|
||||
in the next major version of the balena CLI (so that a different '--app'
|
||||
option can be implemented in the future). Use '-f' or '--fleet' instead.
|
||||
Find out more at: https://git.io/JRuZr`;
|
||||
|
||||
export const appToFleetOutputMsg = `\
|
||||
Renaming notice: The 'app' or 'application' words in table headers
|
||||
or in JSON object keys/properties will be replaced with 'fleet' in
|
||||
the next major version of the CLI (v13). The --v13 option may be used
|
||||
to enable the new names already now, and suppress a warning message.
|
||||
(The --v13 option will be silently ignored in CLI v13.)
|
||||
Find out more at: https://git.io/JRuZr`;
|
||||
|
@ -184,7 +184,7 @@ export function selectApplication(
|
||||
.hasAny()
|
||||
.then(async (hasAnyApplications) => {
|
||||
if (!hasAnyApplications) {
|
||||
throw new ExpectedError("You don't have any applications");
|
||||
throw new ExpectedError('No fleets found');
|
||||
}
|
||||
|
||||
const apps = (await balena.models.application.getAll({
|
||||
@ -198,7 +198,7 @@ export function selectApplication(
|
||||
})
|
||||
.then((applications) => {
|
||||
if (errorOnEmptySelection && applications.length === 0) {
|
||||
throw new ExpectedError('No suitable applications found for selection');
|
||||
throw new ExpectedError('No suitable fleets found for selection');
|
||||
}
|
||||
return getCliForm().ask({
|
||||
message: 'Select an application',
|
||||
@ -378,15 +378,13 @@ export async function getOnlineTargetDeviceUuid(
|
||||
// Not a device UUID, try app
|
||||
let app: Application;
|
||||
try {
|
||||
logger.logDebug(
|
||||
`Trying to fetch application by name/slug/ID: ${applicationOrDevice}`,
|
||||
);
|
||||
logger.logDebug(`Fetching fleet ${applicationOrDevice}`);
|
||||
app = await getApplication(sdk, applicationOrDevice);
|
||||
} catch (err) {
|
||||
const { BalenaApplicationNotFound } = await import('balena-errors');
|
||||
if (instanceOf(err, BalenaApplicationNotFound)) {
|
||||
throw new ExpectedError(
|
||||
`Application or Device not found: ${applicationOrDevice}`,
|
||||
`Fleet or Device not found: ${applicationOrDevice}`,
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
@ -402,13 +400,13 @@ export async function getOnlineTargetDeviceUuid(
|
||||
// Throw if no devices online
|
||||
if (_.isEmpty(devices)) {
|
||||
throw new ExpectedError(
|
||||
`Application ${app.slug} found, but has no devices online.`,
|
||||
`Fleet ${app.slug} found, but has no devices online.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Ask user to select from online devices for application
|
||||
return getCliForm().ask({
|
||||
message: `Select a device on application ${app.slug}`,
|
||||
message: `Select a device on fleet ${app.slug}`,
|
||||
type: 'list',
|
||||
default: devices[0].uuid,
|
||||
choices: _.map(devices, (device) => ({
|
||||
|
@ -39,10 +39,10 @@ export async function join(
|
||||
const deviceType = await getDeviceType(deviceHostnameOrIp);
|
||||
logger.logDebug(`Device type: ${deviceType}`);
|
||||
|
||||
logger.logDebug('Determining application...');
|
||||
logger.logDebug('Determining fleet...');
|
||||
const app = await getOrSelectApplication(sdk, deviceType, appName);
|
||||
logger.logDebug(
|
||||
`Using application: ${app.app_name} (${app.is_for__device_type[0].slug})`,
|
||||
`Using fleet: ${app.app_name} (${app.is_for__device_type[0].slug})`,
|
||||
);
|
||||
if (app.is_for__device_type[0].slug !== deviceType) {
|
||||
logger.logDebug(`Forcing device type to: ${deviceType}`);
|
||||
@ -53,7 +53,7 @@ export async function join(
|
||||
const deviceOsVersion = await getOsVersion(deviceHostnameOrIp);
|
||||
logger.logDebug(`Device OS version: ${deviceOsVersion}`);
|
||||
|
||||
logger.logDebug('Generating application config...');
|
||||
logger.logDebug('Generating fleet config...');
|
||||
const config = await generateApplicationConfig(sdk, app, {
|
||||
version: deviceOsVersion,
|
||||
appUpdatePollInterval,
|
||||
@ -175,10 +175,10 @@ async function selectAppFromList(
|
||||
const _ = await import('lodash');
|
||||
const { selectFromList } = await import('../utils/patterns');
|
||||
|
||||
// Present a list to the user which shows the fully qualified application
|
||||
// name (user/appname) and allows them to select.
|
||||
// Present a list to the user which shows the fully qualified fleet
|
||||
// name (user/fleetname) and allows them to select.
|
||||
return selectFromList(
|
||||
'Select application',
|
||||
'Select fleet',
|
||||
_.map(applications, (app) => {
|
||||
return { name: app.slug, ...app };
|
||||
}),
|
||||
@ -220,11 +220,11 @@ async function getOrSelectApplication(
|
||||
},
|
||||
};
|
||||
|
||||
// Check for an app of the form `user/application` and update the API query.
|
||||
// Check for a fleet slug of the form `user/fleet` and update the API query.
|
||||
let name: string;
|
||||
const match = appName.split('/');
|
||||
if (match.length > 1) {
|
||||
// These will match at most one app
|
||||
// These will match at most one fleet
|
||||
options.$filter = { slug: appName.toLowerCase() };
|
||||
name = match[1];
|
||||
} else {
|
||||
@ -241,7 +241,7 @@ async function getOrSelectApplication(
|
||||
if (applications.length === 0) {
|
||||
const shouldCreateApp = await getCliForm().ask({
|
||||
message:
|
||||
`No application found with name "${appName}".\n` +
|
||||
`No fleet found with name "${appName}".\n` +
|
||||
'Would you like to create it now?',
|
||||
type: 'confirm',
|
||||
default: true,
|
||||
@ -252,14 +252,14 @@ async function getOrSelectApplication(
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// We've found at least one app with the given name.
|
||||
// Filter out apps for non-matching device types and see what we're left with.
|
||||
// We've found at least one fleet with the given name.
|
||||
// Filter out fleets for non-matching device types and see what we're left with.
|
||||
const validApplications = applications.filter((app) =>
|
||||
_.includes(compatibleDeviceTypes, app.is_for__device_type[0].slug),
|
||||
);
|
||||
|
||||
if (validApplications.length === 0) {
|
||||
throw new ExpectedError('No application found with a matching device type');
|
||||
throw new ExpectedError('No fleet found with a matching device type');
|
||||
}
|
||||
|
||||
if (validApplications.length === 1) {
|
||||
@ -277,7 +277,7 @@ async function createOrSelectAppOrExit(
|
||||
compatibleDeviceTypes: string[],
|
||||
deviceType: string,
|
||||
): Promise<ApplicationWithDeviceType> {
|
||||
// No application specified, show a list to select one.
|
||||
// No fleet specified, show a list to select one.
|
||||
const applications = (await sdk.models.application.getAll({
|
||||
$expand: { is_for__device_type: { $select: 'slug' } },
|
||||
$filter: {
|
||||
@ -293,7 +293,7 @@ async function createOrSelectAppOrExit(
|
||||
if (applications.length === 0) {
|
||||
const shouldCreateApp = await getCliForm().ask({
|
||||
message:
|
||||
'You have no applications this device can join.\n' +
|
||||
'You have no fleets this device can join.\n' +
|
||||
'Would you like to create one now?',
|
||||
type: 'confirm',
|
||||
default: true,
|
||||
@ -323,7 +323,7 @@ async function createApplication(
|
||||
while (true) {
|
||||
try {
|
||||
const appName = await getCliForm().ask({
|
||||
message: 'Enter a name for your new application:',
|
||||
message: 'Enter a name for your new fleet:',
|
||||
type: 'input',
|
||||
default: name,
|
||||
validate: validation.validateApplicationName,
|
||||
@ -341,7 +341,7 @@ async function createApplication(
|
||||
// TODO: This is the only example in the codebase where `printErrorMessage()`
|
||||
// is called directly. Consider refactoring.
|
||||
printErrorMessage(
|
||||
'You already have an application with that name; please choose another.',
|
||||
'You already have a fleet with that name; please choose another.',
|
||||
);
|
||||
continue;
|
||||
} catch (err) {
|
||||
|
@ -43,7 +43,7 @@ export function validatePassword(input: string) {
|
||||
|
||||
export function validateApplicationName(input: string) {
|
||||
if (input.length < 4) {
|
||||
return 'The application name should be at least 4 characters';
|
||||
return 'The fleet name should be at least 4 characters long';
|
||||
}
|
||||
|
||||
return APPNAME_REGEX.test(input);
|
||||
|
@ -19,27 +19,7 @@ import { expect } from 'chai';
|
||||
import { BalenaAPIMock } from '../../balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
const HELP_MESSAGE = `
|
||||
Usage: app create <name>
|
||||
|
||||
Use this command to create a new balena application.
|
||||
|
||||
You can specify the application device type with the \`--type\` option.
|
||||
Otherwise, an interactive dropdown will be shown for you to select from.
|
||||
|
||||
You can see a list of supported device types with
|
||||
|
||||
\t$ balena devices supported
|
||||
|
||||
Examples:
|
||||
|
||||
\t$ balena app create MyApp
|
||||
\t$ balena app create MyApp --type raspberry-pi
|
||||
|
||||
Options:
|
||||
|
||||
--type, -t <type> application device type (Check available types with \`balena devices supported\`)
|
||||
`;
|
||||
const HELP_MESSAGE = '';
|
||||
|
||||
describe('balena app create', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
@ -29,6 +29,8 @@ import { cleanOutput, runCommand } from '../helpers';
|
||||
import {
|
||||
ExpectedTarStreamFiles,
|
||||
ExpectedTarStreamFilesByService,
|
||||
getDockerignoreWarn1,
|
||||
getDockerignoreWarn2,
|
||||
} from '../projects';
|
||||
|
||||
const repoPath = path.normalize(path.join(__dirname, '..', '..'));
|
||||
@ -230,16 +232,10 @@ describe('balena build', function () {
|
||||
`[Info] Creating default composition with source: "${projectPath}"`,
|
||||
'[Info] Building for rpi/raspberry-pi',
|
||||
'[Info] Emulation is enabled',
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`,
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project root)',
|
||||
'[Warn] is used. Microservices (multicontainer) applications may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.',
|
||||
'[Warn] See "balena help build" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn1(
|
||||
[path.join(projectPath, 'src', '.dockerignore')],
|
||||
'build',
|
||||
),
|
||||
'[Build] main Step 1/4 : FROM busybox',
|
||||
'[Success] Build succeeded!',
|
||||
];
|
||||
@ -315,16 +311,10 @@ describe('balena build', function () {
|
||||
...commonResponseLines[responseFilename],
|
||||
`[Info] No "docker-compose.yml" file found at "${projectPath}"`,
|
||||
`[Info] Creating default composition with source: "${projectPath}"`,
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`,
|
||||
'[Warn] When --multi-dockerignore (-m) is used, only .dockerignore files at the root of',
|
||||
"[Warn] each service's build context (in a microservices/multicontainer application),",
|
||||
'[Warn] plus a .dockerignore file at the overall project root, are used.',
|
||||
'[Warn] See "balena help build" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn2(
|
||||
[path.join(projectPath, 'src', '.dockerignore')],
|
||||
'build',
|
||||
),
|
||||
'[Build] main Step 1/4 : FROM busybox',
|
||||
];
|
||||
if (isWindows) {
|
||||
@ -410,16 +400,10 @@ describe('balena build', function () {
|
||||
'[Build] service1 Step 1/4 : FROM busybox',
|
||||
'[Build] service2 Step 1/4 : FROM busybox',
|
||||
],
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'service2', '.dockerignore')}`,
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project root)',
|
||||
'[Warn] is used. Microservices (multicontainer) applications may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.',
|
||||
'[Warn] See "balena help build" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn1(
|
||||
[path.join(projectPath, 'service2', '.dockerignore')],
|
||||
'build',
|
||||
),
|
||||
];
|
||||
if (isWindows) {
|
||||
expectedResponseLines.push(
|
||||
|
@ -28,6 +28,7 @@ import { cleanOutput, runCommand, switchSentry } from '../helpers';
|
||||
import {
|
||||
ExpectedTarStreamFiles,
|
||||
ExpectedTarStreamFilesByService,
|
||||
getDockerignoreWarn1,
|
||||
} from '../projects';
|
||||
|
||||
const repoPath = path.normalize(path.join(__dirname, '..', '..'));
|
||||
@ -124,16 +125,10 @@ describe('balena deploy', function () {
|
||||
...commonResponseLines[responseFilename],
|
||||
`[Info] No "docker-compose.yml" file found at "${projectPath}"`,
|
||||
`[Info] Creating default composition with source: "${projectPath}"`,
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`,
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project root)',
|
||||
'[Warn] is used. Microservices (multicontainer) applications may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.',
|
||||
'[Warn] See "balena help deploy" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn1(
|
||||
[path.join(projectPath, 'src', '.dockerignore')],
|
||||
'deploy',
|
||||
),
|
||||
];
|
||||
if (isWindows) {
|
||||
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
||||
|
@ -21,24 +21,8 @@ import * as path from 'path';
|
||||
import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
const HELP_RESPONSE = `
|
||||
Show info about a single device.
|
||||
|
||||
USAGE
|
||||
$ balena device <uuid>
|
||||
|
||||
ARGUMENTS
|
||||
<uuid> the device uuid
|
||||
|
||||
OPTIONS
|
||||
-h, --help show CLI help
|
||||
|
||||
DESCRIPTION
|
||||
Show information about a single device.
|
||||
|
||||
EXAMPLE
|
||||
$ balena device 7cf02a6
|
||||
`;
|
||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
||||
import { isV13 } from '../../../build/utils/version';
|
||||
|
||||
describe('balena device', function () {
|
||||
let api: BalenaAPIMock;
|
||||
@ -54,13 +38,12 @@ describe('balena device', function () {
|
||||
api.done();
|
||||
});
|
||||
|
||||
it('should print help text with the -h flag', async () => {
|
||||
const { out, err } = await runCommand('device -h');
|
||||
|
||||
expect(cleanOutput(out)).to.deep.equal(cleanOutput([HELP_RESPONSE]));
|
||||
|
||||
expect(err).to.eql([]);
|
||||
});
|
||||
const expectedWarn =
|
||||
!isV13() &&
|
||||
process.stderr.isTTY &&
|
||||
process.env.BALENA_CLI_TEST_TYPE !== 'standalone'
|
||||
? warnify(appToFleetOutputMsg) + '\n'
|
||||
: '';
|
||||
|
||||
it('should error if uuid not provided', async () => {
|
||||
const { out, err } = await runCommand('device');
|
||||
@ -88,7 +71,7 @@ describe('balena device', function () {
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('test app');
|
||||
|
||||
expect(err).to.eql([]);
|
||||
expect(err.join('')).to.eql(expectedWarn);
|
||||
});
|
||||
|
||||
it.skip('correctly handles devices with missing fields', async () => {
|
||||
@ -112,7 +95,7 @@ describe('balena device', function () {
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('test app');
|
||||
|
||||
expect(err).to.eql([]);
|
||||
expect(err.join('')).to.eql(expectedWarn);
|
||||
});
|
||||
|
||||
it('correctly handles devices with missing application', async () => {
|
||||
@ -138,6 +121,6 @@ describe('balena device', function () {
|
||||
expect(lines[0]).to.equal('== SPARKLING WOOD');
|
||||
expect(lines[6].split(':')[1].trim()).to.equal('N/a');
|
||||
|
||||
expect(err).to.eql([]);
|
||||
expect(err.join('')).to.eql(expectedWarn);
|
||||
});
|
||||
});
|
||||
|
@ -21,6 +21,9 @@ import * as path from 'path';
|
||||
import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
import { appToFleetOutputMsg, warnify } from '../../../build/utils/messages';
|
||||
import { isV13 } from '../../../build/utils/version';
|
||||
|
||||
describe('balena devices', function () {
|
||||
let api: BalenaAPIMock;
|
||||
|
||||
@ -35,6 +38,13 @@ describe('balena devices', function () {
|
||||
api.done();
|
||||
});
|
||||
|
||||
const expectedWarn =
|
||||
!isV13() &&
|
||||
process.stderr.isTTY &&
|
||||
process.env.BALENA_CLI_TEST_TYPE !== 'standalone'
|
||||
? warnify(appToFleetOutputMsg) + '\n'
|
||||
: '';
|
||||
|
||||
it('should list devices from own and collaborator apps', async () => {
|
||||
api.scope
|
||||
.get(
|
||||
@ -60,6 +70,6 @@ describe('balena devices', function () {
|
||||
// e.g. When user has a device associated with app that user is no longer a collaborator of.
|
||||
expect(lines.some((l) => l.includes('N/a'))).to.be.true;
|
||||
|
||||
expect(err).to.eql([]);
|
||||
expect(err.join('')).to.eql(expectedWarn);
|
||||
});
|
||||
});
|
||||
|
53
tests/commands/env/envs.spec.ts
vendored
53
tests/commands/env/envs.spec.ts
vendored
@ -21,6 +21,13 @@ import { stripIndent } from '../../../lib/utils/lazy';
|
||||
import { BalenaAPIMock } from '../../balena-api-mock';
|
||||
import { runCommand } from '../../helpers';
|
||||
|
||||
import {
|
||||
appToFleetFlagMsg,
|
||||
appToFleetOutputMsg,
|
||||
warnify,
|
||||
} from '../../../build/utils/messages';
|
||||
import { isV13 } from '../../../build/utils/version';
|
||||
|
||||
describe('balena envs', function () {
|
||||
const appName = 'test';
|
||||
let fullUUID: string;
|
||||
@ -41,7 +48,21 @@ describe('balena envs', function () {
|
||||
api.done();
|
||||
});
|
||||
|
||||
it('should successfully list env vars for a test app', async () => {
|
||||
const appToFleetFlagWarn =
|
||||
!isV13() &&
|
||||
process.stderr.isTTY &&
|
||||
process.env.BALENA_CLI_TEST_TYPE !== 'standalone'
|
||||
? warnify(appToFleetFlagMsg) + '\n'
|
||||
: '';
|
||||
|
||||
const appToFleetOutputWarn =
|
||||
!isV13() &&
|
||||
process.stderr.isTTY &&
|
||||
process.env.BALENA_CLI_TEST_TYPE !== 'standalone'
|
||||
? warnify(appToFleetOutputMsg) + '\n'
|
||||
: '';
|
||||
|
||||
it('should successfully list env vars for a test fleet', async () => {
|
||||
api.expectGetApplication();
|
||||
api.expectGetAppEnvVars();
|
||||
api.expectGetAppServiceVars();
|
||||
@ -57,18 +78,18 @@ describe('balena envs', function () {
|
||||
120102 var2 22 test *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetFlagWarn);
|
||||
});
|
||||
|
||||
it('should successfully list config vars for a test app', async () => {
|
||||
it('should successfully list config vars for a test fleet', async () => {
|
||||
api.expectGetApplication();
|
||||
api.expectGetAppConfigVars();
|
||||
|
||||
const { out, err } = await runCommand(`envs -a ${appName} --config`);
|
||||
const { out, err } = await runCommand(`envs -f ${appName} --config`);
|
||||
|
||||
expect(out.join('')).to.equal(
|
||||
stripIndent`
|
||||
ID NAME VALUE APPLICATION
|
||||
ID NAME VALUE FLEET
|
||||
120300 RESIN_SUPERVISOR_NATIVE_LOGGER false test
|
||||
` + '\n',
|
||||
);
|
||||
@ -76,7 +97,7 @@ describe('balena envs', function () {
|
||||
expect(err.join('')).to.equal('');
|
||||
});
|
||||
|
||||
it('should successfully list config vars for a test app (JSON output)', async () => {
|
||||
it('should successfully list config vars for a test fleet (JSON output)', async () => {
|
||||
api.expectGetApplication();
|
||||
api.expectGetAppConfigVars();
|
||||
|
||||
@ -93,7 +114,7 @@ describe('balena envs', function () {
|
||||
expect(err.join('')).to.equal('');
|
||||
});
|
||||
|
||||
it('should successfully list service variables for a test app (-s flag)', async () => {
|
||||
it('should successfully list service variables for a test fleet (-s flag)', async () => {
|
||||
const serviceName = 'service2';
|
||||
api.expectGetService({ serviceName });
|
||||
api.expectGetApplication();
|
||||
@ -112,10 +133,10 @@ describe('balena envs', function () {
|
||||
120102 var2 22 test *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetFlagWarn);
|
||||
});
|
||||
|
||||
it('should successfully list env and service vars for a test app (-s flags)', async () => {
|
||||
it('should successfully list env and service vars for a test fleet (-s flags)', async () => {
|
||||
const serviceName = 'service1';
|
||||
api.expectGetService({ serviceName });
|
||||
api.expectGetApplication();
|
||||
@ -134,7 +155,7 @@ describe('balena envs', function () {
|
||||
120102 var2 22 test *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetFlagWarn);
|
||||
});
|
||||
|
||||
it('should successfully list env variables for a test device', async () => {
|
||||
@ -162,7 +183,7 @@ describe('balena envs', function () {
|
||||
` + '\n',
|
||||
);
|
||||
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
||||
});
|
||||
|
||||
it('should successfully list env variables for a test device (JSON output)', async () => {
|
||||
@ -207,7 +228,7 @@ describe('balena envs', function () {
|
||||
` + '\n',
|
||||
);
|
||||
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
||||
});
|
||||
|
||||
it('should successfully list service variables for a test device (-s flag)', async () => {
|
||||
@ -235,10 +256,10 @@ describe('balena envs', function () {
|
||||
` + '\n',
|
||||
);
|
||||
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
||||
});
|
||||
|
||||
it('should successfully list env and service variables for a test device (unknown app)', async () => {
|
||||
it('should successfully list env and service variables for a test device (unknown fleet)', async () => {
|
||||
api.expectGetDevice({ fullUUID, inaccessibleApp: true });
|
||||
api.expectGetDeviceEnvVars();
|
||||
api.expectGetDeviceServiceVars();
|
||||
@ -256,7 +277,7 @@ describe('balena envs', function () {
|
||||
120204 var4 44 N/A ${uuid} *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
||||
});
|
||||
|
||||
it('should successfully list env and service vars for a test device (-s flags)', async () => {
|
||||
@ -283,7 +304,7 @@ describe('balena envs', function () {
|
||||
120204 var4 44 test ${uuid} *
|
||||
` + '\n',
|
||||
);
|
||||
expect(err.join('')).to.equal('');
|
||||
expect(err.join('')).to.equal(appToFleetOutputWarn);
|
||||
});
|
||||
|
||||
it('should successfully list env and service vars for a test device (-js flags)', async () => {
|
||||
|
@ -55,7 +55,7 @@ if (process.platform !== 'win32') {
|
||||
`os configure ${tmpPath}`,
|
||||
'--device-type raspberrypi3',
|
||||
'--version 2.47.0+rev1',
|
||||
'--application testApp',
|
||||
'--fleet testApp',
|
||||
'--config-app-update-poll-interval 10',
|
||||
'--config-network ethernet',
|
||||
'--initial-device-name testDeviceName',
|
||||
|
@ -26,6 +26,8 @@ import { cleanOutput, runCommand } from '../helpers';
|
||||
import {
|
||||
addRegSecretsEntries,
|
||||
ExpectedTarStreamFiles,
|
||||
getDockerignoreWarn1,
|
||||
getDockerignoreWarn2,
|
||||
setupDockerignoreTestData,
|
||||
} from '../projects';
|
||||
|
||||
@ -124,16 +126,10 @@ describe('balena push', function () {
|
||||
);
|
||||
const expectedResponseLines = [
|
||||
...commonResponseLines[responseFilename],
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`,
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project root)',
|
||||
'[Warn] is used. Microservices (multicontainer) applications may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.',
|
||||
'[Warn] See "balena help push" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn1(
|
||||
[path.join(projectPath, 'src', '.dockerignore')],
|
||||
'push',
|
||||
),
|
||||
];
|
||||
if (isWindows) {
|
||||
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
||||
@ -171,16 +167,10 @@ describe('balena push', function () {
|
||||
);
|
||||
const expectedResponseLines = [
|
||||
...commonResponseLines[responseFilename],
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`,
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project root)',
|
||||
'[Warn] is used. Microservices (multicontainer) applications may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.',
|
||||
'[Warn] See "balena help push" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn1(
|
||||
[path.join(projectPath, 'src', '.dockerignore')],
|
||||
'push',
|
||||
),
|
||||
];
|
||||
const expectedQueryParams = commonQueryParams.map((i) =>
|
||||
i[0] === 'dockerfilePath' ? ['dockerfilePath', 'Dockerfile-alt'] : i,
|
||||
@ -218,16 +208,10 @@ describe('balena push', function () {
|
||||
);
|
||||
const expectedResponseLines = [
|
||||
...commonResponseLines[responseFilename],
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'src', '.dockerignore')}`,
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project root)',
|
||||
'[Warn] is used. Microservices (multicontainer) applications may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.',
|
||||
'[Warn] See "balena help push" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn1(
|
||||
[path.join(projectPath, 'src', '.dockerignore')],
|
||||
'push',
|
||||
),
|
||||
];
|
||||
if (isWindows) {
|
||||
const fname = path.join(projectPath, 'src', 'windows-crlf.sh');
|
||||
@ -434,16 +418,10 @@ describe('balena push', function () {
|
||||
'utf8',
|
||||
);
|
||||
const expectedResponseLines: string[] = [
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'lib', '.dockerignore')}`,
|
||||
'[Warn] When --multi-dockerignore (-m) is used, only .dockerignore files at the root of',
|
||||
"[Warn] each service's build context (in a microservices/multicontainer application),",
|
||||
'[Warn] plus a .dockerignore file at the overall project root, are used.',
|
||||
'[Warn] See "balena help push" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn2(
|
||||
[path.join(projectPath, 'lib', '.dockerignore')],
|
||||
'push',
|
||||
),
|
||||
...commonResponseLines[responseFilename],
|
||||
];
|
||||
|
||||
@ -484,16 +462,10 @@ describe('balena push', function () {
|
||||
);
|
||||
const expectedResponseLines: string[] = [
|
||||
...commonResponseLines[responseFilename],
|
||||
...[
|
||||
`[Warn] ${hr}`,
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
`[Warn] * ${path.join(projectPath, 'service2', '.dockerignore')}`,
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project root)',
|
||||
'[Warn] is used. Microservices (multicontainer) applications may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m) option.',
|
||||
'[Warn] See "balena help push" for more details.',
|
||||
`[Warn] ${hr}`,
|
||||
],
|
||||
...getDockerignoreWarn1(
|
||||
[path.join(projectPath, 'service2', '.dockerignore')],
|
||||
'push',
|
||||
),
|
||||
];
|
||||
if (isWindows) {
|
||||
expectedResponseLines.push(
|
||||
|
@ -83,3 +83,39 @@ export async function addRegSecretsEntries(
|
||||
};
|
||||
return regSecretsPath;
|
||||
}
|
||||
|
||||
export function getDockerignoreWarn1(paths: string[], cmd: string) {
|
||||
const lines = [
|
||||
'[Warn] ----------------------------------------------------------------------',
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
];
|
||||
lines.push(...paths.map((p) => `[Warn] * ${p}`));
|
||||
lines.push(
|
||||
...[
|
||||
'[Warn] By default, only one .dockerignore file at the source folder (project',
|
||||
'[Warn] root) is used. Microservices (multicontainer) fleets may use a separate',
|
||||
'[Warn] .dockerignore file for each service with the --multi-dockerignore (-m)',
|
||||
`[Warn] option. See "balena help ${cmd}" for more details.`,
|
||||
'[Warn] ----------------------------------------------------------------------',
|
||||
],
|
||||
);
|
||||
return lines;
|
||||
}
|
||||
|
||||
export function getDockerignoreWarn2(paths: string[], cmd: string) {
|
||||
const lines = [
|
||||
'[Warn] ----------------------------------------------------------------------',
|
||||
'[Warn] The following .dockerignore file(s) will not be used:',
|
||||
];
|
||||
lines.push(...paths.map((p) => `[Warn] * ${p}`));
|
||||
lines.push(
|
||||
...[
|
||||
'[Warn] When --multi-dockerignore (-m) is used, only .dockerignore files at the',
|
||||
"[Warn] root of each service's build context (in a microservices/multicontainer",
|
||||
'[Warn] fleet), plus a .dockerignore file at the overall project root, are used.',
|
||||
`[Warn] See "balena help ${cmd}" for more details.`,
|
||||
'[Warn] ----------------------------------------------------------------------',
|
||||
],
|
||||
);
|
||||
return lines;
|
||||
}
|
||||
|
@ -56,20 +56,20 @@ describe('validatePassword() function', () => {
|
||||
});
|
||||
|
||||
describe('validateApplicationName() function', () => {
|
||||
it('should reject applications names shorter than 4 characters, with a message', () => {
|
||||
const errorMessage = 'The application name should be at least 4 characters';
|
||||
it('should reject fleet names shorter than 4 characters, with a message', () => {
|
||||
const errorMessage = 'The fleet name should be at least 4 characters long';
|
||||
expect(v.validateApplicationName('abc')).to.equal(errorMessage);
|
||||
expect(v.validateApplicationName('')).to.equal(errorMessage);
|
||||
});
|
||||
|
||||
it('should return false for application names with characters other than `a-z,A-Z,0-9,_-`', () => {
|
||||
it('should return false for fleet names with characters other than `a-z,A-Z,0-9,_-`', () => {
|
||||
expect(v.validateApplicationName('abcd.')).to.equal(false);
|
||||
expect(v.validateApplicationName('abcd$')).to.equal(false);
|
||||
expect(v.validateApplicationName('ab cd')).to.equal(false);
|
||||
expect(v.validateApplicationName('(abcd)')).to.equal(false);
|
||||
});
|
||||
|
||||
it('should return true for valid application names', () => {
|
||||
it('should return true for valid fleet names', () => {
|
||||
expect(v.validateApplicationName('Test-Application1')).to.equal(true);
|
||||
expect(v.validateApplicationName('test_application2')).to.equal(true);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user