2020-02-15 20:20:56 +00:00
|
|
|
###*
|
|
|
|
# @license
|
|
|
|
# Copyright 2016-2020 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.
|
|
|
|
###
|
|
|
|
|
2017-03-29 11:03:40 +00:00
|
|
|
# Imported here because it's needed for the setup
|
|
|
|
# of this action
|
|
|
|
Promise = require('bluebird')
|
|
|
|
dockerUtils = require('../utils/docker')
|
2017-12-11 22:37:56 +00:00
|
|
|
compose = require('../utils/compose')
|
2019-02-27 18:01:47 +00:00
|
|
|
{ registrySecretsHelp } = require('../utils/messages')
|
2017-03-29 11:03:40 +00:00
|
|
|
|
2017-12-11 22:37:56 +00:00
|
|
|
###
|
|
|
|
Opts must be an object with the following keys:
|
2017-03-29 11:03:40 +00:00
|
|
|
|
2018-03-27 16:04:29 +00:00
|
|
|
app: the app this build is for (optional)
|
2017-12-11 22:37:56 +00:00
|
|
|
arch: the architecture to build for
|
|
|
|
deviceType: the device type to build for
|
|
|
|
buildEmulated
|
|
|
|
buildOpts: arguments to forward to docker build command
|
|
|
|
###
|
|
|
|
buildProject = (docker, logger, composeOpts, opts) ->
|
2020-02-15 20:20:56 +00:00
|
|
|
{ loadProject } = require('../utils/compose_ts')
|
|
|
|
Promise.resolve(loadProject(logger, composeOpts))
|
2017-12-11 22:37:56 +00:00
|
|
|
.then (project) ->
|
2018-03-27 16:04:29 +00:00
|
|
|
appType = opts.app?.application_type?[0]
|
|
|
|
if appType? and project.descriptors.length > 1 and not appType.supports_multicontainer
|
2018-03-19 18:58:57 +00:00
|
|
|
logger.logWarn(
|
|
|
|
'Target application does not support multiple containers.\n' +
|
|
|
|
'Continuing with build, but you will not be able to deploy.'
|
|
|
|
)
|
|
|
|
|
2017-12-11 22:37:56 +00:00
|
|
|
compose.buildProject(
|
|
|
|
docker
|
|
|
|
logger
|
|
|
|
project.path
|
|
|
|
project.name
|
|
|
|
project.composition
|
|
|
|
opts.arch
|
|
|
|
opts.deviceType
|
|
|
|
opts.buildEmulated
|
|
|
|
opts.buildOpts
|
|
|
|
composeOpts.inlineLogs
|
2020-01-29 11:10:59 +00:00
|
|
|
opts.convertEol
|
2017-12-11 22:37:56 +00:00
|
|
|
)
|
|
|
|
.then ->
|
2020-01-29 11:10:59 +00:00
|
|
|
logger.outputDeferredMessages()
|
2017-12-11 22:37:56 +00:00
|
|
|
logger.logSuccess('Build succeeded!')
|
|
|
|
.tapCatch (e) ->
|
|
|
|
logger.logError('Build failed')
|
2017-03-29 11:03:40 +00:00
|
|
|
|
|
|
|
module.exports =
|
|
|
|
signature: 'build [source]'
|
2017-12-11 22:37:56 +00:00
|
|
|
description: 'Build a single image or a multicontainer project locally'
|
|
|
|
primary: true
|
2019-02-27 18:01:47 +00:00
|
|
|
help: """
|
2019-04-11 11:49:19 +00:00
|
|
|
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.)
|
2017-03-29 11:03:40 +00:00
|
|
|
|
2019-04-11 11:49:19 +00:00
|
|
|
You must provide either an application or a device-type/architecture pair to use
|
|
|
|
the balena Dockerfile pre-processor (e.g. Dockerfile.template -> Dockerfile).
|
2017-03-29 11:03:40 +00:00
|
|
|
|
2017-12-11 22:37:56 +00:00
|
|
|
This command will look into the given source directory (or the current working
|
2019-04-11 11:49:19 +00:00
|
|
|
directory if one isn't specified) for a docker-compose.yml file. If it is found,
|
|
|
|
this command will build each service defined in the compose file. 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.
|
2017-12-11 22:37:56 +00:00
|
|
|
|
2019-02-27 18:01:47 +00:00
|
|
|
#{registrySecretsHelp}
|
|
|
|
|
2017-03-29 11:03:40 +00:00
|
|
|
Examples:
|
|
|
|
|
2018-10-19 14:38:50 +00:00
|
|
|
$ balena build
|
|
|
|
$ balena build ./source/
|
2018-12-03 13:10:54 +00:00
|
|
|
$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated
|
2018-10-19 14:38:50 +00:00
|
|
|
$ balena build --application MyApp ./source/
|
2019-10-07 01:25:18 +00:00
|
|
|
$ balena build --docker /var/run/docker.sock # Linux, Mac
|
|
|
|
$ balena build --docker //./pipe/docker_engine # Windows
|
2018-10-19 14:38:50 +00:00
|
|
|
$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem
|
2019-02-27 18:01:47 +00:00
|
|
|
"""
|
2017-12-11 22:37:56 +00:00
|
|
|
options: dockerUtils.appendOptions compose.appendOptions [
|
2017-03-29 11:03:40 +00:00
|
|
|
{
|
|
|
|
signature: 'arch'
|
|
|
|
parameter: 'arch'
|
|
|
|
description: 'The architecture to build for'
|
|
|
|
alias: 'A'
|
|
|
|
},
|
|
|
|
{
|
2017-05-29 12:59:25 +00:00
|
|
|
signature: 'deviceType'
|
2017-03-29 11:03:40 +00:00
|
|
|
parameter: 'deviceType'
|
|
|
|
description: 'The type of device this build is for'
|
|
|
|
alias: 'd'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
signature: 'application'
|
|
|
|
parameter: 'application'
|
2018-10-19 14:38:50 +00:00
|
|
|
description: 'The target balena application this build is for'
|
2017-03-29 11:03:40 +00:00
|
|
|
alias: 'a'
|
|
|
|
},
|
|
|
|
]
|
|
|
|
action: (params, options, done) ->
|
2017-12-11 22:37:56 +00:00
|
|
|
# compositions with many services trigger misleading warnings
|
|
|
|
require('events').defaultMaxListeners = 1000
|
2019-06-18 10:13:09 +00:00
|
|
|
|
|
|
|
sdk = (require('balena-sdk')).fromSharedOptions()
|
2020-02-15 20:20:56 +00:00
|
|
|
{ ExpectedError } = require('../errors')
|
|
|
|
{ validateProjectDirectory } = require('../utils/compose_ts')
|
2017-12-11 22:37:56 +00:00
|
|
|
helpers = require('../utils/helpers')
|
2017-06-28 16:30:37 +00:00
|
|
|
Logger = require('../utils/logger')
|
2017-03-29 11:03:40 +00:00
|
|
|
|
2019-09-11 18:34:43 +00:00
|
|
|
logger = Logger.getLogger()
|
2017-12-11 22:37:56 +00:00
|
|
|
logger.logDebug('Parsing input...')
|
|
|
|
|
2019-08-15 14:38:50 +00:00
|
|
|
# `build` accepts `[source]` as a parameter, but compose expects it
|
|
|
|
# as an option. swap them here
|
|
|
|
options.source ?= params.source
|
|
|
|
delete params.source
|
2019-02-26 13:32:27 +00:00
|
|
|
|
2020-01-29 11:10:59 +00:00
|
|
|
options.convertEol = options['convert-eol'] || false
|
|
|
|
delete options['convert-eol']
|
|
|
|
|
2020-02-15 20:20:56 +00:00
|
|
|
{ application, arch, deviceType } = options
|
2017-12-11 22:37:56 +00:00
|
|
|
|
2020-02-15 20:20:56 +00:00
|
|
|
Promise.try ->
|
2017-12-11 22:37:56 +00:00
|
|
|
if (not (arch? and deviceType?) and not application?) or (application? and (arch? or deviceType?))
|
2020-02-15 20:20:56 +00:00
|
|
|
throw new ExpectedError('You must specify either an application or an arch/deviceType pair to build for')
|
|
|
|
.then ->
|
|
|
|
validateProjectDirectory(sdk, {
|
|
|
|
dockerfilePath: options.dockerfile,
|
|
|
|
noParentCheck: options['noparent-check'] || false,
|
|
|
|
projectPath: options.source || '.',
|
|
|
|
registrySecretsPath: options['registry-secrets'],
|
|
|
|
})
|
|
|
|
.then ({ dockerfilePath, registrySecrets }) ->
|
|
|
|
options.dockerfile = dockerfilePath
|
|
|
|
options['registry-secrets'] = registrySecrets
|
2017-12-11 22:37:56 +00:00
|
|
|
|
|
|
|
if arch? and deviceType?
|
|
|
|
[ undefined, arch, deviceType ]
|
|
|
|
else
|
2018-03-19 18:58:05 +00:00
|
|
|
Promise.join(
|
|
|
|
helpers.getApplication(application)
|
|
|
|
helpers.getArchAndDeviceType(application)
|
|
|
|
(app, { arch, device_type }) ->
|
|
|
|
app.arch = arch
|
|
|
|
app.device_type = device_type
|
|
|
|
return app
|
|
|
|
)
|
2017-12-11 22:37:56 +00:00
|
|
|
.then (app) ->
|
2018-03-19 18:58:05 +00:00
|
|
|
[ app, app.arch, app.device_type ]
|
2017-12-11 22:37:56 +00:00
|
|
|
|
2018-03-19 18:58:05 +00:00
|
|
|
.then ([ app, arch, deviceType ]) ->
|
2017-12-11 22:37:56 +00:00
|
|
|
Promise.join(
|
|
|
|
dockerUtils.getDocker(options)
|
|
|
|
dockerUtils.generateBuildOpts(options)
|
|
|
|
compose.generateOpts(options)
|
|
|
|
(docker, buildOpts, composeOpts) ->
|
|
|
|
buildProject(docker, logger, composeOpts, {
|
2018-03-19 18:58:05 +00:00
|
|
|
app
|
2017-12-11 22:37:56 +00:00
|
|
|
arch
|
|
|
|
deviceType
|
|
|
|
buildEmulated: !!options.emulated
|
|
|
|
buildOpts
|
2020-01-29 11:10:59 +00:00
|
|
|
convertEol: options.convertEol
|
2017-12-11 22:37:56 +00:00
|
|
|
})
|
|
|
|
)
|
|
|
|
.asCallback(done)
|