Skip to content

Improve build caching - #879

Open
Zelberor wants to merge 4 commits into
mainfrom
dependencies-build-cache-fix
Open

Improve build caching#879
Zelberor wants to merge 4 commits into
mainfrom
dependencies-build-cache-fix

Conversation

@Zelberor

@Zelberor Zelberor commented Dec 8, 2025

Copy link
Copy Markdown
Collaborator

Description

Improves build caching both in the Dockerfile and in the github action.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Does this PR introduce a breaking change?

No

Most important changes

  • build.yml
  • agent-ros2 Dockerfile

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works (might be obsolete with CI later on)
  • New and existing unit tests pass locally with my changes (might be obsolete with CI later on)

Summary by CodeRabbit

Release Notes

  • Chores
    • Optimized Docker build to separate dependency definition handling, improving cache reuse and reducing rebuilds when source changes.
    • Enhanced CI build caching to reuse a registry-backed build cache across test and image-publish steps for faster test and build runs.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 8, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds Buildx registry-backed caching to GitHub Actions steps and a new Dockerfile builder stage that isolates dependency-definition files into a separate layer to improve rebuild caching and avoid rebuilding dependencies when source changes.

Changes

Cohort / File(s) Summary
Buildx Cache Configuration
.github/workflows/build.yml
Adds cache-from and cache-to (registry-backed buildcache, mode=max) to Test (pull_request) and Build & push (non-pull_request) steps to reuse Buildx cache across runs
Dockerfile Dependency Isolation
build/docker/agent-ros2/Dockerfile
Adds agent-deps builder stage that copies dependency files (requirements*.txt, package.xml, setup.*, CMakeLists.txt) into /agent-deps; main build copies from that stage, removes src after installing Python requirements, and removes prior bind-mount usage to enable dependency-layer caching

Sequence Diagram(s)

sequenceDiagram
  participant GH as GitHub Actions
  participant Buildx as Docker Buildx
  participant Registry as Container Registry
  participant Docker as Dockerfile build stages

  GH->>Buildx: Trigger build (Test or Build)
  Buildx->>Registry: pull cache-from: buildcache
  Buildx->>Docker: execute Dockerfile
  Docker->>Docker: use agent-deps stage (copy dependency files)
  Docker->>Docker: main build copies from agent-deps, installs deps
  Buildx->>Registry: push cache-to: buildcache (mode=max)
  Buildx->>Registry: push image (non-pull_request)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 In layers I hop, where dependencies hide,
I stash every file on the caching tide.
Builds skip my burrow when nothing's changed,
Faster the carrots (CI) are rearranged. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Improve build caching' accurately summarizes the main objective of the pull request and clearly reflects the primary changes made to optimize Docker and GitHub Actions build caching.
Description check ✅ Passed The description addresses the template's key sections including a summary of changes, type of change, breaking changes confirmation, and most important files, though the summary could be more detailed about specific improvements.
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.


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.

@Zelberor
Zelberor marked this pull request as ready for review December 8, 2025 16:33
@Zelberor
Zelberor requested review from JulianTrommer and ll7 December 8, 2025 16:33

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 0

🧹 Nitpick comments (2)
build/docker/agent-ros2/Dockerfile (1)

346-355: Clarify rsync filter logic for maintainability.

The rsync command uses both --include="*/" (to preserve directory structure) and --exclude="*" (to filter out unwanted files), which is correct but the intent could be clearer with a brief comment explaining that only the listed dependency file types are copied while maintaining the source directory hierarchy.

RUN --mount=type=bind,target=/workspace/code,source=code \
  mkdir /arlab-deps && \
  # Copy only dependency definition files to preserve directory structure for caching
  rsync -r --prune-empty-dirs --include="*/" \
  --include="requirements.txt" \
  --include="requirements.*.txt" \
  --include="package.xml" \
  --include="setup.cfg" \
  --include="setup.py" \
  --include="CMakeLists.txt" \
  --exclude="*" "/workspace/code" "/agent-deps/"
.github/workflows/build.yml (1)

54-55: LGTM, but note registry cache accumulation strategy.

