Skip to content

Add a new --ignore-file flag to docker build#29405

Closed
vdemeester wants to merge 1 commit intomoby:masterfrom
vdemeester:build-ignore-file-flag
Closed

Add a new --ignore-file flag to docker build#29405
vdemeester wants to merge 1 commit intomoby:masterfrom
vdemeester:build-ignore-file-flag

Conversation

@vdemeester
Copy link
Copy Markdown
Member

@vdemeester vdemeester commented Dec 14, 2016

This makes it possible to use something else than .dockerignore as a ignore file. Should fix #12886.

$ docker build -t foo/bar --ignore-file mydockerignore .
# […]

Questions/todos :

  • support multiple files ? (--ignorefile f1 --ignorefile f2 and merge the content naively)
  • update documentation

/cc @thaJeztah @duglin @tiborvass

🐸

Signed-off-by: Vincent Demeester vincent@sbr.pm

@duglin
Copy link
Copy Markdown
Contributor

duglin commented Dec 14, 2016

Given how many people have asked for this, if we can do it nicely, then I'm in favor of it.

I don't think we need to support multiple files. Once we have a single file done we can explore adding it if people scream for it, but I'm not sure I see a lot of value yet. Should be easy to add though.

@thaJeztah
Copy link
Copy Markdown
Member

I agree with @duglin; baby steps, and just having this is already a good start :-)

@vdemeester vdemeester force-pushed the build-ignore-file-flag branch from 9917d3b to 63c976b Compare December 14, 2016 14:18
api/swagger.yaml Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be "Path within the build context to the dockerignore file." (I think)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e., missing file at the end

@vdemeester vdemeester force-pushed the build-ignore-file-flag branch from 63c976b to 928ce93 Compare December 14, 2016 14:19
@thaJeztah
Copy link
Copy Markdown
Member

Design looks good to me

@vdemeester
Copy link
Copy Markdown
Member Author

oh I guess it's breaking the cache & co… hum… I missed something somewhere 😂

@vdemeester vdemeester force-pushed the build-ignore-file-flag branch 2 times, most recently from 7ab916b to 20ffb77 Compare December 15, 2016 13:42
@tianon
Copy link
Copy Markdown
Member

tianon commented Dec 15, 2016

Any particular reason we can't have both --ignore and --ignore-file? 😇

@vdemeester
Copy link
Copy Markdown
Member Author

@tianon what do you mean ?

@tianon
Copy link
Copy Markdown
Member

tianon commented Dec 15, 2016

So I could just docker build --ignore .git rather than writing a whole new file 😇

@tianon
Copy link
Copy Markdown
Member

tianon commented Dec 15, 2016

Also, can this be specified more than once?

Right now, if I wanted to just "build this thing, but also ignore .git", I need to copy the entire .dockerignore verbatim to add a single line (no concept of includes there either), and then keep it in sync over time (for example, I've cloned someone else's repo and want to ignore extra stuff I've got locally that upstream doesn't care about -- the types of things we tell people to put in .git/info/excludes rather than .gitignore as concrete examples).

@vdemeester
Copy link
Copy Markdown
Member Author

@tianon I hear you 👼 I think we should go step by step, and I would follow up this one with proposal on a --ignore and why not support of multiple --ignore-file too.

@thaJeztah
Copy link
Copy Markdown
Member

@vdemeester agreed; was discussing this with @tianon already; this is a good start, and I'm open to future improvements

@vdemeester
Copy link
Copy Markdown
Member Author

yup, and I really really like the idea of --ignore .git for example, that could help a lot of workflow quite a bit 👼

@justincormack
Copy link
Copy Markdown
Contributor

@tianon you don't really want multiple ignores we want to be able to specify multiple bits of context, additive not subtractive. I will do a PR at some point.

api/swagger.yaml Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be .dockerignore I think

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this ignoreFile, its very confusing to have to work out what fileName refers to.

@vdemeester vdemeester force-pushed the build-ignore-file-flag branch from 20ffb77 to 594bd8e Compare December 25, 2016 19:30
@vdemeester
Copy link
Copy Markdown
Member Author

Rebase and @justincormack comments taken into account 👼

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this comment/logic is true in the case of someone being explicit about it. I would prefer if we threw an error if we were told to use a file and that file didn't exist. I think it should only silently ignore the "not found" error in the default case.

Copy link
Copy Markdown
Member Author

@vdemeester vdemeester Jan 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would mean making the server side know what the default is (i.e. .dockerignore), I think it make sense, even just for retro-compatibility API-wise 👼. Hum actually I'm not sure anymore…

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good one; yes (without looking at how to accomplish it), I think it someone specified a .dockerignore file, and it doesn't exist, that should be an error. And if no file was specified, it should not be an error.

