Skip to content

Commit

Permalink
Allow for Dockerfile to be named something else.
Browse files Browse the repository at this point in the history
Add a check to make sure Dockerfile is in the build context
Add docs and a testcase
Make -f relative to current dir, not build context

Signed-off-by: Doug Davis <[email protected]>
  • Loading branch information
Doug Davis committed Jan 7, 2015
1 parent 6d78013 commit eb3ea3b
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 32 deletions.
52 changes: 46 additions & 6 deletions api/client/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
Expand Down Expand Up @@ -84,6 +85,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds")
pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile(Default is 'Dockerfile' at context root)")

cmd.Require(flag.Exact, 1)

utils.ParseFlags(cmd, args, true)
Expand All @@ -109,7 +112,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if err != nil {
return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
}
context, err = archive.Generate("Dockerfile", string(dockerfile))
if *dockerfileName == "" {
*dockerfileName = api.DefaultDockerfileName
}
context, err = archive.Generate(*dockerfileName, string(dockerfile))
} else {
context = ioutil.NopCloser(buf)
}
Expand All @@ -136,9 +142,40 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if _, err := os.Stat(root); err != nil {
return err
}
filename := path.Join(root, "Dockerfile")

absRoot, err := filepath.Abs(root)
if err != nil {
return err
}

var filename string // path to Dockerfile
var origDockerfile string // used for error msg

if *dockerfileName == "" {
// No -f/--file was specified so use the default
origDockerfile = api.DefaultDockerfileName
*dockerfileName = origDockerfile
filename = path.Join(absRoot, *dockerfileName)
} else {
origDockerfile = *dockerfileName
if filename, err = filepath.Abs(*dockerfileName); err != nil {
return err
}

// Verify that 'filename' is within the build context
if !strings.HasSuffix(absRoot, string(os.PathSeparator)) {
absRoot += string(os.PathSeparator)
}
if !strings.HasPrefix(filename, absRoot) {
return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", *dockerfileName, root)
}

// Now reset the dockerfileName to be relative to the build context
*dockerfileName = filename[len(absRoot):]
}

if _, err = os.Stat(filename); os.IsNotExist(err) {
return fmt.Errorf("no Dockerfile found in %s", cmd.Arg(0))
return fmt.Errorf("Can not locate Dockerfile: %s", origDockerfile)
}
var includes []string = []string{"."}

Expand All @@ -147,16 +184,16 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
return err
}

// If .dockerignore mentions .dockerignore or Dockerfile
// If .dockerignore mentions .dockerignore or the Dockerfile
// then make sure we send both files over to the daemon
// because Dockerfile is, obviously, needed no matter what, and
// .dockerignore is needed to know if either one needs to be
// removed. The deamon will remove them for us, if needed, after it
// parses the Dockerfile.
keepThem1, _ := fileutils.Matches(".dockerignore", excludes)
keepThem2, _ := fileutils.Matches("Dockerfile", excludes)
keepThem2, _ := fileutils.Matches(*dockerfileName, excludes)
if keepThem1 || keepThem2 {
includes = append(includes, ".dockerignore", "Dockerfile")
includes = append(includes, ".dockerignore", *dockerfileName)
}

