perf: optimize Docker build with better layer caching

Dockerfile optimizations:
- Use BuildKit cache mounts for pip and apt
- Reorder COPY instructions: least-changing first, most-changing last
- Remove duplicate frontend build stage (use pre-built from workflow)
- Separate requirements.txt copy for maximum pip cache hits

Workflow optimizations:
- Use actions/setup-node built-in npm caching
- Add trigger for master branch (not just main)
- Use multiple GHA cache scopes for better granularity
- Improved release summary output

Build order optimized for caching:
1. Python deps (cached by requirements.txt hash)
2. System packages (cached by apt cache mount)
3. Entrypoint + libs (change infrequently)
4. App code (changes most frequently)
5. Frontend build (pre-built artifact from workflow)
This commit is contained in:
Laszlo Toth 2025-12-14 23:12:33 +01:00
parent 91fa5eb6b8
commit b5f2895157
2 changed files with 64 additions and 56 deletions

View file

@ -4,6 +4,7 @@ on:
push:
branches:
- main
- master
paths-ignore:
- '*.md'
- 'docs/**'
@ -30,6 +31,9 @@ env:
UI_DIRECTORY: ./frontend
jobs:
# ==========================================================================
# Build Scraper Image (separate job with its own cache)
# ==========================================================================
build-scraper:
runs-on: ubuntu-latest
permissions:
@ -80,23 +84,21 @@ jobs:
cache-from: type=gha,scope=scraper
cache-to: type=gha,mode=max,scope=scraper
# ==========================================================================
# Build Frontend (cached via GitHub Actions cache)
# ==========================================================================
build-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Cache node_modules
uses: actions/cache@v4
with:
path: "${{ env.UI_DIRECTORY }}/node_modules"
key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }}
restore-keys: ${{ runner.os }}-modules-
- name: Setup NodeJS
uses: actions/setup-node@v4
with:
node-version-file: "${{ env.UI_DIRECTORY }}/.nvmrc"
cache: 'npm'
cache-dependency-path: "${{ env.UI_DIRECTORY }}/package-lock.json"
- name: Install Dependencies
run: npm ci
@ -113,6 +115,9 @@ jobs:
path: ${{ env.UI_DIRECTORY }}/build
retention-days: 1
# ==========================================================================
# Build and Push Bazarr Image
# ==========================================================================
build-and-push:
needs: [build-frontend, build-scraper]
runs-on: ubuntu-latest
@ -183,7 +188,7 @@ jobs:
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# Latest tag on main branch
# Latest tag on main/master branch
type=raw,value=latest,enable={{is_default_branch}}
# Version tag (e.g., v1.5.3-lavx.20241214)
type=raw,value=${{ steps.version.outputs.fork_version }}
@ -207,8 +212,11 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Use GHA cache with separate scopes for different layers
cache-from: |
type=gha,scope=bazarr-deps
type=gha,scope=bazarr-main
cache-to: type=gha,mode=max,scope=bazarr-main
build-args: |
BAZARR_VERSION=${{ steps.version.outputs.fork_version }}
BUILD_DATE=${{ github.event.repository.updated_at }}
@ -223,28 +231,27 @@ jobs:
- name: Create Release Summary
run: |
echo "## 🐳 Docker Image Published" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Pull Commands" >> $GITHUB_STEP_SUMMARY
echo "## 🐳 Docker Images Published" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Bazarr (LavX Fork)" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "# Latest" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "# Specific Version" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.fork_version }}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### OpenSubtitles Scraper" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.REGISTRY }}/${{ env.SCRAPER_IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Image Details" >> $GITHUB_STEP_SUMMARY
echo "- **Digest:** \`${{ steps.push.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY
echo "- **Platforms:** linux/amd64, linux/arm64" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🔌 OpenSubtitles Scraper" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.REGISTRY }}/${{ env.SCRAPER_IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "- **Cache Status:** GHA cache enabled" >> $GITHUB_STEP_SUMMARY
# Optional: Create GitHub Release for tagged versions
# ==========================================================================
# Create GitHub Release for tagged versions
# ==========================================================================
create-release:
needs: build-and-push
runs-on: ubuntu-latest
@ -260,15 +267,20 @@ jobs:
with:
generate_release_notes: true
body: |
## 🐳 Docker Image
## 🐳 Docker Images
```bash
# Bazarr (LavX Fork)
docker pull ghcr.io/${{ github.repository }}:${{ github.ref_name }}
# OpenSubtitles Scraper
docker pull ghcr.io/${{ github.repository_owner }}/opensubtitles-scraper:latest
```
## 📝 Changes
This release is based on upstream Bazarr with the following custom modifications:
- OpenSubtitles.org web scraper provider
- OpenSubtitles.org web scraper provider (no VIP API needed)
- Auto-sync with upstream daily at 4 AM UTC
See the auto-generated release notes below for detailed changes.

