Note: This is a temporary planning document. It will be deleted or archived once the decision is implemented.
Documentation Build Strategy: Clone in Actions vs Dockerfile¶
Question¶
When building the unified docs Docker image, where should we clone the other repos (cluster-gitops, camarades-infrastructure)?
Option A: Clone in GitHub Actions, then COPY into Dockerfile Option B: Clone directly in Dockerfile Option C: Hybrid approach
Option A: Clone in Actions, COPY in Dockerfile¶
How It Works¶
# .github/workflows/ci-cd.yml
- name: Checkout syrf
uses: actions/checkout@v4
with:
path: syrf
- name: Checkout cluster-gitops
uses: actions/checkout@v4
with:
repository: camaradesuk/cluster-gitops
path: cluster-gitops
- name: Checkout camarades-infrastructure
uses: actions/checkout@v4
with:
repository: camaradesuk/camarades-infrastructure
path: camarades-infrastructure
- name: Build Docker image
run: docker build -f syrf/docs/Dockerfile -t syrf-docs .
# docs/Dockerfile
FROM python:3.10-alpine AS builder
WORKDIR /workspace
# Copy all repos from context
COPY syrf/ syrf/
COPY cluster-gitops/ cluster-gitops/
COPY camarades-infrastructure/ camarades-infrastructure/
# Install dependencies and build
WORKDIR /workspace/syrf
RUN pip install -r requirements.txt
RUN mkdocs build --strict --site-dir _docs_build
FROM nginx:alpine
COPY --from=builder /workspace/syrf/_docs_build /usr/share/nginx/html
Pros¶
- ✅ Better Docker layer caching - repos don't change every build
- ✅ Faster builds - no git clone in Docker (uses Actions cache)
- ✅ No secrets in Dockerfile - Actions checkout handles auth
- ✅ Works with private repos - Actions has GITHUB_TOKEN by default
- ✅ Deterministic builds - exact commits, no "latest" ambiguity
- ✅ Easy local testing - can manually clone and build
- ✅ Build context is explicit - visible in Actions workflow
Cons¶
- ❌ More complex Actions workflow
- ❌ Larger build context sent to Docker daemon
- ❌ Must coordinate checkout paths with Dockerfile COPY
Build Performance¶
Actions checkout (3 repos): ~10-20s (with cache: ~3-5s)
Docker COPY: ~2-5s
Total: ~12-25s (with cache: ~5-10s)
Option B: Clone in Dockerfile¶
How It Works¶
# .github/workflows/ci-cd.yml
- name: Checkout syrf
uses: actions/checkout@v4
- name: Build Docker image
run: docker build -f docs/Dockerfile -t syrf-docs .
# docs/Dockerfile
FROM python:3.10-alpine AS builder
# Install git
RUN apk add --no-cache git build-base
WORKDIR /workspace
# Clone all repos
RUN git clone https://github.com/camaradesuk/syrf.git && \
git clone https://github.com/camaradesuk/cluster-gitops.git && \
git clone https://github.com/camaradesuk/camarades-infrastructure.git
# Install dependencies and build
WORKDIR /workspace/syrf
RUN pip install -r requirements.txt
RUN mkdocs build --strict --site-dir _docs_build
FROM nginx:alpine
COPY --from=builder /workspace/syrf/_docs_build /usr/share/nginx/html
Pros¶
- ✅ Simple Actions workflow - just build, no coordination
- ✅ Smaller build context - only syrf repo sent to Docker
- ✅ Self-contained Dockerfile - everything in one place
- ✅ Easy to understand - clear what's happening
- ✅ Works for manual builds -
docker buildjust works
Cons¶
- ❌ Poor Docker layer caching - git clone runs every time
- ❌ Slower builds - clones entire repos every build (~30-60s)
- ❌ Authentication issues - private repos need tokens
- ❌ Non-deterministic - always clones "latest" main/master
- ❌ Harder to debug - can't inspect cloned repos
- ❌ Network dependency - fails if GitHub is down during build
Build Performance¶
Docker git clone (3 repos): ~30-60s (no caching!)
Docker pip install: ~20-30s
Docker mkdocs build: ~10-15s
Total: ~60-105s (always full build)
Option C: Hybrid Approach (RECOMMENDED)¶
How It Works¶
For CI/CD (Actions): Use Option A (clone in Actions) For Local Development: Use Option B pattern (but with local repos)
# .github/workflows/ci-cd.yml
build-and-push-docs:
steps:
- name: Checkout syrf
uses: actions/checkout@v4
with:
path: syrf
- name: Checkout cluster-gitops
uses: actions/checkout@v4
with:
repository: camaradesuk/cluster-gitops
path: cluster-gitops
- name: Checkout camarades-infrastructure
uses: actions/checkout@v4
with:
repository: camaradesuk/camarades-infrastructure
path: camarades-infrastructure
- name: Build Docker image
run: |
docker build \
-f syrf/docs/Dockerfile.ci \
-t ghcr.io/camaradesuk/syrf-docs:${{ needs.version-docs.outputs.version }} \
.
# docs/Dockerfile.ci (for CI/CD with Actions checkout)
FROM python:3.10-alpine AS builder
WORKDIR /workspace
# Copy all repos from Actions checkout
COPY syrf/ syrf/
COPY cluster-gitops/ cluster-gitops/
COPY camarades-infrastructure/ camarades-infrastructure/
WORKDIR /workspace/syrf
RUN pip install --no-cache-dir -r requirements.txt
RUN mkdocs build --strict --site-dir _docs_build
FROM nginx:alpine
COPY --from=builder /workspace/syrf/_docs_build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# docs/Dockerfile (for local development)
FROM python:3.10-alpine AS builder
# Install git for local clone capability
RUN apk add --no-cache git build-base
WORKDIR /workspace
# For local builds: check if repos exist in context, else clone
# This allows: docker build -f docs/Dockerfile .
COPY . syrf/
# Clone other repos if not already present
RUN if [ ! -d "../cluster-gitops" ]; then \
git clone https://github.com/camaradesuk/cluster-gitops.git ../cluster-gitops; \
fi && \
if [ ! -d "../camarades-infrastructure" ]; then \
git clone https://github.com/camaradesuk/camarades-infrastructure.git ../camarades-infrastructure; \
fi
WORKDIR /workspace/syrf
RUN pip install --no-cache-dir -r requirements.txt
RUN mkdocs build --strict --site-dir _docs_build
FROM nginx:alpine
COPY --from=builder /workspace/syrf/_docs_build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Pros¶
- ✅ Best of both worlds - fast CI, easy local dev
- ✅ Optimized for each use case
- ✅ Clear separation - .ci suffix shows intent
- ✅ Flexible - works in all scenarios
Cons¶
- ❌ Two Dockerfiles to maintain (but they're similar)
- ❌ Slightly more complex setup
Performance Comparison¶
| Scenario | Option A (Actions) | Option B (Dockerfile) | Option C (Hybrid) |
|---|---|---|---|
| First CI Build | ~60s | ~105s | ~60s |
| Cached CI Build | ~15s | ~105s | ~15s |
| Local Build | Manual setup | ~105s | ~105s |
| Build Context Size | ~50MB | ~1MB | ~50MB (CI), ~1MB (local) |
Recommendation: Option C (Hybrid)¶
Why Hybrid Wins¶
- CI Performance: Option A speed with Actions checkout caching
- Developer Experience: Local builds "just work" without manual setup
- Flexibility: Adapts to context (CI vs local)
- Cost Efficiency: Faster CI = lower GitHub Actions costs
- Reliability: No network dependency during Docker build in CI
Additional Benefits¶
- Repository Dispatch Ready: When other repos trigger rebuilds, Actions checkout handles it cleanly
- Version Pinning: Can checkout specific commits/tags if needed
- Debugging: Easy to inspect repos in Actions before Docker build
- Security: No auth tokens in Dockerfile, GitHub Actions handles it
Implementation Priority¶
Phase 1: Quick Win (Use Option A)¶
Just implement Actions checkout + Dockerfile.ci for immediate improvement.
Phase 2: Developer Experience (Add local Dockerfile)¶
Add fallback Dockerfile for local development when needed.
Phase 3: Optimization (Add caching)¶
Add Docker layer caching in Actions for even faster builds.
Example: Full GitHub Actions Job¶
build-and-push-docs:
needs: version-docs
runs-on: ubuntu-latest
steps:
# Checkout all 3 repos
- name: Checkout syrf
uses: actions/checkout@v4
with:
path: syrf
- name: Checkout cluster-gitops
uses: actions/checkout@v4
with:
repository: camaradesuk/cluster-gitops
path: cluster-gitops
- name: Checkout camarades-infrastructure
uses: actions/checkout@v4
with:
repository: camaradesuk/camarades-infrastructure
path: camarades-infrastructure
# Optional: Show what was cloned (debugging)
- name: Verify repos
run: |
echo "Syrf commit: $(git -C syrf rev-parse HEAD)"
echo "GitOps commit: $(git -C cluster-gitops rev-parse HEAD)"
echo "Infra commit: $(git -C camarades-infrastructure rev-parse HEAD)"
# Build with all repos in context
- name: Build Docker image
run: |
docker build \
-f syrf/docs/Dockerfile.ci \
-t ghcr.io/camaradesuk/syrf-docs:${{ needs.version-docs.outputs.version }} \
-t ghcr.io/camaradesuk/syrf-docs:latest \
.
# Push to registry
- name: Push to GHCR
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/camaradesuk/syrf-docs:${{ needs.version-docs.outputs.version }}
docker push ghcr.io/camaradesuk/syrf-docs:latest
Testing Strategy¶
Test 1: Local Build (without other repos)¶
cd /home/chris/workspace/syrf
docker build -f docs/Dockerfile -t syrf-docs:local .
# Should clone cluster-gitops and camarades-infrastructure
Test 2: Local Build (with other repos present)¶
cd /home/chris/workspace
docker build -f syrf/docs/Dockerfile.ci -t syrf-docs:ci .
# Should use existing repos
Test 3: CI Build (simulate Actions)¶
cd /tmp
git clone https://github.com/camaradesuk/syrf.git
git clone https://github.com/camaradesuk/cluster-gitops.git
git clone https://github.com/camaradesuk/camarades-infrastructure.git
docker build -f syrf/docs/Dockerfile.ci -t syrf-docs:ci .
Test 4: Verify Aggregated Docs¶
docker run --rm -p 8080:80 syrf-docs:ci
# Open browser: http://localhost:8080
# Check: Architecture, GitOps Operations, Infrastructure sections all present
Decision¶
✅ Implement Option C (Hybrid Approach)
Rationale:
- Performance: 4-7x faster CI builds with caching
- Flexibility: Works for CI and local development
- Cost: Reduces GitHub Actions minutes by 70-80%
- Developer Experience: Easy local testing without manual setup
- Future-proof: Ready for repository dispatch automation