But, now sure if that can be achieved 😊

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way to know what's going on is to use an empty string to mean "not specified" - then in Process() if its "" you know you can default to .dockerignore and ignore the error if the file isn't there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to my previous comment, if we don't send this query parameter at all when the --ignore-file wasn't specified then the server should be able to ignore the "not found" error because it's defaulting.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@duglin so docker build --ignore-file .dockerignore would behave differently than docker build ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. I was thinking that docker build --ignore-file .dockerignore would do a couple of things:
1 - check for .dockerignore so that we can fail fast if its not there. Note, it would only do this check if the user specified the --ignore-file flag.
2 - send the appropriate ignore-file query param to the daemon - but only when the user specified a value. Meaning, if they didn't specify the --ignore-file flag then there would be no query param.
3 - the daemon would detect whether or not the query param was specified. If specified, regardless of its value, if the file isn't there then it would generate an error. If not there then we use the default value but do not generate an error if the file is missing. This is similar to how we act on the client - no error for a missing file when we're defaulting, but generate an error when the user was explicit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so this should be what is implemented now 👼

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funky formatting

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add a test for when the file isn't found - regardless of whether you change things based on my previous comments.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the cli test for this. I think we may need one at the "api" level too - meaning one where we call the REST API with a missing dockerignore file since this just tests the client side, not the server's side.

@duglin
Copy link
Copy Markdown
Contributor

duglin commented Dec 25, 2016

we'll need to update the docs for this.

@vdemeester vdemeester force-pushed the build-ignore-file-flag branch from 21f7c7d to 170f251 Compare January 2, 2017 16:02
@vdemeester
Copy link
Copy Markdown
Member Author

@duglin updated to take your comment into account 👼, added a tests case on that too

