Skip to content

ci: optimize Go and Docker layer caching#3213

Merged
tac0turtle merged 3 commits intomainfrom
ci/optimize-caching-3196
Apr 1, 2026
Merged

ci: optimize Go and Docker layer caching#3213
tac0turtle merged 3 commits intomainfrom
ci/optimize-caching-3196

Conversation

@tac0turtle
Copy link
Copy Markdown
Contributor

@tac0turtle tac0turtle commented Mar 30, 2026

Summary

  • Fix apps/testapp/Dockerfile: was copying all source before go mod download, invalidating the download layer on every code change. Now copies go.mod/go.sum first, downloads deps, then copies source — same pattern already used by the evm Dockerfile.
  • Add GHA Docker layer cache to docker-build-push.yml via cache-from/cache-to: type=gha with per-app scopes so the 3 parallel image builds don't evict each other's cache.
  • Add cache-dependency-path: "**/go.sum" to all actions/setup-go steps in test.yml, docker-tests.yml, and lint.yml. This repo has multiple go.mod files; the glob ensures the Go module cache key covers all of them.
  • Add scope to e2e Docker cache in test.yml (scope=ev-node-testapp) to align with the docker-build-push.yml naming and avoid cross-job cache conflicts.

Closes #3196

Test plan

  • Verify Docker build jobs in CI show cache hits on re-runs where only non-go.mod/go.sum files changed
  • Verify Go module cache is restored in setup-go steps (check "Restore cache" step output in Actions logs)
  • All existing tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Improved Docker image build caching in CI (scoped persistent cache) to speed up builds and reuse layers
    • Enabled explicit Go module caching across workflows for faster test and lint runs
    • Bumped Go toolchain used in service build images and refined build layering to reduce image rebuild time and streamline artifact creation

- Fix apps/testapp/Dockerfile: copy go.mod/go.sum before source so
  the go mod download layer is stable across code-only changes
- Add cache-from/cache-to GHA cache to docker-build-push.yml with
  per-app scopes to prevent cache eviction between parallel builds
- Add cache-dependency-path: "**/go.sum" to all actions/setup-go
  steps in test.yml, docker-tests.yml, and lint.yml so the module
  cache key covers all go.sum files in the multi-module repo
- Add explicit scope to the e2e Docker build cache in test.yml to
  align with the docker-build-push.yml scope naming

Closes #3196

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedMar 31, 2026, 5:39 PM

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Mar 30, 2026

Claude finished @tac0turtle's task in 2m 43s —— View job


Code Review

  • Read AGENTS.md and CLAUDE.md for repo guidelines
  • Review changed files (Dockerfiles, CI workflows)
  • Analyze caching correctness and potential issues
  • Post review findings

Summary

This is a solid CI caching improvement PR. The changes are correct, well-scoped, and address a real Docker layer cache invalidation bug in apps/testapp/Dockerfile. One pre-existing hadolint issue remains, and there's a minor inconsistency in apps/evm/Dockerfile worth noting.


apps/testapp/Dockerfile

Remaining DL3003 violation (apps/testapp/Dockerfile:24)

RUN go mod download && (cd apps/testapp && go mod download)

The cd pattern inside RUN still technically violates hadolint DL3003. The prior commit message claims to fix hadolint warnings, but this remained. Since the lint step uses failure-threshold: error and DL3003 is a warning-level rule, CI won't fail — but it's worth a clean fix. The correct approach:

RUN go mod download
WORKDIR /ev-node/apps/testapp
RUN go mod download
WORKDIR /ev-node

Or simply add #hadolint ignore=DL3003 above the RUN line (consistent with how the repo already ignores DL3018). Fix this →

Layer caching logic is correct: Copying both go.mod/go.sum pairs before the go mod download steps is the right pattern for this multi-module layout (no go.work file, so each module needs its own download).


apps/evm/Dockerfile — Pre-existing inconsistency (not introduced here)

