All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bmuschko.gradle.docker.tasks.image.Dockerfile.groovy Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 the original author or authors.
 *
 * 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.
 */
package com.bmuschko.gradle.docker.tasks.image

import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode
import org.gradle.api.DefaultTask
import org.gradle.api.Transformer
import org.gradle.api.file.Directory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

import javax.annotation.Nullable

/**
 * Creates a Dockerfile based on the provided instructions.
 */
@CacheableTask
@CompileStatic
class Dockerfile extends DefaultTask {

    private final ListProperty instructions

    /**
     * The destination file representing the Dockerfile. The destination file encourages the conventional file name Dockerfile but allows any arbitrary file name.
     * 

* Defaults to {@code $buildDir/docker/Dockerfile}. *

* The method {@link #getDestDir()} returns the parent directory of the Dockerfile. */ @OutputFile final RegularFileProperty destFile Dockerfile() { instructions = project.objects.listProperty(Instruction).empty() destFile = project.objects.fileProperty() destFile.set(project.layout.buildDirectory.file('docker/Dockerfile')) } /** * Returns all instructions used to generate the Dockerfile. * * @return All instructions */ @Nested ListProperty getInstructions() { instructions } /** * Returns a provider representing the destination directory containing the Dockerfile. * * @return The destination directory containing the Dockerfile * @since 4.4.0 */ @Internal Provider getDestDir() { destFile.flatMap(new Transformer, RegularFile>() { @Override Provider transform(RegularFile f) { DirectoryProperty destDir = project.objects.directoryProperty() destDir.set(f.asFile.parentFile) destDir } }) } @TaskAction void create() { verifyValidInstructions() destFile.get().asFile.withWriter { out -> instructions.get().forEach() { Instruction instruction -> String instructionText = instruction.getText() if (instructionText) { out.println instructionText } } } } private void verifyValidInstructions() { List allInstructions = instructions.get().collect() // Comments are not relevant for validating instruction order allInstructions.removeAll { it.text?.startsWith(CommentInstruction.KEYWORD) } if (allInstructions.empty) { throw new IllegalStateException('Please specify instructions for your Dockerfile') } def fromPos = allInstructions.findIndexOf { it.keyword == FromInstruction.KEYWORD } def othersPos = allInstructions.findIndexOf { it.keyword != ArgInstruction.KEYWORD && it.keyword != FromInstruction.KEYWORD } if (fromPos < 0 || (othersPos >= 0 && fromPos > othersPos)) { throw new IllegalStateException("The first instruction of a Dockerfile has to be $FromInstruction.KEYWORD (or $ArgInstruction.KEYWORD for Docker later than 17.05)") } } /** * Adds instructions to the Dockerfile from a template file. The template file can have any name. * * @param template The template file * @see #instructionsFromTemplate(String) * @see #instructionsFromTemplate(Provider) */ void instructionsFromTemplate(java.io.File template) { if (!template.exists()) { throw new FileNotFoundException("docker template file not found at location : ${template.getAbsolutePath()}") } template.readLines().findAll { it.length() > 0 } each { String instruction -> instructions.add(new GenericInstruction(instruction)) } } /** * Adds instructions to the Dockerfile from a template file. The path can be relative to the project root directory or absolute. * * @param templatePath The path to the template file * @see #instructionsFromTemplate(java.io.File) * @see #instructionsFromTemplate(Provider) */ void instructionsFromTemplate(String templatePath) { instructionsFromTemplate(project.file(templatePath)) } /** * Adds instructions to the Dockerfile from a template file. Currently, the provider is evaluated as soon as the method is called * which means that the provider is not evaluated lazily. This behavior might change in the future. * * @param provider The provider of the template file * @see #instructionsFromTemplate(java.io.File) * @see #instructionsFromTemplate(String) * @since 4.0.0 */ void instructionsFromTemplate(Provider provider) { instructionsFromTemplate(provider.get().asFile) } /** * Adds a full instruction as String. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     instruction('FROM ubuntu:14.04')
     *     instruction('LABEL [email protected]')
     * }
     * 
* * @param instruction Instruction as String * @see #instruction(Provider) */ void instruction(String instruction) { instructions.add(new GenericInstruction(instruction)) } /** * Adds a full instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     instruction(project.provider(new Callable() {
     *         {@literal @}Override
     *         String call() throws Exception {
     *             'FROM ubuntu:14.04'
     *         }
     *     }))
     * }
     * 