The cache configuration correctly uses registry-backed caching with mode=max. The Test build will load locally (load: true, line 47) and also push cache layers to the registry for reuse in subsequent builds. This integrates well with the Dockerfile's new agent-deps caching strategy.

One consideration: There is no explicit cleanup or retention policy for the buildcache tag. Over time, old cache layers will accumulate in the registry. Monitor registry storage and consider adding a cache cleanup job if needed.

Consider documenting or implementing a periodic cache cleanup strategy (e.g., monthly pruning of old cache entries) to manage registry storage growth.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2ea5ac and c032f38.

📒 Files selected for processing (2)
  • .github/workflows/build.yml (2 hunks)
  • build/docker/agent-ros2/Dockerfile (3 hunks)
🔇 Additional comments (4)
build/docker/agent-ros2/Dockerfile (3)

415-416: Verify the src removal doesn't corrupt the development workflow.

Line 416 removes the src directory after pip install, and Line 422 immediately recreates it as a symlink to the volume mount (/workspace/code). This pattern allows live code changes during development. Confirm this doesn't cause issues with:

  • Partially installed packages in build caches
  • Container startup after the symlink is created
  • Dependency resolution across rebuilds

The removal and recreation pattern is intentional for the dev container (allowing live edits), but verify it doesn't interfere with cached dependency layers. Consider if any stale state from line 411-413's pip install could persist in earlier cache layers.


393-394: No action needed - the COPY source path is correct.

The rsync command in the agent-deps stage (line 346-355) copies /workspace/code to /agent-deps/, resulting in /agent-deps/code/.... The COPY command at line 393 correctly references /agent-deps/code as the source, properly matching the rsync output structure.

Likely an incorrect or invalid review comment.


332-356: Bind mount and rsync configuration verified.

The code directory exists at the repository root and contains all expected dependency files (requirements.txt, package.xml, setup.py/cfg, CMakeLists.txt across 13 packages). The rsync command without a trailing slash on the source will correctly generate the /agent-deps/code/<path-to-files> structure as intended for caching dependency definitions.

.github/workflows/build.yml (1)

73-74: LGTM, but be aware of concurrent build race conditions.

The Build+Push step uses the same buildcache tag as the Test step, allowing both PR and main branch builds to share cached layers. However, if multiple builds run simultaneously (e.g., two PRs or a PR + main push), they could race when writing to the buildcache tag, potentially causing inconsistent cache state.

This is generally acceptable in most CI environments, but worth monitoring if concurrent builds are frequent.

Verify that your GitHub Actions runner (self-hosted, build) is configured to serialize builds or handle concurrent cache writes gracefully. You may want to check if Docker registry locking mechanisms protect against concurrent tag overwrites.

Comment thread build/docker/agent-ros2/Dockerfile Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @build/docker/agent-ros2/Dockerfile:
- Around line 342-356: The rsync invocation that builds /agent-deps currently
includes patterns "--include='requirements.txt'" and
"--include='requirements.*.txt'", which misses files with underscores like
requirements_infrastructure.txt; update the rsync include filters in that RUN
block (the rsync command that copies from "/workspace/code" to "/agent-deps/")
to either add "--include='requirements_infrastructure.txt'" or more generically
replace the two patterns with "--include='requirements*.txt'" so that both
dotted and underscored variants are copied.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c032f38 and 70c5dfd.

📒 Files selected for processing (1)
  • build/docker/agent-ros2/Dockerfile
🔇 Additional comments (3)
build/docker/agent-ros2/Dockerfile (3)

332-341: LGTM! Clear documentation of the caching strategy.

The comment effectively explains the rationale for this optimization stage. Using a minimal ubuntu:24.04 base is appropriate since this stage only performs file operations without requiring the full agent runtime environment.


393-393: LGTM! Dependency-based caching correctly implemented.

This change replaces the previous approach with copying from the agent-deps stage, which will only invalidate the cache when dependency definition files change (not when source code changes). This is the core optimization for the build cache improvement.


415-416: LGTM! Proper cleanup of temporary dependency files.

