feat(workflow): scope docker publishes to changed services
Add a preflight change-detection job to the Docker publish workflow and derive the build matrix from shared-root and service-specific path filters. Preserve full publishes on manual dispatch while skipping unaffected service image builds on routine pushes.
This commit is contained in:
@@ -14,18 +14,90 @@ env:
|
||||
REGISTRY: ghcr.io
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
shared: ${{ steps.manual.outputs.shared || steps.filter.outputs.shared }}
|
||||
frontend: ${{ steps.manual.outputs.frontend || steps.filter.outputs.frontend }}
|
||||
backend: ${{ steps.manual.outputs.backend || steps.filter.outputs.backend }}
|
||||
remote_gateway: ${{ steps.manual.outputs.remote_gateway || steps.filter.outputs.remote_gateway }}
|
||||
matrix: ${{ steps.matrix.outputs.matrix }}
|
||||
has_changes: ${{ steps.matrix.outputs.has_changes }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Mark all services changed for manual dispatch
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
id: manual
|
||||
run: |
|
||||
echo "shared=true" >> "$GITHUB_OUTPUT"
|
||||
echo "frontend=true" >> "$GITHUB_OUTPUT"
|
||||
echo "backend=true" >> "$GITHUB_OUTPUT"
|
||||
echo "remote_gateway=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Detect changed paths
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
id: filter
|
||||
uses: dorny/paths-filter@v3
|
||||
with:
|
||||
filters: |
|
||||
shared:
|
||||
- '.github/workflows/docker-publish.yml'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- 'docker-compose.yml'
|
||||
- 'patches/**'
|
||||
frontend:
|
||||
- 'packages/frontend/**'
|
||||
backend:
|
||||
- 'packages/backend/**'
|
||||
remote_gateway:
|
||||
- 'packages/remote-gateway/**'
|
||||
|
||||
- name: Build service matrix
|
||||
id: matrix
|
||||
env:
|
||||
SHARED: ${{ steps.manual.outputs.shared || steps.filter.outputs.shared }}
|
||||
FRONTEND: ${{ steps.manual.outputs.frontend || steps.filter.outputs.frontend }}
|
||||
BACKEND: ${{ steps.manual.outputs.backend || steps.filter.outputs.backend }}
|
||||
REMOTE_GATEWAY: ${{ steps.manual.outputs.remote_gateway || steps.filter.outputs.remote_gateway }}
|
||||
run: |
|
||||
python <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
shared = os.environ.get("SHARED") == "true"
|
||||
services = [
|
||||
("frontend", "packages/frontend/Dockerfile", os.environ.get("FRONTEND") == "true"),
|
||||
("backend", "packages/backend/Dockerfile", os.environ.get("BACKEND") == "true"),
|
||||
("remote-gateway", "packages/remote-gateway/Dockerfile", os.environ.get("REMOTE_GATEWAY") == "true"),
|
||||
]
|
||||
|
||||
include = [
|
||||
{"service": service, "dockerfile": dockerfile}
|
||||
for service, dockerfile, changed in services
|
||||
if shared or changed
|
||||
]
|
||||
|
||||
output_path = Path(os.environ["GITHUB_OUTPUT"])
|
||||
with output_path.open("a", encoding="utf-8") as output:
|
||||
output.write(f"matrix={json.dumps({'include': include})}\n")
|
||||
output.write(f"has_changes={'true' if include else 'false'}\n")
|
||||
PY
|
||||
|
||||
publish:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.has_changes == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- service: frontend
|
||||
dockerfile: packages/frontend/Dockerfile
|
||||
- service: backend
|
||||
dockerfile: packages/backend/Dockerfile
|
||||
- service: remote-gateway
|
||||
dockerfile: packages/remote-gateway/Dockerfile
|
||||
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
Reference in New Issue
Block a user