if err = utils.ValidateContextDirectory(root, excludes); err != nil {
Expand Down Expand Up @@ -219,6 +256,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if *pull {
v.Set("pull", "1")
}

v.Set("dockerfile", *dockerfileName)

cli.LoadConfigFile()

headers := http.Header(make(map[string][]string))
Expand Down
7 changes: 4 additions & 3 deletions api/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import (
)

const (
APIVERSION version.Version = "1.16"
DEFAULTHTTPHOST = "127.0.0.1"
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
APIVERSION version.Version = "1.16"
DEFAULTHTTPHOST = "127.0.0.1"
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
DefaultDockerfileName string = "Dockerfile"
)

func ValidateHost(val string) (string, error) {
Expand Down
1 change: 1 addition & 0 deletions api/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
}
job.Stdin.Add(r.Body)
job.Setenv("remote", r.FormValue("remote"))
job.Setenv("dockerfile", r.FormValue("dockerfile"))
job.Setenv("t", r.FormValue("t"))
job.Setenv("q", r.FormValue("q"))
job.Setenv("nocache", r.FormValue("nocache"))
Expand Down
23 changes: 12 additions & 11 deletions builder/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,14 @@ type Builder struct {
// both of these are controlled by the Remove and ForceRemove options in BuildOpts
TmpContainers map[string]struct{} // a map of containers used for removes

dockerfile *parser.Node // the syntax tree of the dockerfile
image string // image name for commit processing
maintainer string // maintainer name. could probably be removed.
cmdSet bool // indicates is CMD was set in current Dockerfile
context tarsum.TarSum // the context is a tarball that is uploaded by the client
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system.
dockerfileName string // name of Dockerfile
dockerfile *parser.Node // the syntax tree of the dockerfile
image string // image name for commit processing
maintainer string // maintainer name. could probably be removed.
cmdSet bool // indicates is CMD was set in current Dockerfile
context tarsum.TarSum // the context is a tarball that is uploaded by the client
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system.
}

// Run the builder with the context. This is the lynchpin of this package. This
Expand All @@ -137,7 +138,7 @@ func (b *Builder) Run(context io.Reader) (string, error) {
}
}()

if err := b.readDockerfile("Dockerfile"); err != nil {
if err := b.readDockerfile(b.dockerfileName); err != nil {
return "", err
}

Expand Down Expand Up @@ -205,9 +206,9 @@ func (b *Builder) readDockerfile(filename string) error {
os.Remove(path.Join(b.contextPath, ".dockerignore"))
b.context.(tarsum.BuilderContext).Remove(".dockerignore")
}
if rm, _ := fileutils.Matches("Dockerfile", excludes); rm == true {
os.Remove(path.Join(b.contextPath, "Dockerfile"))
b.context.(tarsum.BuilderContext).Remove("Dockerfile")
if rm, _ := fileutils.Matches(b.dockerfileName, excludes); rm == true {
os.Remove(path.Join(b.contextPath, b.dockerfileName))
b.context.(tarsum.BuilderContext).Remove(b.dockerfileName)
}

return nil
Expand Down
10 changes: 9 additions & 1 deletion builder/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"

"github.com/docker/docker/api"
"github.com/docker/docker/daemon"
"github.com/docker/docker/engine"
"github.com/docker/docker/graph"
Expand All @@ -30,6 +31,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
return job.Errorf("Usage: %s\n", job.Name)
}
var (
dockerfileName = job.Getenv("dockerfile")
remoteURL = job.Getenv("remote")
repoName = job.Getenv("t")
suppressOutput = job.GetenvBool("q")
Expand All @@ -42,6 +44,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
tag string
context io.ReadCloser
)

job.GetenvJson("authConfig", authConfig)
job.GetenvJson("configFile", configFile)

Expand All @@ -57,6 +60,10 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
}
}

if dockerfileName == "" {
dockerfileName = api.DefaultDockerfileName
}

if remoteURL == "" {
context = ioutil.NopCloser(job.Stdin)
} else if urlutil.IsGitURL(remoteURL) {
Expand Down Expand Up @@ -88,7 +95,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
if err != nil {
return job.Error(err)
}
c, err := archive.Generate("Dockerfile", string(dockerFile))
c, err := archive.Generate(dockerfileName, string(dockerFile))
if err != nil {
return job.Error(err)
}
Expand Down Expand Up @@ -118,6 +125,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
StreamFormatter: sf,
AuthConfig: authConfig,
AuthConfigFile: configFile,
dockerfileName: dockerfileName,
}

id, err := builder.Run(context)
Expand Down
4 changes: 4 additions & 0 deletions docs/man/docker-build.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ docker-build - Build a new image from the source code at PATH
# SYNOPSIS
**docker build**
[**--help**]
[**-f**|**--file**[=*Dockerfile*]]
[**--force-rm**[=*false*]]
[**--no-cache**[=*false*]]
[**-q**|**--quiet**[=*false*]]
Expand All @@ -31,6 +32,9 @@ When a Git repository is set as the **URL**, the repository is used
as context.

# OPTIONS
**-f**, **--file**=*Dockerfile*
Path to the Dockerfile to use. If the path is a relative path then it must be relative to the current directory. The file must be within the build context. The default is *Dockerfile*.