Removing the src directory after dependency installation is correct because:

  • The dependency files were only needed for rosdep and pip installation (lines 397-413)
  • The actual source code will be mounted at runtime via the /workspace volume (line 420)
  • The symlink at line 422 will point to the runtime-mounted code
  • This prevents stale or partial files from persisting in the image layer

Comment on lines +342 to +356
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y rsync

RUN --mount=type=bind,target=/workspace/code,source=code \
mkdir /agent-deps && \
rsync -r --prune-empty-dirs --include="*/" \
--include="requirements.txt" \
--include="requirements.*.txt" \
--include="package.xml" \
--include="setup.cfg" \
--include="setup.py" \
--include="CMakeLists.txt" \
--exclude="*" "/workspace/code" "/agent-deps/"

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the repository structure
git ls-files | head -30

Repository: una-auxme/paf

Length of output: 768


🏁 Script executed:

# Check the Dockerfile context - find all files in or related to the agent-ros2 build
fd -t f 'Dockerfile|requirements|setup|pyproject|MANIFEST|CMakeLists' --max-results=50

Repository: una-auxme/paf

Length of output: 1808


🏁 Script executed:

# Get more context on the Dockerfile around line 393 mentioned in the comment
sed -n '390,400p' build/docker/agent-ros2/Dockerfile

Repository: una-auxme/paf

Length of output: 510


🏁 Script executed:

# Search for all dependency files that exist in the repo
echo "=== Python dependency files ===" && \
fd -t f -n 'pyproject\.toml|MANIFEST\.in' && \
echo -e "\n=== Requirements files ===" && \
fd -t f -n 'requirements.*\.txt|requirements\.txt' && \
echo -e "\n=== Setup files ===" && \
fd -t f -n 'setup\.py|setup\.cfg' && \
echo -e "\n=== Package manifests ===" && \
fd -t f -n 'package\.xml' && \
echo -e "\n=== CMake files ===" && \
fd -t f -n 'CMakeLists\.txt'

Repository: una-auxme/paf

Length of output: 336


🏁 Script executed:

# Fix the command - search for dependency files properly
echo "=== Python dependency files ===" && \
fd 'pyproject\.toml' && \
fd 'MANIFEST\.in' && \
echo -e "\n=== Requirements files ===" && \
fd 'requirements.*\.txt' && \
echo -e "\n=== Setup files ===" && \
fd 'setup\.py|setup\.cfg' && \
echo -e "\n=== Package manifests ===" && \
fd 'package\.xml' && \
echo -e "\n=== CMake files ===" && \
fd 'CMakeLists\.txt'

Repository: una-auxme/paf

Length of output: 2121


🏁 Script executed:

# Specifically check in code/ directory for dependency files
echo "=== All dependency-related files in code/ directory ===" && \
fd -t f '' code/ | grep -E '(requirements|setup|pyproject|MANIFEST|CMakeLists|package\.xml)' | head -50

Repository: una-auxme/paf

Length of output: 1180


🏁 Script executed:

# Check what files the rsync is supposed to copy - look at the code/ directory structure
echo "=== Directory structure of code/ ===" && \
fd -t f -d 2 '' code/ | head -40

Repository: una-auxme/paf

Length of output: 1232


🏁 Script executed:

# Verify which requirements files match the rsync pattern "requirements.*.txt"
echo "=== Testing rsync glob pattern 'requirements.*.txt' ===" && \
echo "Files that should match 'requirements.*.txt' pattern:" && \
cd code && ls -1 requirements*.txt && cd - && \
echo -e "\n=== Testing with bash glob matching ===" && \
python3 << 'EOF'
import fnmatch
import os

pattern = "requirements.*.txt"
test_files = [
    "requirements.txt",
    "requirements.cpu.txt",
    "requirements.cuda.txt", 
    "requirements.rocm.txt",
    "requirements_infrastructure.txt"
]

print(f"Pattern: {pattern}\n")
for f in test_files:
    matches = fnmatch.fnmatch(f, pattern)
    print(f"  {f:<35} -> {'✓ MATCHES' if matches else '✗ DOES NOT MATCH'}")
EOF

Repository: una-auxme/paf

Length of output: 143


🏁 Script executed:

# Check if requirements_infrastructure.txt is actually needed/used
echo "=== Checking usage of requirements_infrastructure.txt ===" && \
rg -l 'requirements_infrastructure' --type txt --type py --type sh

Repository: una-auxme/paf

Length of output: 177


🏁 Script executed:

# Look at the full Dockerfile to understand the context better
sed -n '342,400p' build/docker/agent-ros2/Dockerfile

Repository: una-auxme/paf

Length of output: 2693


🏁 Script executed:

# Check if requirements_infrastructure.txt is required for the build
cat build/docker/agent-ros2/scripts/install-python-requirements.sh

Repository: una-auxme/paf

Length of output: 2368


🏁 Script executed:

# Verify the exact rsync filter matching behavior
python3 << 'EOF'
import fnmatch

# Test files from the codebase
files = [
    "requirements.txt",
    "requirements.cpu.txt",
    "requirements.cuda.txt",
    "requirements.rocm.txt",
    "requirements_infrastructure.txt"
]

# Rsync patterns from the Dockerfile
patterns = [
    "requirements.txt",
    "requirements.*.txt"
]

print("Rsync filter matching analysis:\n")
for f in files:
    matches = any(fnmatch.fnmatch(f, p) for p in patterns)
    status = "✓ COPIED" if matches else "✗ NOT COPIED"
    print(f"  {f:<35} -> {status}")
    if not matches:
        print(f"    (not matched by any pattern: {patterns})")
EOF

Repository: una-auxme/paf

Length of output: 420


Add requirements_infrastructure.txt to rsync filters.

The rsync glob pattern requirements.*.txt only matches files with dots (e.g., requirements.cpu.txt), not underscores. However, requirements_infrastructure.txt is actively used in the build process and will not be copied to /agent-deps/code/, causing infrastructure dependencies to be missing during the build.

Update the rsync filters to explicitly include the infrastructure requirements file:

Fix rsync filters
 RUN --mount=type=bind,target=/workspace/code,source=code \
   mkdir /agent-deps && \
   rsync -r --prune-empty-dirs --include="*/" \
   --include="requirements.txt" \
   --include="requirements.*.txt" \
+  --include="requirements_infrastructure.txt" \
   --include="package.xml" \
   --include="setup.cfg" \
   --include="setup.py" \
   --include="CMakeLists.txt" \
   --exclude="*" "/workspace/code" "/agent-deps/"

Alternatively, use requirements*.txt instead of both requirements.txt and requirements.*.txt to capture all variants (dot and underscore).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y rsync
RUN --mount=type=bind,target=/workspace/code,source=code \
mkdir /agent-deps && \
rsync -r --prune-empty-dirs --include="*/" \
--include="requirements.txt" \
--include="requirements.*.txt" \
--include="package.xml" \
--include="setup.cfg" \
--include="setup.py" \
--include="CMakeLists.txt" \
--exclude="*" "/workspace/code" "/agent-deps/"
RUN --mount=type=bind,target=/workspace/code,source=code \
mkdir /agent-deps && \
rsync -r --prune-empty-dirs --include="*/" \
--include="requirements.txt" \
--include="requirements.*.txt" \
--include="requirements_infrastructure.txt" \
--include="package.xml" \
--include="setup.cfg" \
--include="setup.py" \
--include="CMakeLists.txt" \
--exclude="*" "/workspace/code" "/agent-deps/"
🤖 Prompt for AI Agents
In @build/docker/agent-ros2/Dockerfile around lines 342 - 356, The rsync
invocation that builds /agent-deps currently includes patterns
"--include='requirements.txt'" and "--include='requirements.*.txt'", which
misses files with underscores like requirements_infrastructure.txt; update the
rsync include filters in that RUN block (the rsync command that copies from
"/workspace/code" to "/agent-deps/") to either add
"--include='requirements_infrastructure.txt'" or more generically replace the
two patterns with "--include='requirements*.txt'" so that both dotted and
underscored variants are copied.

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.

2 participants