* * @param provider Instruction as Provider * @see #instruction(String) * @since 4.0.0 */ void instruction(Provider provider) { instructions.add(new GenericInstruction(provider)) } /** * The FROM instruction sets the Base Image for * subsequent instructions. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     from('ubuntu:14.04')
     * }
     * 
* * @param from From definition * @see #from(From) * @see #from(Provider) */ void from(String image) { instructions.add(new FromInstruction(new From(image))) } /** * The FROM instruction sets the Base Image for * subsequent instructions. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     from(new From('ubuntu:14.04'))
     * }
     * 
* * @param from From definition * @param stageName stage name in case of multi-stage builds (default null) * @see #from(String) * @see #from(Provider) */ void from(From from) { instructions.add(new FromInstruction(from)) } /** * A FROM instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     from(project.provider(new Callable() {
     *         {@literal @}Override
     *         Dockerfile.From call() throws Exception {
     *             new Dockerfile.From('ubuntu:14.04')
     *         }
     *     }))
     * }
     * 
* * @param provider From information as Provider * @see #from(From) * @since 4.0.0 */ void from(Provider provider) { instructions.add(new FromInstruction(provider)) } /** * The ARG instruction defines a variable that * users can pass at build-time to the builder. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     arg('user1=someuser')
     * }
     * 
* * @param arg Argument to pass, possibly with default value. * @see #arg(Provider) */ void arg(String arg) { instructions.add(new ArgInstruction(arg)) } /** * A ARG instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     arg(project.provider(new Callable() {
     *         {@literal @}Override
     *         String call() throws Exception {
     *             'user1=someuser'
     *         }
     *     }))
     * }
     * 
* * @param provider Argument to pass as Provider * @see #arg(String) * @since 4.0.0 */ void arg(Provider provider) { instructions.add(new ArgInstruction(provider)) } /** * The RUN instruction will execute any commands in a * new layer on top of the current image and commit the results. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     runCommand('/bin/bash -c echo hello')
     * }
     * 
* * @param command Command * @see #runCommand(Provider) */ void runCommand(String command) { instructions.add(new RunCommandInstruction(command)) } /** * A RUN instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     runCommand(project.provider(new Callable() {
     *         {@literal @}Override
     *         String call() throws Exception {
     *             '/bin/bash -c echo hello'
     *         }
     *     }))
     * }
     * 
* * @param provider Command as Provider * @see #runCommand(String) * @since 4.0.0 */ void runCommand(Provider provider) { instructions.add(new RunCommandInstruction(provider)) } /** * The main purpose of a CMD instruction is to provide * defaults for an executing container. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     defaultCommand('/usr/bin/wc', '--help')
     * }
     * 
* * @param command Command * @see #defaultCommand(Provider) */ void defaultCommand(String... command) { instructions.add(new DefaultCommandInstruction(command)) } /** * A CMD instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     defaultCommand(project.provider(new Callable>() {
     *         {@literal @}Override
     *         List call() throws Exception {
     *             ['/usr/bin/wc', '--help']
     *         }
     *     }))
     * }
     * 
* * @param provider Command as Provider * @see #defaultCommand(String...) * @since 4.0.0 */ void defaultCommand(Provider> provider) { instructions.add(new DefaultCommandInstruction(provider)) } /** * The EXPOSE instruction informs Docker that the * container will listen on the specified network ports at runtime. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     exposePort(8080, 9090)
     * }
     * 
