Consider utilizing parameterized Dockerfiles as a means to reduce duplication
Investigate whether or not utilizing parameterized Dockerfiles would reduce the amount of duplication and ease the maintenance cost of the Dockerfiles. It may help increase the developer productivity.
An example of this would be to parameterize the Alpine base image tag within the Alpine Helix Dockerfiles. The base image tag appears to be the only thing different between the various versions
alpine 3.11 amd64 vs alpine 3.10 amd64
The FROM image argument value can be specified within the manifest.
First of all, my +1 for proposing to DRY stuff in this repo, it is getting baggy by day, and I think it is possible to accomplish true DRY'ness while maintaining the tidiness/readability.
After experimented a few approaches in CI runs of recent PRs and seeing what is and isn't possible/supported, I have a few ideas that I'd like to share, which I believe would scale with a few moving parts.
Here are my 2 cents...
There are two kinds of duplicates which I think worth pursuing:
- docker commands; which we solve with
WithNodeandhelixrelying on (otherwise useless) amd64 base image.
- Solution: for this, we can (and in this day and age, should) use one physical Dockerfile with multi-stages, without needing to publish an intermediate/base container to the registry.
- dependency list per OS (across versions x architectures with minor exceptions here and there).
- Background: Dockerfile grammar does not provide
INCLUDEor more accuratelyPASTEidioms for templating OOTB. Their official guidance for syntax extensibility is that we can create our own custom ones in frontend, use# syntaxdirective in Dockerfile and define some custom semantics on top, which sounds too much (maintenance) work for (relatively) a little gain. - Solution: given the above situation, the most natural thing to use is preprocessor, which any C compiler on any platform can expand (think e.g. an
#include "../helix_common_dependencies"line in aDockerfile.in). The semantics of preprocessor are well-established, well-documented and well-understood including its limitations, but it quite adequately fulfills what we need here. Using this we can describe the whole platform matrix as chain of logical partial files and callingclang -E Dockerfile.in -D SomeThingReadFrom_manifest_json...to expand/emit the realDockerfiles on-the-fly (which will be .gitignore'd) before invoking buildx etc. This likely has zero negative effects on caching. This will deduplicate it all and only requires a good, understandable logical structuring of (partial) files; which we anyway have to do with other options.- inspired by https://github.com/moby/moby/issues/735#issuecomment-508028941.
Note that our infrastructure does now have support for Dockerfile templates and are being used by the dotnet/dotnet-docker and microsoft/dotnet-framework-docker repos. See documentation and example template.
Great! I can attempt to convert Alpine 3.13 and 3.14 to use the templates and DRY out the repetitions. Most like would need your help, @mthalman on passing build-time vars correctly. :)
I presume Alpine 3.12 and below can probably be deleted, based on https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md#linux; that .NET 6 only supports Alpine 3.13+?