The evm Dockerfile still doesn't copy apps/evm/go.mod/apps/evm/go.sum before go mod download:

COPY go.mod go.sum ./
RUN go mod download   # only downloads root deps
COPY . .
WORKDIR /src/apps/evm
RUN go mod tidy && ... go build -o evm .

Since apps/evm has its own go.mod (confirmed), changes to its dependencies won't invalidate the cache layer. Compare with apps/grpc/Dockerfile which correctly pre-copies 4 module files before download. This isn't introduced by this PR but is worth a follow-up for consistency.


CI Workflows — Looks good

docker-build-push.yml: cache-from/cache-to: type=gha,scope=${{ matrix.app.name }} with per-app scoping is correct. Cross-checked against ci.yml — the matrix names are ev-node-evm, ev-node-grpc, ev-node-testapp, which match the explicit scope used in test.yml's e2e step (scope=ev-node-testapp). ✓

test.yml: The cache-dependency-path: "**/go.sum" glob is the right choice for this repo with 10+ go.mod files. Note this means a change to any go.sum busts the Go module cache for all jobs — this is conservative but correct. ✓

docker-tests.yml: The go-version-file points to ./test/docker-e2e/go.mod while cache-dependency-path uses **/go.sum — this over-invalidates slightly (cache busts on any go.sum change, not just test/docker-e2e/go.sum), but it's safe. ✓

lint.yml: cache-dependency-path: "**/go.sum" added to golangci-lint setup. The Go module cache will now correctly reflect all module dependencies. ✓


Minor

The go build -o /go/bin/testapp . in apps/testapp/Dockerfile:30 is cleaner than the original go install — matches grpc's pattern and avoids $GOPATH path ambiguity. ✓

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

Adds scoped Buildx/GHA Docker layer caching and consistent Go module caching (cache-dependency-path: "**/go.sum") across workflows; bumps Go base images to 1.26 in several Dockerfiles; restructures apps/testapp builder to copy module files and run go mod download before building to improve layer cache reuse.

Changes

Cohort / File(s) Summary
GitHub Actions workflows
.github/workflows/docker-build-push.yml, .github/workflows/docker-tests.yml, .github/workflows/lint.yml, .github/workflows/test.yml
Added cache-dependency-path: "**/go.sum" to actions/setup-go@v6.3.0 steps for explicit Go module caching; configured Buildx/GHA cache scoping (cache-from/cache-to) for Docker build steps (scoped GHA cache entries).
Testapp Dockerfile (builder restructure)
apps/testapp/Dockerfile
Bumped builder base to golang:1.26; changed build stage to copy root and apps/testapp go.mod/go.sum, run go mod download, then set WORKDIR and go build -o /go/bin/testapp . to improve layer caching.
Other Dockerfiles (Go version bumps)
apps/evm/Dockerfile, apps/grpc/Dockerfile, tools/local-da/Dockerfile
Updated build-stage base images from golang:1.25(-alpine) to golang:1.26(-alpine); no other functional changes to build commands or runtime stages.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibbled go.sum, hid layers in the ground,
Caches snugged tight, no downloads abound,
Builders hop faster, images stash with glee,
CI wakes refreshed — a carrot feast for me! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'ci: optimize Go and Docker layer caching' clearly and concisely summarizes the main changes: adding caching optimizations for both Go modules and Docker layers across CI workflows.
Description check ✅ Passed The pull request description provides comprehensive details on all changes made, including the specific fixes to Dockerfiles, GHA Docker layer cache additions, Go module cache configuration updates, and references the closed issue #3196 with a clear test plan.
Linked Issues check ✅ Passed The PR fully addresses issue #3196's objective to optimize CI and reduce Docker build time by: (1) implementing GHA Docker layer caching with per-app scopes to prevent cache eviction [docker-build-push.yml], (2) adding cache-dependency-path to Go setup steps to cover multiple go.mod files [test.yml, docker-tests.yml, lint.yml], and (3) optimizing testapp Dockerfile's layer caching pattern [apps/testapp/Dockerfile].
Out of Scope Changes check ✅ Passed All changes are directly scoped to the stated objectives: Go version bumps in Dockerfiles (1.25→1.26) align with optimization goals, and all workflow and Dockerfile modifications serve the CI optimization and caching purposes outlined in issue #3196.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/optimize-caching-3196

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.43%. Comparing base (8d68f9d) to head (7e08c9d).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3213      +/-   ##
==========================================
+ Coverage   61.41%   61.43%   +0.02%     
==========================================
  Files         120      120              
  Lines       12474    12504      +30     