* * @param ports Ports * @see #exposePort(Provider) */ void exposePort(Integer... ports) { instructions.add(new ExposePortInstruction(ports)) } /** * A EXPOSE instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     exposePort(project.provider(new Callable>() {
     *         {@literal @}Override
     *         List call() throws Exception {
     *             [8080, 9090]
     *         }
     *     }))
     * }
     * 
* * @param ports Ports as Provider * @see #exposePort(Integer...) * @since 4.0.0 */ void exposePort(Provider> provider) { instructions.add(new ExposePortInstruction(provider)) } /** * The ENV instruction sets the environment variable * <key> to the value <value>. This value will be passed to all future RUN instructions. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     environmentVariable('myName', 'John Doe')
     * }
     * 
* * @param key Key * @param value Value * @see #environmentVariable(Map) * @see #environmentVariable(Provider) */ void environmentVariable(String key, String value) { instructions.add(new EnvironmentVariableInstruction(key, value)) } /** * A ENV instruction as Map. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     environmentVariable(['myName': 'John Doe'])
     * }
     * 
* * @param envVars Environment variables * @see #environmentVariable(String, String) * @see #environmentVariable(Provider) */ void environmentVariable(Map envVars) { instructions.add(new EnvironmentVariableInstruction(envVars)) } /** * A ENV instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     environmentVariable(project.provider(new Callable>() {
     *         {@literal @}Override
     *         Map call() throws Exception {
     *             ['myName': 'John Doe']
     *         }
     *     }))
     * }
     * 
* * @param provider Environment variables as Provider * @see #environmentVariable(String, String) * @see #environmentVariable(Map) * @since 4.0.0 */ void environmentVariable(Provider> provider) { instructions.add(new EnvironmentVariableInstruction(provider)) } /** * The ADD instruction copies new files, directories * or remote file URLs from <src> and adds them to the filesystem of the container at the path <dest>. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     addFile('test', '/absoluteDir/')
     * }
     * 
* * @param src The source path * @param dest The destination path * @see #addFile(File) * @see #addFile(Provider) */ void addFile(String src, String dest) { addFile(new File(src, dest)) } /** * The ADD instruction copies new files, directories * or remote file URLs from <src> and adds them to the filesystem of the container at the path <dest>. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     addFile(new Dockerfile.File('test', '/absoluteDir/'))
     * }
     * 
* * @param file File definition * @see #addFile(String, String) * @see #addFile(Provider) */ void addFile(File file) { instructions.add(new AddFileInstruction(file)) } /** * A ADD instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     addFile(project.provider(new Callable() {
     *         {@literal @}Override
     *         Dockerfile.File call() throws Exception {
     *             new Dockerfile.File('test', '/absoluteDir/')
     *         }
     *     }))
     * }
     * 
* * @param provider Add instruction as Provider * @see #addFile(String, String) * @see #addFile(File) * @since 4.0.0 */ void addFile(Provider provider) { instructions.add(new AddFileInstruction(provider)) } /** * The COPY instruction copies new files or directories * from <src> and adds them to the filesystem of the container at the path <dest>. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     copyFile('test', '/absoluteDir/')
     * }
     * 
* * @param src The source path * @param dest The destination path * @see #copyFile(CopyFile) * @see #copyFile(Provider) */ void copyFile(String src, String dest) { copyFile(new CopyFile(src, dest)) } /** * The COPY instruction copies new files or directories * from <src> and adds them to the filesystem of the container at the path <dest>. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     copyFile(new Dockerfile.CopyFile('test', '/absoluteDir/'))
     * }
     * 
* * @param file File definition * @see #copyFile(String, String) * @see #copyFile(Provider) */ void copyFile(CopyFile file) { instructions.add(new CopyFileInstruction(file)) } /** * A COPY instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     copyFile(project.provider(new Callable() {
     *         {@literal @}Override
     *         Dockerfile.CopyFile call() throws Exception {
     *             new Dockerfile.CopyFile('test', '/absoluteDir/')
     *         }
     *     }))
     * }
     * 
