mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-18 21:27:51 +00:00
Convert lib/actions/build.coffee to javascript
Change-type: patch
This commit is contained in:
parent
dbe9a727d5
commit
4d8cd1cc46
@ -1,186 +0,0 @@
|
||||
###*
|
||||
# @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.
|
||||
###
|
||||
|
||||
# Imported here because it's needed for the setup
|
||||
# of this action
|
||||
Promise = require('bluebird')
|
||||
dockerUtils = require('../utils/docker')
|
||||
compose = require('../utils/compose')
|
||||
{ registrySecretsHelp } = require('../utils/messages')
|
||||
{ getBalenaSdk } = require('../utils/lazy')
|
||||
|
||||
###
|
||||
Opts must be an object with the following keys:
|
||||
|
||||
app: the app this build is for (optional)
|
||||
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) ->
|
||||
{ loadProject } = require('../utils/compose_ts')
|
||||
Promise.resolve(loadProject(logger, composeOpts))
|
||||
.then (project) ->
|
||||
appType = opts.app?.application_type?[0]
|
||||
if appType? and project.descriptors.length > 1 and not appType.supports_multicontainer
|
||||
logger.logWarn(
|
||||
'Target application does not support multiple containers.\n' +
|
||||
'Continuing with build, but you will not be able to deploy.'
|
||||
)
|
||||
|
||||
compose.buildProject(
|
||||
docker
|
||||
logger
|
||||
project.path
|
||||
project.name
|
||||
project.composition
|
||||
opts.arch
|
||||
opts.deviceType
|
||||
opts.buildEmulated
|
||||
opts.buildOpts
|
||||
composeOpts.inlineLogs
|
||||
opts.convertEol
|
||||
composeOpts.dockerfilePath
|
||||
)
|
||||
.then ->
|
||||
logger.outputDeferredMessages()
|
||||
logger.logSuccess('Build succeeded!')
|
||||
.tapCatch (e) ->
|
||||
logger.logError('Build failed')
|
||||
|
||||
module.exports =
|
||||
signature: 'build [source]'
|
||||
description: 'Build a single image or a multicontainer project locally'
|
||||
primary: true
|
||||
help: """
|
||||
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 to use
|
||||
the balena Dockerfile pre-processor (e.g. Dockerfile.template -> Dockerfile).
|
||||
|
||||
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}
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena build
|
||||
$ balena build ./source/
|
||||
$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated
|
||||
$ balena build --application MyApp ./source/
|
||||
$ balena build --docker /var/run/docker.sock # Linux, Mac
|
||||
$ balena build --docker //./pipe/docker_engine # Windows
|
||||
$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem
|
||||
"""
|
||||
options: dockerUtils.appendOptions compose.appendOptions [
|
||||
{
|
||||
signature: 'arch'
|
||||
parameter: 'arch'
|
||||
description: 'The architecture to build for'
|
||||
alias: 'A'
|
||||
},
|
||||
{
|
||||
signature: 'deviceType'
|
||||
parameter: 'deviceType'
|
||||
description: 'The type of device this build is for'
|
||||
alias: 'd'
|
||||
},
|
||||
{
|
||||
signature: 'application'
|
||||
parameter: 'application'
|
||||
description: 'The target balena application this build is for'
|
||||
alias: 'a'
|
||||
},
|
||||
]
|
||||
action: (params, options) ->
|
||||
# compositions with many services trigger misleading warnings
|
||||
require('events').defaultMaxListeners = 1000
|
||||
|
||||
sdk = getBalenaSdk()
|
||||
{ ExpectedError } = require('../errors')
|
||||
{ checkLoggedIn } = require('../utils/patterns')
|
||||
{ validateProjectDirectory } = require('../utils/compose_ts')
|
||||
helpers = require('../utils/helpers')
|
||||
Logger = require('../utils/logger')
|
||||
|
||||
logger = Logger.getLogger()
|
||||
logger.logDebug('Parsing input...')
|
||||
|
||||
# `build` accepts `[source]` as a parameter, but compose expects it
|
||||
# as an option. swap them here
|
||||
options.source ?= params.source
|
||||
delete params.source
|
||||
|
||||
options.convertEol = options['convert-eol'] || false
|
||||
delete options['convert-eol']
|
||||
|
||||
{ application, arch, deviceType } = options
|
||||
|
||||
Promise.try ->
|
||||
if (not (arch? and deviceType?) and not application?) or (application? and (arch? or deviceType?))
|
||||
throw new ExpectedError('You must specify either an application or an arch/deviceType pair to build for')
|
||||
if (application)
|
||||
checkLoggedIn()
|
||||
.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
|
||||
|
||||
if arch? and deviceType?
|
||||
[ undefined, arch, deviceType ]
|
||||
else
|
||||
Promise.join(
|
||||
helpers.getApplication(application)
|
||||
helpers.getArchAndDeviceType(application)
|
||||
(app, { arch, device_type }) ->
|
||||
app.arch = arch
|
||||
app.device_type = device_type
|
||||
return app
|
||||
)
|
||||
.then (app) ->
|
||||
[ app, app.arch, app.device_type ]
|
||||
|
||||
.then ([ app, arch, deviceType ]) ->
|
||||
Promise.join(
|
||||
dockerUtils.getDocker(options)
|
||||
dockerUtils.generateBuildOpts(options)
|
||||
compose.generateOpts(options)
|
||||
(docker, buildOpts, composeOpts) ->
|
||||
buildProject(docker, logger, composeOpts, {
|
||||
app
|
||||
arch
|
||||
deviceType
|
||||
buildEmulated: !!options.emulated
|
||||
buildOpts
|
||||
convertEol: options.convertEol
|
||||
})
|
||||
)
|
221
lib/actions/build.js
Normal file
221
lib/actions/build.js
Normal file
@ -0,0 +1,221 @@
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
|
||||
// Imported here because it's needed for the setup
|
||||
// of this action
|
||||
import * as Promise from 'bluebird';
|
||||
|
||||
import * as dockerUtils from '../utils/docker';
|
||||
import * as compose from '../utils/compose';
|
||||
import { registrySecretsHelp } from '../utils/messages';
|
||||
import { getBalenaSdk } from '../utils/lazy';
|
||||
|
||||
/*
|
||||
Opts must be an object with the following keys:
|
||||
|
||||
app: the app this build is for (optional)
|
||||
arch: the architecture to build for
|
||||
deviceType: the device type to build for
|
||||
buildEmulated
|
||||
buildOpts: arguments to forward to docker build command
|
||||
*/
|
||||
const buildProject = function(docker, logger, composeOpts, opts) {
|
||||
const { loadProject } = require('../utils/compose_ts');
|
||||
return Promise.resolve(loadProject(logger, composeOpts))
|
||||
.then(function(project) {
|
||||
const appType = opts.app?.application_type?.[0];
|
||||
if (
|
||||
appType != null &&
|
||||
project.descriptors.length > 1 &&
|
||||
!appType.supports_multicontainer
|
||||
) {
|
||||
logger.logWarn(
|
||||
'Target application does not support multiple containers.\n' +
|
||||
'Continuing with build, but you will not be able to deploy.',
|
||||
);
|
||||
}
|
||||
|
||||
return compose.buildProject(
|
||||
docker,
|
||||
logger,
|
||||
project.path,
|
||||
project.name,
|
||||
project.composition,
|
||||
opts.arch,
|
||||
opts.deviceType,
|
||||
opts.buildEmulated,
|
||||
opts.buildOpts,
|
||||
composeOpts.inlineLogs,
|
||||
opts.convertEol,
|
||||
composeOpts.dockerfilePath,
|
||||
);
|
||||
})
|
||||
.then(function() {
|
||||
logger.outputDeferredMessages();
|
||||
logger.logSuccess('Build succeeded!');
|
||||
})
|
||||
.tapCatch(() => {
|
||||
logger.logError('Build failed');
|
||||
});
|
||||
};
|
||||
|
||||
export const build = {
|
||||
signature: 'build [source]',
|
||||
description: 'Build a single image or a multicontainer project locally',
|
||||
primary: true,
|
||||
help: `\
|
||||
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 to use
|
||||
the balena Dockerfile pre-processor (e.g. Dockerfile.template -> Dockerfile).
|
||||
|
||||
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}
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena build
|
||||
$ balena build ./source/
|
||||
$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated
|
||||
$ balena build --application MyApp ./source/
|
||||
$ balena build --docker /var/run/docker.sock # Linux, Mac
|
||||
$ balena build --docker //./pipe/docker_engine # Windows
|
||||
$ balena build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem\
|
||||
`,
|
||||
options: dockerUtils.appendOptions(
|
||||
compose.appendOptions([
|
||||
{
|
||||
signature: 'arch',
|
||||
parameter: 'arch',
|
||||
description: 'The architecture to build for',
|
||||
alias: 'A',
|
||||
},
|
||||
{
|
||||
signature: 'deviceType',
|
||||
parameter: 'deviceType',
|
||||
description: 'The type of device this build is for',
|
||||
alias: 'd',
|
||||
},
|
||||
{
|
||||
signature: 'application',
|
||||
parameter: 'application',
|
||||
description: 'The target balena application this build is for',
|
||||
alias: 'a',
|
||||
},
|
||||
]),
|
||||
),
|
||||
action(params, options) {
|
||||
// compositions with many services trigger misleading warnings
|
||||
// @ts-ignore editing property that isn't typed but does exist
|
||||
require('events').defaultMaxListeners = 1000;
|
||||
|
||||
const sdk = getBalenaSdk();
|
||||
const { ExpectedError } = require('../errors');
|
||||
const { checkLoggedIn } = require('../utils/patterns');
|
||||
const { validateProjectDirectory } = require('../utils/compose_ts');
|
||||
const helpers = require('../utils/helpers');
|
||||
const Logger = require('../utils/logger');
|
||||
|
||||
const logger = Logger.getLogger();
|
||||
logger.logDebug('Parsing input...');
|
||||
|
||||
// `build` accepts `[source]` as a parameter, but compose expects it
|
||||
// as an option. swap them here
|
||||
if (options.source == null) {
|
||||
options.source = params.source;
|
||||
}
|
||||
delete params.source;
|
||||
|
||||
options.convertEol = options['convert-eol'] || false;
|
||||
delete options['convert-eol'];
|
||||
|
||||
const { application, arch, deviceType } = options;
|
||||
|
||||
return Promise.try(function() {
|
||||
if (
|
||||
(application == null && (arch == null || deviceType == null)) ||
|
||||
(application != null && (arch != null || deviceType != null))
|
||||
) {
|
||||
throw new ExpectedError(
|
||||
'You must specify either an application or an arch/deviceType pair to build for',
|
||||
);
|
||||
}
|
||||
if (application) {
|
||||
return checkLoggedIn();
|
||||
}
|
||||
})
|
||||
.then(() =>
|
||||
validateProjectDirectory(sdk, {
|
||||
dockerfilePath: options.dockerfile,
|
||||
noParentCheck: options['noparent-check'] || false,
|
||||
projectPath: options.source || '.',
|
||||
registrySecretsPath: options['registry-secrets'],
|
||||
}),
|
||||
)
|
||||
.then(function({ dockerfilePath, registrySecrets }) {
|
||||
options.dockerfile = dockerfilePath;
|
||||
options['registry-secrets'] = registrySecrets;
|
||||
|
||||
if (arch != null && deviceType != null) {
|
||||
return [undefined, arch, deviceType];
|
||||
} else {
|
||||
return Promise.join(
|
||||
helpers.getApplication(application),
|
||||
helpers.getArchAndDeviceType(application),
|
||||
function(
|
||||
app,
|
||||
{ arch: resolvedArch, device_type: resolvedDeviceType },
|
||||
) {
|
||||
// @ts-ignore extending the app object
|
||||
app.arch = resolvedArch;
|
||||
app.device_type = resolvedDeviceType;
|
||||
return app;
|
||||
},
|
||||
).then(app =>
|
||||
// @ts-ignore using the extended prop
|
||||
[app, app.arch, app.device_type],
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
.then(function([app, resolvedArch, resolvedDeviceType]) {
|
||||
return Promise.join(
|
||||
dockerUtils.getDocker(options),
|
||||
dockerUtils.generateBuildOpts(options),
|
||||
compose.generateOpts(options),
|
||||
(docker, buildOpts, composeOpts) =>
|
||||
buildProject(docker, logger, composeOpts, {
|
||||
app,
|
||||
arch: resolvedArch,
|
||||
deviceType: resolvedDeviceType,
|
||||
buildEmulated: !!options.emulated,
|
||||
buildOpts,
|
||||
convertEol: options.convertEol,
|
||||
}),
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
@ -26,7 +26,7 @@ module.exports =
|
||||
os: require('./os')
|
||||
config: require('./config')
|
||||
ssh: require('./ssh')
|
||||
build: require('./build')
|
||||
build: require('./build').build
|
||||
deploy: require('./deploy')
|
||||
util: require('./util')
|
||||
preload: require('./preload').preload
|
||||
|
Loading…
Reference in New Issue
Block a user