diff --git a/doc/cli.markdown b/doc/cli.markdown index f95561fd..f2ac66bb 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -2734,20 +2734,20 @@ answer "yes" to all questions (non interactive use) ## build [source] Use this command to build an image or a complete multicontainer project with -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.) + 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 provide either an application or a device-type/architecture pair. -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, -each service defined in the compose file will be built. If a compose file isn't -found, it will look for a Dockerfile[.template] file (or alternative Dockerfile -specified with the `--dockerfile` option), and if no dockerfile is found, it -will try to generate one. + 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, + each service defined in the compose file will be built. If a compose file isn't + found, it will look for a Dockerfile[.template] file (or alternative Dockerfile + specified with the `--dockerfile` option), and if no dockerfile is found, it + will try to generate one. -REGISTRY SECRETS + REGISTRY SECRETS The --registry-secrets option specifies a JSON or YAML file containing private Docker registry usernames and passwords to be used when pulling base images. Sample registry-secrets YAML file: @@ -2767,9 +2767,11 @@ check: https://github.com/balena-io-examples/sample-gcr-registry-secrets If the --registry-secrets option is not specified, and a secrets.yml or secrets.json file exists in the balena directory (usually $HOME/.balena), -this file will be used instead. +this file will be used instead..split(' +').join(' + ')} -DOCKERIGNORE AND GITIGNORE FILES + DOCKERIGNORE AND GITIGNORE FILES 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 @@ -2819,7 +2821,9 @@ project. If necessary, the effect of the `**/.git` pattern may be modified by adding counter patterns to the applicable .dockerignore file(s), for example `!mysubmodule/.git`. For documentation on pattern format, see: - https://docs.docker.com/engine/reference/builder/#dockerignore-file -- https://www.npmjs.com/package/@balena/dockerignore +- https://www.npmjs.com/package/@balena/dockerignore.split(' +').join(' + ')} Examples: @@ -2848,7 +2852,7 @@ the type of device this build is for #### -a, --application APPLICATION -name of the target balena application this build is for +name or slug of the target application this build is for #### -e, --emulated diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 7a0364f4..1e98f751 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -17,7 +17,7 @@ import { flags } from '@oclif/command'; import Command from '../command'; -import { getBalenaSdk } from '../utils/lazy'; +import { getBalenaSdk, stripIndent } from '../utils/lazy'; import * as compose from '../utils/compose'; import type { Application, ApplicationType, BalenaSDK } from 'balena-sdk'; import { dockerignoreHelp, registrySecretsHelp } from '../utils/messages'; @@ -25,6 +25,7 @@ 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 { lowercaseIfSlug } from '../utils/normalization'; interface FlagsDef extends ComposeCliFlags, DockerCliFlags { arch?: string; @@ -39,27 +40,28 @@ interface ArgsDef { } export default class BuildCmd extends Command { - public static description = `\ -Build a project locally. + public static description = stripIndent` + Build a project locally. -Use this command to build an image or a complete multicontainer project with -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.) + Use this command to build an image or a complete multicontainer project with + 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 provide either an application or a device-type/architecture pair. -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, -each service defined in the compose file will be built. If a compose file isn't -found, it will look for a Dockerfile[.template] file (or alternative Dockerfile -specified with the \`--dockerfile\` option), and if no dockerfile is found, it -will try to generate one. + 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, + each service defined in the compose file will be built. If a compose file isn't + found, it will look for a Dockerfile[.template] file (or alternative Dockerfile + specified with the \`--dockerfile\` option), and if no dockerfile is found, it + will try to generate one. -${registrySecretsHelp} + ${registrySecretsHelp}.split('\n').join('\n\t\t')} + + ${dockerignoreHelp}.split('\n').join('\n\t\t')} + `; -${dockerignoreHelp} -`; public static examples = [ '$ balena build --application myApp', '$ balena build ./source/ --application myApp', @@ -88,8 +90,9 @@ ${dockerignoreHelp} char: 'd', }), application: flags.string({ - description: 'name of the target balena application this build is for', + description: 'name or slug of the target application this build is for', char: 'a', + parse: lowercaseIfSlug, }), ...composeCliFlags, ...dockerCliFlags, @@ -122,7 +125,7 @@ ${dockerignoreHelp} await this.validateOptions(options, sdk); - const app = await this.getAppAndResolveArch(options); + const app = await this.getAppAndResolveArch(sdk, options); const { docker, buildOpts, composeOpts } = await this.prepareBuild(options); @@ -173,10 +176,10 @@ ${dockerignoreHelp} opts['registry-secrets'] = registrySecrets; } - protected async getAppAndResolveArch(opts: FlagsDef) { + protected async getAppAndResolveArch(sdk: BalenaSDK, opts: FlagsDef) { if (opts.application) { - const { getAppWithArch } = await import('../utils/helpers'); - const app = await getAppWithArch(opts.application); + const { getAppWithArch } = await import('../utils/sdk'); + const app = await getAppWithArch(sdk, opts.application); opts.arch = app.arch; opts.deviceType = app.is_for__device_type[0].slug; return app; diff --git a/lib/commands/push.ts b/lib/commands/push.ts index 492d2d3e..dec0f737 100644 --- a/lib/commands/push.ts +++ b/lib/commands/push.ts @@ -246,9 +246,9 @@ export default class PushCmd extends Command { }), gitignore: flags.boolean({ description: stripIndent` - Consider .gitignore files in addition to the .dockerignore file. This reverts - to the CLI v11 behavior/implementation (deprecated) if compatibility is - required until your project can be adapted.`, + Consider .gitignore files in addition to the .dockerignore file. This reverts + to the CLI v11 behavior/implementation (deprecated) if compatibility is + required until your project can be adapted.`, char: 'g', default: false, exclusive: ['multi-dockerignore'], diff --git a/lib/utils/sdk.ts b/lib/utils/sdk.ts index 8e965e88..09d8bb69 100644 --- a/lib/utils/sdk.ts +++ b/lib/utils/sdk.ts @@ -18,9 +18,12 @@ import type { Application, BalenaSDK, + DeviceType, Organization, PineOptions, } from 'balena-sdk'; +import { getBalenaSdk } from './lazy'; +import * as BalenaSdk from 'balena-sdk'; /** * Wraps the sdk application.get method, @@ -112,3 +115,28 @@ export async function getOwnOrganizations( $orderby: 'name asc', }); } + +export async function getAppWithArch( + sdk: BalenaSDK, + appNameOrSlug: string, +): Promise { + const [app, deviceTypes] = await Promise.all([ + getApplication(sdk, appNameOrSlug, { + $expand: { + is_for__device_type: { $select: 'slug' }, + }, + }) as Promise, + getBalenaSdk().models.config.getDeviceTypes(), + ]); + + const _ = await import('lodash'); + const config = _.find(deviceTypes, { + slug: (app.is_for__device_type as DeviceType[])[0].slug, + }); + + if (!config) { + throw new Error('Could not read application information!'); + } + + return { ...app, arch: config.arch }; +}