* * @param provider Copy instruction as Provider * @see #copyFile(String, String) * @see #copyFile(CopyFile) * @since 4.0.0 */ void copyFile(Provider provider) { instructions.add(new CopyFileInstruction(provider)) } /** * An ENTRYPOINT allows you to configure a container * that will run as an executable. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     entryPoint('top', '-b')
     * }
     * 
* * @param entryPoint Entry point * @see #entryPoint(Provider) */ void entryPoint(String... entryPoint) { instructions.add(new EntryPointInstruction(entryPoint)) } /** * A ENTRYPOINT as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     entryPoint(project.provider(new Callable>() {
     *         {@literal @}Override
     *         List call() throws Exception {
     *             ['top', '-b']
     *         }
     *     }))
     * }
     * 
* * @param entryPoint Entry point * @see #entryPoint(String...) * @since 4.0.0 */ void entryPoint(Provider> provider) { instructions.add(new EntryPointInstruction(provider)) } /** * The VOLUME instruction will create a mount point * with the specified name and mark it as holding externally mounted volumes from native host or other containers. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     volume('/myvol')
     * }
     * 
* * @param volume Volume * @see #volume(Provider) */ void volume(String... volume) { instructions.add(new VolumeInstruction(volume)) } /** * A VOLUME instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     volume(project.provider(new Callable>() {
     *         {@literal @}Override
     *         List call() throws Exception {
     *             ['/myvol']
     *         }
     *     }))
     * }
     * 
* * @param volume Volume * @see #volume(String...) * @since 4.0.0 */ void volume(Provider> provider) { instructions.add(new VolumeInstruction(provider)) } /** * The USER instruction sets the user name or UID to * use when running the image and for any RUN, CMD and ENTRYPOINT instructions that follow it in the Dockerfile. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     user('patrick')
     * }
     * 
* * @param user User * @see #user(Provider) */ void user(String user) { instructions.add(new UserInstruction(user)) } /** * A USER instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     user(project.provider(new Callable() {
     *         {@literal @}Override
     *         String call() throws Exception {
     *             'patrick'
     *         }
     *     }))
     * }
     * 
* * @param provider User as Provider * @see #user(String) * @since 4.0.0 */ void user(Provider provider) { instructions.add(new UserInstruction(provider)) } /** * The WORKDIR instruction sets the working directory * for any RUN, CMD and ENTRYPOINT instructions that follow it in the Dockerfile. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     workingDir('/path/to/workdir')
     * }
     * 
* * @param dir Directory * @see #workingDir(Provider) */ void workingDir(String dir) { instructions.add(new WorkDirInstruction(dir)) } /** * A WORKDIR instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     workingDir(project.provider(new Callable() {
     *         {@literal @}Override
     *         String call() throws Exception {
     *             '/path/to/workdir'
     *         }
     *     }))
     * }
     * 
* * @param dir Directory * @see #workingDir(String) * @since 4.0.0 */ void workingDir(Provider provider) { instructions.add(new WorkDirInstruction(provider)) } /** * The ONBUILD instruction adds to the image a * trigger instruction to be executed at a later time, when the image is used as the base for another build. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     onBuild('ADD . /app/src')
     * }
     * 
* * @param instruction Instruction * @see #onBuild(Provider) */ void onBuild(String instruction) { instructions.add(new OnBuildInstruction(instruction)) } /** * A ONBUILD instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     onBuild(project.provider(new Callable() {
     *         {@literal @}Override
     *         String call() throws Exception {
     *             'ADD . /app/src'
     *         }
     *     }))
     * }
     * 
* * @param instruction Instruction * @see #onBuild(String) * @since 4.0.0 */ void onBuild(Provider provider) { instructions.add(new OnBuildInstruction(provider)) } /** * The LABEL instruction adds metadata to an image. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     label(['version': '1.0'])
     * }
     * 
* * @param labels Labels * @see #label(Provider) */ void label(Map labels) { instructions.add(new LabelInstruction(labels)) } /** * A LABEL instruction as Provider. *

* Example in Groovy DSL: *