This makes it possible to use something else than .dockerignore as a
ignore file.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
@vdemeester vdemeester force-pushed the build-ignore-file-flag branch from 170f251 to 568d669 Compare January 2, 2017 17:31
err = dockerIgnore.Process(ignoreFile, []string{b.options.Dockerfile})
if !ignoreErr && err != nil {
return err
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason this "ignoreErr" logic isn't in Process()? I worry about putting it here because it assumes that the only possible error from Process() is that .dockerignore doesn't exist - which right now is true. But, if at some point in the future another error is returned then the next person to touch this code might do something really hacky like start checking for certain errors - or worse, forget to even check at all and introduce a bug.

Its not a big thing, but I would prefer if all of this new logic were moved into Process() so that we can add the ignoreErr logic right at the os.IsNotExist(err) line of code and avoid this potential headache later on. I think the code will be smaller/easier too.

}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("Error reading .dockerignore: %v", err)
return nil, fmt.Errorf("Error reading dockerignore file: %v", err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add the name of the dockerignore file to this error message to help to user when they "fat finger" it?

if err == nil {
excludes, err = dockerignore.ReadAll(f)
if err != nil {
if !ignoreErr && err != nil {
Copy link
Copy Markdown
Contributor

@duglin duglin Jan 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right spot for this? if we get an os.IsNotExist() above then won't f be nil and then the ReadAll() will return immediately with nil, nil? It seems as though ignoreErr might not be needed at all, or you'll want to add it to the if err == nil { line (2 lines above) to skip the ReadAll() in that case. But, it actually seems like that "if" might be better written as if f != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edited comment, was missing a "not"

@justincormack
Copy link
Copy Markdown
Contributor

I do think that specifying the files you don't want is totally wrong by the way. Subtractive specification is hard. I write all my docker builds as tar cf - Dockerfile files I want | docker build - so I can additively specify files. If docker build accepted more than one file for the context life would be much easier and no one would mess around with .dockerignore at all, they would just list the files they wanted...

@thaJeztah
Copy link
Copy Markdown
Member

Well, it's modeled after .gitignore. Also, you'd still be able to do this, by ignoring everything, then add files you do want as not ignored

@justincormack
Copy link
Copy Markdown
Contributor

@thaJeztah the use case quoted in the issue is multiple Dockerfiles in one directory. I use these a lot and ignore is absolutely the wrong way to deal with them.

for example, taking one of my things:
Dockerfile.1 uses file.1, directory.1, file.shared, directory.shared
Dockerfile.2 uses file.2, directory.2, file.shared, directory.shared

So I do

tar cf - Dockerfile.1 file.1 directory.1 file.shared directory.shared | docker build -
tar cf - Dockerfile.2 file.2 directory.2 file.shared directory.shared | docker build -

and thats great. I want to do, in the future

docker build -f Dockerfile.1 file.1 directory.1 file.shared directory.shared
docker build -f Dockerfile.2 file.2 directory.2 file.shared directory.shared

After this PR I would have to create two more files:

ignore.1 < Dockerfile.2 file.2 directory.2
ignore.2 <  Dockerfile.1 file.1 directory.1

and then do

docker build -f Dockerfile.1 --ignore ignore.1 .
docker build -f Dockerfile.2 --ignore ignore.2 .

This is a terrible way to do things, the ignore file for the first build is listing the files for the second build because it is subtractive not additive. If the second build needs more files, I need to add them to the ignore file for the first one. The more builds I have the more I have to list in each file.

In addition if you use make, with the additive way the files for the context are the make dependencies, so I tend to just write:

build.1: Makefile.1 file.1 file.shared directory.shared
       tar cf - $^ | docker build -

which could be the simpler docker build $^ with multi file contexts.

@duglin
Copy link
Copy Markdown
Contributor

duglin commented Jan 8, 2017

@justincormack what's interesting about your use case is that its basically asking for a new Dockerfile command to list the files to be included in the build context. Then you wouldn't need to jump thru the tar steps you do - it would make the Dockerfile more self-describing.

@justincormack
Copy link
Copy Markdown
Contributor

justincormack commented Jan 8, 2017 via email

@thaJeztah
Copy link
Copy Markdown
Member

This is a terrible way to do things, the ignore file for the first build is listing the files for the second build because it is subtractive not additive. If the second build needs more files, I need to add them to the ignore file for the first one. The more builds I have the more I have to list in each file.

It really depends on the use case; if the only difference between Dockerfile1 and Dockerfile2 is to have a single directory excluded, having to specify everything else is terrible.

I think allowing multiple paths to be set for the build-context is slightly orthogonal to this PR; this PR just brings having a second Dockerfile "on parity" with the "main" Dockerfile.

I don't think both options are mutually exclusive though

what's interesting about your use case is that its basically asking for a new Dockerfile command to list the files to be included in the build context. Then you wouldn't need to jump thru the tar steps you do - it would make the Dockerfile more self-describing.

Yes, I think we've discussed such options before, i.e., have the client only send whats actually needed to the daemon. Previous discussions were more about doing so automatically, but having a way to explicitly set what's needed is probably an easier solution.

I do think something like that would make sense, and makes it easier to use, as having to manually specify all paths to include during build makes it quite hard to use ("to build this Dockerfile, specify the following options during docker build ...."). Ideally build should be just docker build -t foo . in most cases.

@justincormack
Copy link
Copy Markdown
Contributor

@thaJeztah no, because when you make changes, eg you refactor your code, you have to refactor a negative, which basically means unrelated code. As you see from my example, the ignore file for build 1 only refers to files that are used in build 2, so if you change build 2, you have to change ignore file 1. This means when you try to split your code, eg split these into different directories, the commit history is horribly entangled.

The model for docker build is not git, it is go build. go build takes a directory or a list of files. It does not have an --exclude flag, nor does it have an exclude file. Sure this might make your command line shorter in extremely rare situations, but you will not get that added to go as the design is wrong: build processes specify what they are built from, not what they are not built from. This is the whole basis of dependency management and analysis.

This change is not actively harmful, it is just adding bloat by taking one existing design decision to a really complex extreme, instead of changing a much simpler part of the design which would make this totally unnecessary.

@justincormack
Copy link
Copy Markdown
Contributor

I opened #30101 to discuss alternatives

@LK4D4
Copy link
Copy Markdown
Contributor

LK4D4 commented Jan 27, 2017

I see that this is in code-review, but discuss looks like design-review.
@vdemeester Should we proceed with review?

@vdemeester
Copy link
Copy Markdown
Member Author

@LK4D4 maybe it should go back to design-review if there is no consensus 😅

@LK4D4
Copy link
Copy Markdown
Contributor

LK4D4 commented Jan 27, 2017

@vdemeester still @justincormack's proposal was proven to be undoable. Maybe we should discuss on maintainers meeting.

@thaJeztah
Copy link
Copy Markdown
Member

We discussed this in the maintainers meeting, and think that this is not the right approach; having the builder selectively upload content that is used, will probably be a better solution, and something that is actively looked in to

@thaJeztah thaJeztah closed this Feb 2, 2017
@vdemeester vdemeester deleted the build-ignore-file-flag branch February 2, 2017 20:27
@vdemeester
Copy link
Copy Markdown
Member Author

ok :)

@thaJeztah
Copy link
Copy Markdown
Member

sorry 😢 😄

@dvalentiate
Copy link
Copy Markdown

Has anything come along to replace the proposed functionality of this PR?

Not having --ignore-file is making life difficult for the teams I'm working with.

@thaJeztah
Copy link
Copy Markdown
Member

@dvalentiate the issue associated with this PR is #12886, feel free to provide input/suggestions there

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for specifying .dockerignore file with -i/--ignore

8 participants