==========================================
+ Hits         7661     7682      +21     
- Misses       3953     3960       +7     
- Partials      860      862       +2     
Flag Coverage Δ
combined 61.43% <ø> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/testapp/Dockerfile`:
- Around line 20-23: The Dockerfile currently only primes the root module cache
(COPY go.mod go.sum ./ and RUN go mod download) and uses a banned cd pattern
(RUN cd apps/testapp && go install .); update the build stage to COPY the
testapp module files (apps/testapp/go.mod and apps/testapp/go.sum) into the
image before running dependency download, switch to using WORKDIR to enter
apps/testapp instead of the cd pattern, and run go mod download / go install
from that WORKDIR (replace RUN cd apps/testapp && go install . with a WORKDIR
apps/testapp and RUN go install ./... or equivalent) so cache invalidation is
correct and hadolint DL3003/DL3062 are resolved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bffcc61d-516e-4b29-8a76-25441fe5dcca

📥 Commits

Reviewing files that changed from the base of the PR and between 8d68f9d and 91fd6fa.

📒 Files selected for processing (5)
  • .github/workflows/docker-build-push.yml
  • .github/workflows/docker-tests.yml
  • .github/workflows/lint.yml
  • .github/workflows/test.yml
  • apps/testapp/Dockerfile

…nings

Also copy apps/testapp/go.mod and apps/testapp/go.sum before running
go mod download so the testapp's own dependencies are cached in their
own layer (separate from the root module).

Replace RUN cd ... && go install with WORKDIR + go build to resolve
hadolint DL3003 and DL3062.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
apps/testapp/Dockerfile (1)

24-24: ⚠️ Potential issue | 🟠 Major

Use WORKDIR instead of cd in the dependency download step.

Line 24 still uses cd inside RUN, which triggers hadolint DL3003 and currently fails lint CI. Move directory switching to WORKDIR and run the second go mod download there.

Suggested patch
 COPY go.mod go.sum ./
 COPY apps/testapp/go.mod apps/testapp/go.sum ./apps/testapp/
-RUN go mod download && (cd apps/testapp && go mod download)
+RUN go mod download
+WORKDIR /ev-node/apps/testapp
+RUN go mod download
 
 # Copy the rest of the source and build.
+WORKDIR /ev-node
 COPY . .
 
 WORKDIR /ev-node/apps/testapp
 RUN go build -o /go/bin/testapp .
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/testapp/Dockerfile` at line 24, The RUN line uses "cd" which triggers
hadolint DL3003; replace the inline "cd apps/testapp && go mod download" by
switching the build context to that directory with a WORKDIR instruction (e.g.,
set WORKDIR to the app directory before running the second go mod download) and
then run "go mod download" there; update the Dockerfile so the first go mod
download runs in the repo root, add WORKDIR apps/testapp, run go mod download,
and if needed restore the previous WORKDIR afterward.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/testapp/Dockerfile`:
- Line 24: The RUN line uses "cd" which triggers hadolint DL3003; replace the
inline "cd apps/testapp && go mod download" by switching the build context to
that directory with a WORKDIR instruction (e.g., set WORKDIR to the app
directory before running the second go mod download) and then run "go mod
download" there; update the Dockerfile so the first go mod download runs in the
repo root, add WORKDIR apps/testapp, run go mod download, and if needed restore
the previous WORKDIR afterward.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a3e787ca-8781-4538-bf6a-07bdd5849a21

📥 Commits

Reviewing files that changed from the base of the PR and between 91fd6fa and ee1e017.

📒 Files selected for processing (1)
  • apps/testapp/Dockerfile

@tac0turtle tac0turtle requested a review from auricom March 30, 2026 17:58
@julienrbrt
Copy link
Copy Markdown
Member

Unrelated, but shall we start building the apps with Go 1.26 to enjoy the new GC improvements?

@tac0turtle
Copy link
Copy Markdown
Contributor Author

Unrelated, but shall we start building the apps with Go 1.26 to enjoy the new GC improvements?

done

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/testapp/Dockerfile`:
- Line 24: The RUN line uses a subshell with cd (RUN go mod download && (cd
apps/testapp && go mod download)) which triggers hadolint DL3003; replace the
subshell by switching WORKDIR to apps/testapp before running its go mod download
and then reset WORKDIR (or use a second RUN) so you run go mod download in the
project directory without cd; update the Dockerfile to add a WORKDIR
apps/testapp + RUN go mod download (and then WORKDIR back to original if needed)
or split into two RUNs, referencing the existing RUN command and the
apps/testapp path to locate where to change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 332401e1-8a27-485f-81c9-c867434a785e