View file

@ -1,7 +1,7 @@
# =============================================================================
# Bazarr LavX Fork - Production Docker Image
# =============================================================================
# Multi-stage build for optimized image size
# Multi-stage build optimized for layer caching
# Based on Debian Slim for better compatibility (unrar, etc.)
# =============================================================================
@ -10,22 +10,7 @@ ARG BUILD_DATE
ARG VCS_REF
# =============================================================================
# Stage 1: Build Frontend
# =============================================================================
FROM node:20-slim AS frontend-builder
WORKDIR /app
# Install dependencies first for better caching
COPY frontend/package*.json ./frontend/
RUN cd frontend && npm ci
# Copy frontend source and build
COPY frontend ./frontend/
RUN cd frontend && npm run build
# =============================================================================
# Stage 2: Install Python Dependencies
# Stage 1: Install Python Dependencies (cached heavily)
# =============================================================================
FROM python:3.12-slim-bookworm AS python-builder
@ -40,12 +25,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /app
# Copy requirements and install Python packages
# Copy ONLY requirements first for maximum caching
# This layer will only rebuild when requirements.txt changes
COPY requirements.txt ./
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Use pip cache mount to avoid re-downloading packages across builds
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --prefix=/install -r requirements.txt
# =============================================================================
# Stage 3: Production Image
# Stage 2: Production Image
# =============================================================================
FROM python:3.12-slim-bookworm AS production
@ -64,7 +53,10 @@ LABEL org.opencontainers.image.title="Bazarr (LavX Fork)" \
org.opencontainers.image.licenses="GPL-3.0"
# Enable non-free repository for unrar and install runtime dependencies
RUN sed -i 's/Components: main/Components: main non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources && \
# Use apt cache mount to speed up repeated builds
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
sed -i 's/Components: main/Components: main non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources && \
apt-get update && apt-get install -y --no-install-recommends \
ffmpeg \
libxml2 \
@ -76,31 +68,35 @@ RUN sed -i 's/Components: main/Components: main non-free non-free-firmware/' /et
bash \
gosu \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /app/bazarr/bin /config /defaults \
&& groupadd -g 1000 bazarr \
&& useradd -u 1000 -g bazarr -d /config -s /bin/bash bazarr
# Copy Python packages from builder
# Copy Python packages from builder (changes rarely)
COPY --from=python-builder /install /usr/local
# Copy application code
# Copy entrypoint script (changes rarely)
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Set work directory
WORKDIR /app/bazarr
COPY bazarr.py ./
# Copy libs directories (change less frequently than main app code)
COPY libs ./libs
COPY custom_libs ./custom_libs
COPY bazarr ./bazarr
COPY migrations ./migrations
# Copy main application code (changes most frequently - keep at end)
COPY bazarr.py ./
COPY bazarr ./bazarr
# Copy fork identification file (shows "LavX Fork" in System Status)
COPY package_info /app/bazarr/package_info
# Copy frontend build
COPY --from=frontend-builder /app/frontend/build ./frontend/build
# Copy entrypoint script
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Copy pre-built frontend (built in GitHub Actions workflow for caching)
# This layer only rebuilds when frontend/build changes
COPY frontend/build ./frontend/build
# Set environment variables
ENV HOME="/config" \