**--force-rm**=*true*|*false*
Always remove intermediate containers, even after unsuccessful builds. The default is *false*.

Expand Down
17 changes: 11 additions & 6 deletions docs/sources/reference/api/docker_remote_api_v1.17.md
Original file line number Diff line number Diff line change
Expand Up @@ -1157,16 +1157,21 @@ Build an image from Dockerfile via stdin
{"stream": "..."}
{"error": "Error...", "errorDetail": {"code": 123, "message": "Error..."}}

The stream must be a tar archive compressed with one of the
following algorithms: identity (no compression), gzip, bzip2, xz.
The input stream must be a tar archive compressed with one of the
following algorithms: identity (no compression), gzip, bzip2, xz.

The archive must include a file called `Dockerfile`
at its root. It may include any number of other files,
which will be accessible in the build context (See the [*ADD build
command*](/reference/builder/#dockerbuilder)).
The archive must include a build instructions file, typically called
`Dockerfile` at the root of the archive. The `f` parameter may be used
to specify a different build instructions file by having its value be
the path to the alternate build instructions file to use.

The archive may include any number of other files,
which will be accessible in the build context (See the [*ADD build
command*](/reference/builder/#dockerbuilder)).

Query Parameters:

- **dockerfile** - path within the build context to the Dockerfile
- **t** – repository name (and optionally a tag) to be applied to
the resulting image in case of success
- **q** – suppress verbose build output
Expand Down
40 changes: 35 additions & 5 deletions docs/sources/reference/commandline/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,11 +461,12 @@ To kill the container, use `docker kill`.

Build a new image from the source code at PATH

--force-rm=false Always remove intermediate containers, even after unsuccessful builds
--no-cache=false Do not use cache when building the image
-q, --quiet=false Suppress the verbose output generated by the containers
--rm=true Remove intermediate containers after a successful build
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
-f, --file="" Location of the Dockerfile to use. Default is 'Dockerfile' at the root of the build context
--force-rm=false Always remove intermediate containers, even after unsuccessful builds
--no-cache=false Do not use cache when building the image
-q, --quiet=false Suppress the verbose output generated by the containers
--rm=true Remove intermediate containers after a successful build
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success

Use this command to build Docker images from a Dockerfile and a
"context".
Expand Down Expand Up @@ -510,6 +511,13 @@ For example, the files `tempa`, `tempb` are ignored from the root directory.
Currently there is no support for regular expressions. Formats
like `[^temp*]` are ignored.

By default the `docker build` command will look for a `Dockerfile` at the
root of the build context. The `-f`, `--file`, option lets you specify
the path to an alternative file to use instead. This is useful
in cases where the same set of files are used for multiple builds. The path
must be to a file within the build context. If a relative path is specified
then it must to be relative to the current directory.


See also:

Expand Down Expand Up @@ -612,6 +620,28 @@ repository is used as Dockerfile. Note that you
can specify an arbitrary Git repository by using the `git://` or `git@`
schema.

$ sudo docker build -f Dockerfile.debug .

This will use a file called `Dockerfile.debug` for the build
instructions instead of `Dockerfile`.

$ sudo docker build -f dockerfiles/Dockerfile.debug -t myapp_debug .
$ sudo docker build -f dockerfiles/Dockerfile.prod -t myapp_prod .

The above commands will build the current build context (as specified by
the `.`) twice, once using a debug version of a `Dockerfile` and once using
a production version.

$ cd /home/me/myapp/some/dir/really/deep
$ sudo docker build -f /home/me/myapp/dockerfiles/debug /home/me/myapp
$ sudo docker build -f ../../../../dockerfiles/debug /home/me/myapp

These two `docker build` commands do the exact same thing. They both
use the contents of the `debug` file instead of looking for a `Dockerfile`
and will use `/home/me/myapp` as the root of the build context. Note that
`debug` is in the directory structure of the build context, regardless of how
you refer to it on the command line.

> **Note:** `docker build` will return a `no such file or directory` error
> if the file or directory does not exist in the uploaded context. This may
> happen if there is no context, or if you specify a file that is elsewhere
Expand Down
Loading

0 comments on commit eb3ea3b

Please sign in to comment.