*

     * task createDockerfile(type: Dockerfile) {
     *     label(project.provider(new Callable>() {
     *         {@literal @}Override
     *         Map call() throws Exception {
     *             ['version': '1.0']
     *         }
     *     }))
     * }
     * 
* * @param provider Labels as Provider * @see #label(Map) * @since 4.0.0 */ void label(Provider> provider) { instructions.add(new LabelInstruction(provider)) } /** * A representation of an instruction in a Dockerfile. */ static interface Instruction { /** * Gets the keyword of the instruction as used in the Dockerfile. *

* For example the keyword of the {@link FromInstruction} is {@code FROM}. * * @return The instruction keyword */ @Internal @Nullable String getKeyword() /** * Gets the full text of the instruction as used in the Dockerfile. * * @return The instruction * @since 3.6.0 */ @Input @Optional @Nullable String getText() } /** * An instruction that uses the provided value as-is without any additional formatting. *

* Use this instruction if you want to provide a very complex instruction or if there's not a specific implementation of {@link Instruction} that serves your use case. */ static class GenericInstruction implements Instruction { private final String instruction private final Provider instructionProvider GenericInstruction(String instruction) { this.instruction = instruction } GenericInstruction(Provider instructionProvider) { this.instructionProvider = instructionProvider } /** * {@inheritDoc} */ @Override String getKeyword() { if (instructionProvider) { return parseKeyword(instructionProvider.getOrNull()) } parseKeyword(instruction) } private String parseKeyword(String inst) { inst?.substring(0, inst.indexOf(' ')) } /** * {@inheritDoc} */ @Override String getText() { if (instructionProvider) { return instructionProvider.getOrNull() } instruction } } /** * An instruction whose value is a String. */ static abstract class StringCommandInstruction implements Instruction { private final String command private final Provider commandProvider StringCommandInstruction(String command) { this.command = command } StringCommandInstruction(Provider commandProvider) { this.commandProvider = commandProvider } /** * {@inheritDoc} */ @Override String getText() { if (commandProvider) { String command = commandProvider.getOrNull() if (command) { return buildText(command) } } else { return buildText(command) } } private String buildText(String command) { "$keyword $command" } } /** * An instruction whose value is a String array. */ static abstract class StringArrayInstruction implements Instruction { private final String[] command private final Provider> commandProvider StringArrayInstruction(String... command) { this.command = command } StringArrayInstruction(Provider> commandProvider) { this.commandProvider = commandProvider } /** * {@inheritDoc} */ @Override String getText() { if (commandProvider) { List command = commandProvider.getOrNull() if (command) { return buildText(command as String[]) } } else { return buildText(command) } } private String buildText(String[] command) { keyword + ' ["' + command.join('", "') + '"]' } } private interface ItemJoiner { String join(Map map) } private static class MultiItemJoiner implements ItemJoiner { @Override @CompileStatic(TypeCheckingMode.SKIP) String join(Map map) { map.inject([]) { result, entry -> def key = ItemJoinerUtil.isUnquotedStringWithWhitespaces(entry.key) ? ItemJoinerUtil.toQuotedString(entry.key) : entry.key def value = ItemJoinerUtil.isUnquotedStringWithWhitespaces(entry.value) ? ItemJoinerUtil.toQuotedString(entry.value) : entry.value value = value.replaceAll("(\r)*\n", "\\\\\n") result << "$key=$value" }.join(' ') } } private static class ItemJoinerUtil { private static boolean isUnquotedStringWithWhitespaces(String str) { return !str.matches('["].*["]') && str.matches('.*(?: |(?:\r?\n)).*') } private static String toQuotedString(final String str) { '"'.concat(str.replaceAll('"', '\\\\"')).concat('"') } } /** * An instruction whose value is a Map. */ static abstract class MapInstruction implements Instruction { private final Map command private final Provider> commandProvider private final ItemJoiner joiner MapInstruction(Map command) { this.command = command this.joiner = new MultiItemJoiner() } MapInstruction(Provider> commandProvider) { this.commandProvider = commandProvider joiner = new MultiItemJoiner() } /** * {@inheritDoc} */ @Override String getText() { Map commandToJoin = command if (commandProvider) { def evaluatedCommand = commandProvider.getOrNull() if (!(evaluatedCommand instanceof Map)) { throw new IllegalArgumentException("the given evaluated closure is not a valid input for instruction ${keyword} while it doesn't provide a `Map` ([ key: value ]) but a `${evaluatedCommand?.class}` (${evaluatedCommand?.toString()})") } commandToJoin = evaluatedCommand as Map } if (commandToJoin == null) { throw new IllegalArgumentException("instruction has to be set for ${keyword}") } validateKeysAreNotBlank commandToJoin "$keyword ${joiner.join(commandToJoin)}" } private void validateKeysAreNotBlank(Map command) throws IllegalArgumentException { command.each { entry -> if (entry.key.trim().length() == 0) { throw new IllegalArgumentException("blank keys for a key=value pair are not allowed: please check instruction ${keyword} and given pair `${entry}`") } } } } /** * An instruction whose value is a File. */ static abstract class FileInstruction implements Instruction { private final T file private final Provider provider FileInstruction(T file) { this.file = file } FileInstruction(Provider provider) { this.provider = provider } @Internal T getFile() { provider ? provider.getOrNull() : file } /** * {@inheritDoc} */ @Override String getText() { File fileValue = getFile() if (fileValue) { StringBuilder instruction = new StringBuilder(keyword) if (fileValue.chown) { instruction.append(" --chown=$fileValue.chown") } if (fileValue.src && fileValue.dest) { instruction.append(" $fileValue.src $fileValue.dest") } instruction.toString() } } } /** * Represents a {@code FROM} instruction. */ static class FromInstruction implements Instruction { public static final String KEYWORD = 'FROM' private final From from private final Provider provider FromInstruction(From from) { this.from = from } FromInstruction(Provider provider) { this.provider = provider } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } /** * {@inheritDoc} */ @Override String getText() { if (provider) { return buildTextInstruction(provider.getOrNull()) } buildTextInstruction(from) } private String buildTextInstruction(From from) { if (from) { String result = "$keyword $from.image" if (from.stage) { result += " AS $from.stage" } result } } } /** * Represents a {@code ARG} instruction. */ static class ArgInstruction extends StringCommandInstruction { public static final String KEYWORD = 'ARG' ArgInstruction(String arg) { super(arg) } ArgInstruction(Provider provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code RUN} instruction. */ static class RunCommandInstruction extends StringCommandInstruction { public static final String KEYWORD = 'RUN' RunCommandInstruction(String command) { super(command) } RunCommandInstruction(Provider provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code CMD} instruction. */ static class DefaultCommandInstruction extends StringArrayInstruction { public static final String KEYWORD = 'CMD' DefaultCommandInstruction(String... command) { super(command) } DefaultCommandInstruction(Provider> provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code EXPOSE} instruction. */ static class ExposePortInstruction implements Instruction { public static final String KEYWORD = 'EXPOSE' private final Integer[] ports private final Provider> provider ExposePortInstruction(Integer... ports) { this.ports = ports } ExposePortInstruction(Provider> provider) { this.provider = provider } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } /** * {@inheritDoc} */ @Override String getText() { if (provider) { List evaluatedPorts = provider.getOrNull() if (evaluatedPorts && !evaluatedPorts.isEmpty()) { return buildText(evaluatedPorts as Integer[]) } } else { return buildText(ports) } } private String buildText(Integer[] ports) { "$keyword ${ports.join(' ')}" } } /** * Represents a {@code ENV} instruction. */ static class EnvironmentVariableInstruction extends MapInstruction { public static final String KEYWORD = 'ENV' EnvironmentVariableInstruction(String key, String value) { super([(key): value]) } EnvironmentVariableInstruction(Map envVars) { super(envVars) } EnvironmentVariableInstruction(Provider> provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code ADD} instruction. */ static class AddFileInstruction extends FileInstruction { public static final String KEYWORD = 'ADD' AddFileInstruction(File file) { super(file) } AddFileInstruction(Provider provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code COPY} instruction. */ static class CopyFileInstruction extends FileInstruction { public static final String KEYWORD = 'COPY' CopyFileInstruction(CopyFile file) { super(file) } CopyFileInstruction(Provider provider) { super(provider) } @Override String getText() { String text = super.getText() if (file && file.stage) { int keywordIndex = keyword.length() text = text.substring(0, keywordIndex) + " --from=$file.stage" + text.substring(keywordIndex, text.length()) } text } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code ENTRYPOINT} instruction. */ static class EntryPointInstruction extends StringArrayInstruction { public static final String KEYWORD = 'ENTRYPOINT' EntryPointInstruction(String... entryPoint) { super(entryPoint) } EntryPointInstruction(Provider> provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } static class VolumeInstruction extends StringArrayInstruction { public static final String KEYWORD = 'VOLUME' VolumeInstruction(String... volume) { super(volume) } VolumeInstruction(Provider> provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code USER} instruction. */ static class UserInstruction extends StringCommandInstruction { public static final String KEYWORD = 'USER' UserInstruction(String user) { super(user) } UserInstruction(Provider provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code WORKDIR} instruction. */ static class WorkDirInstruction extends StringCommandInstruction { public static final String KEYWORD = 'WORKDIR' WorkDirInstruction(String dir) { super(dir) } WorkDirInstruction(Provider provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code ONBUILD} instruction. */ static class OnBuildInstruction extends StringCommandInstruction { public static final String KEYWORD = 'ONBUILD' OnBuildInstruction(String instruction) { super(instruction) } OnBuildInstruction(Provider provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a {@code LABEL} instruction. */ static class LabelInstruction extends MapInstruction { public static final String KEYWORD = 'LABEL' LabelInstruction(Map labels) { super(labels) } LabelInstruction(Provider> provider) { super(provider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Represents a comment instruction. * * @since 4.0.1 */ static class CommentInstruction extends StringCommandInstruction { public static final String KEYWORD = '#' CommentInstruction(String command) { super(command) } CommentInstruction(Provider commandProvider) { super(commandProvider) } /** * {@inheritDoc} */ @Override String getKeyword() { KEYWORD } } /** * Input data for a {@link AddFileInstruction} or {@link CopyFileInstruction}. * * @since 4.0.0 */ static class File { private final String src private final String dest @Nullable private String chown File(String src, String dest) { this.src = src this.dest = dest } /** * Specifies a given username, groupname, or UID/GID combination to request specific ownership of the copied content with the help of the {@code --chown} option. *

* Should be provided in the form of {@code :}. * * @param chown The ownership of the copied content */ File withChown(String chown) { this.chown = chown this } /** * Return the source path. * * @return The source path */ String getSrc() { src } /** * Returns the destination path. * * @return The destination path */ String getDest() { dest } /** * Returns the ownership of the copied content. * * @return The ownership of the copied content */ @Nullable String getChown() { chown } } /** * Input data for a {@link CopyFileInstruction}. * * @since 5.0.0 */ static class CopyFile extends File { @Nullable private String stage CopyFile(String src, String dest) { super(src, dest) } /** * Used to set the source location to a previous build stage. * * @param The previous stage * @return This instruction */ CopyFile withStage(String stage) { this.stage = stage this } /** * Returns the previous build stage. * * @return The previous stage */ @Nullable String getStage() { stage } } /** * Input data for a {@link FromInstruction}. * * @since 4.0.0 */ static class From { private final String image @Nullable private String stage From(String image) { this.image = image } /** * Sets a new build stage by adding {@code AS} name to the {@code FROM} instruction. * * @param stage The stage * @return This instruction */ From withStage(String stage) { this.stage = stage this } /** * Returns the base image. * * @return The base image */ String getImage() { image } /** * Returns the stage. * * @return The stage */ @Nullable String getStage() { stage } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy