Action "docker.build"
Build docker repository from Dockerfile, from content or from current working directory.
The user can choose whether the build is local or on the remote. Options are the same than docker build command with nikita's one. Be aware than you can not use ADD with content option because docker build from STDIN does not support a context.
By default docker always run the build and overwrite existing repositories. Status unmodified if the repository is identical to a previous one
Output
err
Error object if any.$status
True if image was successfully built.image
Image ID if the image was built, the ID is based on the image sha256 checksum.stdout
Stdout value(s) unlessstdout
option is provided.stderr
Stderr value(s) unlessstderr
option is provided.
Builds a repository from dockerfile without any resourcess
const {$status} = await nikita.docker.build({
image: 'ryba/targe-build',
source: '/home/ryba/Dockerfile'
})
console.info(`Container was built: ${$status}`)
Builds a repository from dockerfile with external resources
In this case nikita download all the external files into a resources directory in the same location than the Dockerfile. The Dockerfile content:
FROM centos7
ADD resources/package.tar.gz /tmp/
ADD resources/configuration.sh /tmp/
Build directory tree :
├── Dockerfile
├── resources
│ ├── package.tar.gz
│ ├── configuration.sh
const {$status} = await nikita.docker.build({
tag: 'ryba/target-build',
source: '/home/ryba/Dockerfile',
resources: ['http://url.com/package.tar.gz/','/home/configuration.sh']
})
console.info(`Container was built: ${$status}`)
Builds a repository from stdin
const {$status} = await nikita.docker.build({
tag: 'ryba/target-build'
content: "FROM ubuntu\nRUN echo 'helloworld'"
})
console.info(`Container was built: ${$status}`)
Hooks
on_action = ({config}) ->
throw errors.NIKITA_DOCKER_BUILD_CONTENT_FILE_REQUIRED() if config.content? and config.file?
Schema definitions
definitions =
config:
type: 'object'
properties:
'build_arg':
oneOf: [
type: 'string'
,
type: 'object',
patternProperties: '.*': typeof: 'string'
]
description: '''
Send arguments to the build, match the Docker native ARG command.
'''
'content':
type: 'string'
description: '''
Content of the Docker file, required unless `file` is provided.
'''
'cwd':
type: 'string'
description: '''
Change the build working directory.
'''
'docker':
$ref: 'module://@nikitajs/docker/src/tools/execute#/definitions/docker'
'file':
type: 'string'
description: '''
Path to Dockerfile, required unless `content` is provided.
'''
'force_rm':
type: 'boolean'
default: false
description: '''
Always remove intermediate containers during build.
'''
'image':
type: 'string'
description: '''
Name of the Docker image present in the registry.
'''
'quiet':
type: 'boolean'
default: false
description: '''
Suppress the verbose output generated by the containers.
'''
'rm':
type: 'boolean'
default: true
description: '''
Remove intermediate containers after a successful build.
'''
'no_cache':
type: 'boolean'
default: false
description: '''
Do not use cache when building the repository.
'''
'tag':
type: 'string'
description: '''
Tag of the Docker image, default to latest.
'''
required: ['image']
Handler
handler = ({config, tools: {log}}) ->
number_of_step = 0
userargs = []
# status unmodified if final tag already exists
dockerfile_commands = ['CMD','LABEL','EXPOSE','ENV','ADD','COPY','ENTRYPOINT',
'VOLUME','USER','WORKDIR','ARG','ONBUILD','RUN','STOPSIGNAL','MAINTAINER']
source = undefined
if config.file
source = config.file
else if config.cwd
source = "#{config.cwd}/Dockerfile"
config.cwd ?= path.dirname config.file if config.file
config.file ?= path.resolve config.cwd, 'Dockerfile' if config.cwd
# Make sure the Dockerfile exists
unless config.content
await @fs.assert config.file
# Build the image
{stdout, stderr} = await @docker.tools.execute
command: [
'build'
...( ['force_rm', 'quiet', 'no_cache']
.filter (opt) -> config[opt]
.map (opt) -> "--#{opt.replace '_', '-'}"
)
...( utils.array.flatten ['build_arg']
.filter (opt) -> config[opt]
.map (opt) ->
if Array.isArray config[opt]
"--#{opt.replace '_', '-'} #{k}" for k in config[opt]
else
"--#{opt.replace '_', '-'} #{config[opt]}"
)
'--rm=' + if config.rm then 'true' else 'false'
'-t ' + utils.string.escapeshellarg config.image+if config.tag then ":#{config.tag}" else ''
(
if config.content?
log message: "Building from text: Docker won't have a context. ADD/COPY not working", level: 'WARN'
"- <<DOCKERFILE\n#{config.content}\nDOCKERFILE" if config.content?
else if config.file?
log message: "Building from Dockerfile: \"#{config.file}\"", level: 'INFO'
"-f #{config.file} #{config.cwd}"
else
log message: "Building from CWD", level: 'INFO'
'.'
)
].join ' '
cwd: config.cwd
# Get the content of the Dockerfile
if config.content
await @file
content: config.content
source: source
target: ({content}) ->
config.content = content
from: config.from
to: config.to
match: config.match
replace: config.replace
append: config.append
before: config.before
write: config.write
# Read Dockerfile if necessary to count steps
else
log message: "Reading Dockerfile from : #{config.file}", level: 'INFO'
{data: config.content} = await @fs.base.readFile
target: config.file
encoding: 'utf8'
# Count steps
for line in utils.string.lines config.content
number_of_step++ if /^(.*?)\s/.exec(line)?[1] in dockerfile_commands
image_id = null
# Count cache
lines = utils.string.lines stdout
number_of_cache = 0
for k, line of lines
if (line.indexOf('Using cache') isnt -1 )
number_of_cache = number_of_cache + 1
if (line.indexOf('Successfully built') isnt -1 )
image_id = line.split(' ').pop().toString()
userargs =
$status: number_of_step isnt number_of_cache
image: image_id
stdout: stdout
stderr: stderr
log if userargs.$status
then message: "New image id #{userargs.image}", level: 'INFO', module: 'nikita/lib/docker/build'
else message: "Identical image id #{userargs.image}", level: 'INFO', module: 'nikita/lib/docker/build'
userargs
Exports
module.exports =
handler: handler
metadata:
global: 'docker'
definitions: definitions
hooks:
on_action: on_action
Errors
errors =
NIKITA_DOCKER_BUILD_CONTENT_FILE_REQUIRED: ->
utils.error 'NIKITA_DOCKER_BUILD_CONTENT_FILE_REQUIRED', [
'could not build the container,'
'one of the `content` or `file` config property must be provided'
]
Dependencies
utils = require './utils'
path = require 'path'