-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(core): natively support .dockerignore #10922
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing all this hard work! Got a few minor comments on the implementation, but overall I like where this is going.
On the topic of trust: we've made changes here before and then accidentally broke people. How solidly do you feel about this implementation? And convince me how that's justified please? 😅
@@ -97,20 +96,30 @@ export class DockerImageAsset extends CoreConstruct implements assets.IAsset { | |||
throw new Error(`Cannot find file at ${file}`); | |||
} | |||
|
|||
let ignoreMode = props.ignoreMode || IgnoreMode.DOCKER; | |||
|
|||
if (ignoreMode != IgnoreMode.DOCKER) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this restriction? I am okay with the default being DOCKER
, but can't we support the old ones as well in case people depend on it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I originally did this is because it feels plain wrong to process .dockerignore
patterns with anything other than IgnoreMode.DOCKER
.
This is the core thing that relates to the questions of backward compatibility, meaning, I'm not super confident how existing user-supplied exclude
patterns will behave when flipped from the current IgnoreMode.GLOB
(minimatch
) to IgnoreMode.DOCKER
, because both have different nuanced behavior.
So with this in mind, in the event that users may have become inadvertently dependent on the current broken behavior, maybe it is a good idea to still allow that old behavior as an option. Must we go even further and leave legacy as the default, with IgnoreMode.DOCKER
as opt-in? It would be safest, though it would leave major out-of-the-box ergonomics on the table.
See 2f0b88f where I removed the restriction.
Another option is to default ignoreMode
to the legacy IgnoreMode.GLOB
if exclude
was passed by the user but ignoreMode
wasn't, since it likely means that they may be dependent on the legacy behavior.
Then again, maybe just letting users opt back into the legacy behavior if they run into issues is good enough. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went a little more into this in the comment below.
!Dockerfile | ||
!.dockerignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove these two since they're implicit, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. f768aa1
import { nodeunitShim, Test } from 'nodeunit-shim'; | ||
import { IgnoreStrategy } from '../../lib/fs'; | ||
|
||
nodeunitShim({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I ask you to rewrite these to Jest tests and to use expect(...).toEqual(...)
? It'll generate better error messages.
In fact, I think I'd like to see tests of the form:
describe('globIgnorePattern', () => {
test('excludes nothing by default', () => {
const ignore = IgnoreStrategy.glob([]);
expect(filterFiles(ignore, [
'some/file/path',
]).toEqual([
'some/file/path',
]);
});
});
function filterFiles(strat: IgnoreStrategy, files: string[]) {
return files.filter(file => !strat.ignores(file));
}
Feels like this would produce understandable outputs if the tests fail for some reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me. See b47b404
* @param filePath file path to be assessed against the pattern | ||
* @returns `true` if the file should be ignored | ||
*/ | ||
public abstract ignores(filePath: string): boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For all of these ignore functions, I'd like very much to be very clear about the expectations of absoluteness/relativeness of these file paths.
It seems like right now you're implicitly expecting all paths to be relative to the directory that contains the ignore file.
Please be very clear about that, documentation-wise, and add assertions/checks wherever it makes sense: reject absolute paths, etc.
Or, add in a way to normalize paths which can handle both absolute and relative. Or, expect absolute and relativize before checking. As long as the contract is very very clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. The root path against which paths passed to ignores()
were being relativized was an implicit dependency of an IgnoreStrategy
.
See fe58be1
I made this an explicit dependency. All paths are expected to be absolute paths. This is documented and reflected in the variable naming. I also throw exceptions if relative paths are passed where absolute paths are expected (which is everywhere in this PR).
IgnoreStrategy and the .ignores() method will take absolute paths. The absolute path passed to .ignores() is relativized against the absolute root path passed to IgnoreStrategy.
I went a little over this in the section on Backward Compatibility in the original post. Note: To be clear, everything here that follows is specifically concerning I am confident about the implementation because it's pretty much entirely delegating to packages that purely focus on implementing this behavior, compared to currently mashing I am not super confident about what would happen if we have user-supplied The reason this matters is because ideally we would make The consequences of differing matching behavior is:
(On the flip side, it may help to keep in mind and consider that we're already witnessing something like this, where we're processing In the first case, this would mean that the resulting asset is a little bloated. (Are there other security concerns?) In the second case, this would mean that some downstream process would be missing a file that it requires (e.g. to build the docker image). That is, downstream could break. The options I see are:
I think users, especially future users, would most benefit from having However, I recognize that you all may want to take a more conservative approach, in which case it seems like the second option is the only option. I'll be thinking about the third option because it may get us the best of both worlds: default Hopefully I've explained the situation well enough, but if not please let me know if you'd like some rephrasing or elaboration. I'm also happy to hear of other options that may not have occurred to me. |
I think to be safe I'm going to add a feature flag to trigger the new behavior. EDIT: Before we do that, I'm trying to reason through the consequences. The biggest issue I can see is how files that used to be present would now be missing, as that would lead users to deploying broken applications. It's hard to predict whether or not this will break users, and it seems critical. The flag is probably the safe way to go. |
@blaenk how do you feel about the current proposal? |
Agreed. I don't know for sure that this can happen, but it seems like a possibility. I agree with you that it's probably best to keep it as a feature flag, I totally forgot that CDK had that concept. It seems like the best approach:
I think that's the best we can hope for, and actually seems reasonable to me. |
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
Patterns in
.dockerignore
used to be interpreted as globs, which has different behavior from how.dockerignore
is supposed to be interpreted. Specifically it was hard to properly ignore whole directories at once (such as a largenode_modules
).This PR adds an
ignoreMode
flag which controls how the ignore patterns should be interpreted. The choices are:IgnoreMode.GLOB
- interpret as beforeIgnoreMode.GIT
- interpret as in.gitignore
IgnoreMode.DOCKER
- interpret as in.dockerignore
Users might have dependencies on the current behavior, which is why the default
ignoreMode
depends on a feature flag:@aws-cdk/aws-ecr-assets:dockerIgnoreSupport
(which is set totrue
for new projects) enables the new, correct Docker-ignore behavior by default.Closes #10921
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license