Split Dockerfile into parallel build groups for GitHub Actions#23
Split Dockerfile into parallel build groups for GitHub Actions#23
Conversation
This reduces CI build time from ~3 hours (sequential) to ~30-45 minutes (parallel groups). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Refactors the Docker multi-stage build into multiple named “group” targets so GitHub Actions can build/cache them in parallel, then assembles a final runtime image using cached outputs to reduce CI wall-clock time.
Changes:
- Split the Dockerfile into
builder-buster/builder-bullseyebase stages plusgroup-*build targets and a final aggregation stage. - Add per-target stripping/cleanup and aggregate outputs from each group into the final image.
- Introduce a GitHub Actions workflow that builds each
group-*target in a matrix and then builds/pushes the final image using registry-backed BuildKit cache.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| Dockerfile | Introduces per-version build groups as separate stages and aggregates their artifacts into the final image. |
| .github/workflows/build.yml | Adds a parallelized CI build that caches each group target separately and then builds/pushes the final image. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| RUN echo "deb http://archive.debian.org/debian/ buster main contrib non-free" > /etc/apt/sources.list && \ | ||
| echo "deb http://archive.debian.org/debian-security/ buster/updates main contrib non-free" >> /etc/apt/sources.list && \ |
There was a problem hiding this comment.
This stage uses archive.debian.org (Buster) and disables Acquire::Check-Valid-Until for the apt-get update here, but there is another apt-get update later in the same stage (after adding i386 + deb-src) that will still hit expired Valid-Until unless the check is also disabled/persisted. To avoid flaky/failing builds, apply Check-Valid-Until=false to the later update as well (or add an apt conf entry so it applies to all updates in this stage).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| RUN rm -rf Rakefile versions/ patch/ DIST build/*/log build/*/ruby*/ \ | ||
| build/*/man build/*/share/man build/*/share/doc build/*/share/ri && \ | ||
| rm -f build/*/lib/libruby-static.a build/*/bin/gcc build/*/bin/cc | ||
| RUN find /build-all-ruby -type f \( -name ruby -o -name '*.so' \) -exec sh -c 'file $1 | grep -q "not stripped"' - '{}' \; -print0 | xargs -0 strip |
There was a problem hiding this comment.
The find … -print0 | xargs -0 strip pipeline can fail when no files match the file | grep -q "not stripped" filter (GNU xargs will still invoke strip with no args). Consider avoiding xargs here (e.g., use find … -exec strip … {} +) or add --no-run-if-empty/-r to xargs; also quoting the filename in the file call would be safer.
| RUN rm -rf Rakefile versions/ patch/ DIST build/*/log build/*/ruby*/ \ | ||
| build/*/man build/*/share/man build/*/share/doc build/*/share/ri && \ | ||
| rm -f build/*/lib/libruby-static.a build/*/bin/gcc build/*/bin/cc | ||
| RUN find /build-all-ruby -type f \( -name ruby -o -name '*.so' \) -exec sh -c 'file $1 | grep -q "not stripped"' - '{}' \; -print0 | xargs -0 strip |
There was a problem hiding this comment.
Same issue as other groups: this find … | xargs strip can invoke strip with no args if the file | grep filter selects nothing. Prefer find … -exec strip … {} + or add xargs --no-run-if-empty/-r (and quote the filename passed to file).
| RUN rm -rf Rakefile versions/ patch/ DIST build/*/log build/*/ruby*/ \ | ||
| build/*/man build/*/share/man build/*/share/doc build/*/share/ri && \ | ||
| rm -f build/*/lib/libruby-static.a build/*/bin/gcc build/*/bin/cc | ||
| RUN find /build-all-ruby -type f \( -name ruby -o -name '*.so' \) -exec sh -c 'file $1 | grep -q "not stripped"' - '{}' \; -print0 | xargs -0 strip |
There was a problem hiding this comment.
Same issue as other groups: if the file | grep -q "not stripped" test matches nothing, xargs may still run strip with no arguments and fail the build. Use find … -exec strip … {} + or add xargs --no-run-if-empty/-r.
| RUN rm -rf Rakefile versions/ patch/ DIST build/*/log build/*/ruby*/ \ | ||
| build/*/man build/*/share/man build/*/share/doc build/*/share/ri && \ | ||
| rm -f build/*/lib/libruby-static.a build/*/bin/gcc build/*/bin/cc | ||
| RUN find /build-all-ruby -type f \( -name ruby -o -name '*.so' \) -exec sh -c 'file $1 | grep -q "not stripped"' - '{}' \; -print0 | xargs -0 strip |
There was a problem hiding this comment.
Same issue as other groups: xargs strip may be executed with no operands when the preceding find prints nothing, causing the layer to fail. Prefer find … -exec strip … {} + or add xargs --no-run-if-empty/-r.
| RUN rm -rf Rakefile versions/ patch/ DIST build/*/log build/*/ruby*/ \ | ||
| build/*/man build/*/share/man build/*/share/doc build/*/share/ri && \ | ||
| rm -f build/*/lib/libruby-static.a build/*/bin/gcc build/*/bin/cc | ||
| RUN find /build-all-ruby -type f \( -name ruby -o -name '*.so' \) -exec sh -c 'file $1 | grep -q "not stripped"' - '{}' \; -print0 | xargs -0 strip |
There was a problem hiding this comment.
Same issue as other groups: consider avoiding … -print0 | xargs -0 strip because xargs can run strip with no args if no files are selected. Use find … -exec strip … {} + or add xargs --no-run-if-empty/-r.
| RUN rm -rf Rakefile versions/ patch/ DIST build/*/log build/*/ruby*/ \ | ||
| build/*/man build/*/share/man build/*/share/doc build/*/share/ri && \ | ||
| rm -f build/*/lib/libruby-static.a build/*/bin/gcc build/*/bin/cc | ||
| RUN find /build-all-ruby -type f \( -name ruby -o -name '*.so' \) -exec sh -c 'file $1 | grep -q "not stripped"' - '{}' \; -print0 | xargs -0 strip |
There was a problem hiding this comment.
Same issue as other groups: xargs strip can fail if the file | grep predicate filters everything out (xargs may still invoke strip with no inputs). Prefer find … -exec strip … {} + or add xargs --no-run-if-empty/-r.
| gcc \ | ||
| rdfind \ | ||
| ${system_ruby} \ |
There was a problem hiding this comment.
rdfind is now installed in the final runtime image only to run the hardlink dedup step. If the runtime image size/attack surface matters, consider running rdfind in an intermediate build stage (after aggregating the group outputs) and then copying the resulting /build-all-ruby into the final stage, or purging rdfind after the rdfind run within the same layer.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
…er-buster Co-authored-by: hsbt <12301+hsbt@users.noreply.github.com>
Fix expired Valid-Until bypass missing on second apt-get update in builder-buster
… image Co-authored-by: hsbt <12301+hsbt@users.noreply.github.com>
Move rdfind dedup to intermediate aggregator stage, remove from final runtime image
|
Let's try to this. |
This reduces CI build time from ~3 hours (sequential) to ~30-45 minutes (parallel groups).