📥 Commits

Reviewing files that changed from the base of the PR and between ee1e017 and 7e08c9d.

📒 Files selected for processing (4)
  • apps/evm/Dockerfile
  • apps/grpc/Dockerfile
  • apps/testapp/Dockerfile
  • tools/local-da/Dockerfile
✅ Files skipped from review due to trivial changes (1)
  • apps/grpc/Dockerfile

# Dependencies are only re-downloaded when go.mod or go.sum change.
COPY go.mod go.sum ./
COPY apps/testapp/go.mod apps/testapp/go.sum ./apps/testapp/
RUN go mod download && (cd apps/testapp && go mod download)
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.

⚠️ Potential issue | 🟡 Minor

Fix hadolint DL3003: use WORKDIR instead of cd in subshell.

The CI lint check is failing because (cd apps/testapp && go mod download) violates DL3003. Refactor to use WORKDIR directives to satisfy the linter.

Suggested fix
 COPY go.mod go.sum ./
 COPY apps/testapp/go.mod apps/testapp/go.sum ./apps/testapp/
-RUN go mod download && (cd apps/testapp && go mod download)
+RUN go mod download
+
+WORKDIR /ev-node/apps/testapp
+RUN go mod download
+
+WORKDIR /ev-node
 
 # Copy the rest of the source and build.
 COPY . .
 
-WORKDIR /ev-node/apps/testapp
+WORKDIR /ev-node/apps/testapp
 RUN go build -o /go/bin/testapp .

Alternatively, if you want to minimize layer count, you could use a single multi-line RUN with explicit WORKDIR reset, but splitting into separate RUN commands is cleaner and still benefits from caching since the layer depends only on the module files.

🧰 Tools
🪛 GitHub Check: lint / hadolint

[failure] 24-24:
DL3003 warning: Use WORKDIR to switch to a directory

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/testapp/Dockerfile` at line 24, The RUN line uses a subshell with cd
(RUN go mod download && (cd apps/testapp && go mod download)) which triggers
hadolint DL3003; replace the subshell by switching WORKDIR to apps/testapp
before running its go mod download and then reset WORKDIR (or use a second RUN)
so you run go mod download in the project directory without cd; update the
Dockerfile to add a WORKDIR apps/testapp + RUN go mod download (and then WORKDIR
back to original if needed) or split into two RUNs, referencing the existing RUN
command and the apps/testapp path to locate where to change.

@tac0turtle tac0turtle merged commit cc9f965 into main Apr 1, 2026
34 checks passed
@tac0turtle tac0turtle deleted the ci/optimize-caching-3196 branch April 1, 2026 06:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] CI: optimize CI job

2 participants