diff --git a/.github/workflows/build-webview.yaml b/.github/workflows/build-webview.yaml index 3f4a9531..4261a4a4 100644 --- a/.github/workflows/build-webview.yaml +++ b/.github/workflows/build-webview.yaml @@ -22,6 +22,11 @@ jobs: build-docker-image: name: Build Docker Images (Pi 1-4) runs-on: ubuntu-24.04 + # `packages: write` so `docker login ghcr.io` with GITHUB_TOKEN can push + # the buildcache tag below. Mirrors docker-build.yaml's `buildx` job. + permissions: + contents: read + packages: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -40,27 +45,35 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set buildx arguments - if: github.event_name != 'pull_request' - id: prepare - run: | - GIT_SHORT_HASH=$(git rev-parse --short HEAD) - - { - echo "buildx_args=--cache-from \"screenly/ose-qt-builder:latest\" \ - --output \"type=image,push=true\" \ - --build-arg \"BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')\" \ - --build-arg \"GIT_HASH=$GIT_SHORT_HASH\" \ - --build-arg \"GIT_SHORT_HASH=$GIT_SHORT_HASH\" \ - --build-arg \"GIT_BRANCH=$GITHUB_REF_NAME\"" - } >> "$GITHUB_OUTPUT" + - name: Login to GitHub Container Registry + if: success() && github.event_name != 'pull_request' + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Building container if: github.event_name != 'pull_request' + env: + # Mirrors docker-build.yaml's registry-cache pattern. Cache lives + # at ghcr.io/screenly/anthias-webview-qt5-builder:buildcache and + # is reused across runs of this job. mode=max writes every layer + # (not just the final manifest) so apt + Qt cross-build state + # survives. image-manifest=true is what GHCR requires; legacy + # standalone-cache manifests are rejected on ghcr.io/screenly/*. + CACHE_REF: ghcr.io/screenly/anthias-webview-qt5-builder:buildcache run: | cd webview + GIT_SHORT_HASH=$(git rev-parse --short HEAD) docker buildx build \ - ${{ steps.prepare.outputs.buildx_args }} \ + --cache-from "type=registry,ref=${CACHE_REF}" \ + --cache-to "type=registry,ref=${CACHE_REF},mode=max,image-manifest=true" \ + --output "type=image,push=true" \ + --build-arg "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \ + --build-arg "GIT_HASH=${GIT_SHORT_HASH}" \ + --build-arg "GIT_SHORT_HASH=${GIT_SHORT_HASH}" \ + --build-arg "GIT_BRANCH=${GITHUB_REF_NAME}" \ -t screenly/ose-qt-builder:latest . compile-webview-part-1: @@ -145,6 +158,14 @@ jobs: strategy: matrix: board: ['pi5', 'pi4-64', 'x86'] + # `packages: write` so the build step below can push the buildcache + # tag to GHCR on push events. PRs from forks won't have this scope + # in practice (GITHUB_TOKEN is read-only on fork PRs), which is why + # `cache-to` is gated to non-PR events; cache-from still works + # because the published cache package is public. + permissions: + contents: read + packages: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -155,6 +176,14 @@ jobs: id: buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 + - name: Login to GitHub Container Registry + if: success() && github.event_name != 'pull_request' + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set environment variables run: | # WebView-v2026.04.0 → 2026.04.0 on tag pushes; date-stamped @@ -172,9 +201,31 @@ jobs: } >> "$GITHUB_ENV" - name: Build Docker Image + env: + # Mirrors docker-build.yaml's registry-cache pattern, scoped per + # board because each Qt 6 board uses a distinct Dockerfile + # (Dockerfile.{pi4,pi5,x86}) and shares almost nothing across + # boards. cache-to is push-only: PRs from forks have a read-only + # GITHUB_TOKEN and would auth-fail on the registry write. + CACHE_REF: ghcr.io/screenly/anthias-webview-qt6-builder:buildcache-${{ matrix.board }} + BAKE_TARGET: builder-${{ matrix.board }} run: | cd webview - docker compose build + # `docker buildx bake` reads the compose file, then --set lets us + # inject cache-from / cache-to per target without modifying the + # YAML (which has to stay PR-fork-safe). --load makes the + # resulting image available to the local Docker daemon so the + # subsequent `docker compose run` step finds it via the + # explicit `image:` tag in docker-compose.yml. + set_args=( --set "${BAKE_TARGET}.cache-from=type=registry,ref=${CACHE_REF}" ) + if [[ "${{ github.event_name }}" != "pull_request" ]]; then + set_args+=( --set "${BAKE_TARGET}.cache-to=type=registry,ref=${CACHE_REF},mode=max,image-manifest=true" ) + fi + docker buildx bake \ + -f docker-compose.yml \ + --load \ + "${set_args[@]}" \ + "${BAKE_TARGET}" - name: Prepare bind-mount target for non-root builder user run: | diff --git a/webview/docker-compose.yml b/webview/docker-compose.yml index e3a8a13a..436de99d 100644 --- a/webview/docker-compose.yml +++ b/webview/docker-compose.yml @@ -2,6 +2,7 @@ services: builder-x86: profiles: - x86 + image: webview-builder-x86 build: context: . dockerfile: docker/Dockerfile.x86 @@ -17,6 +18,7 @@ services: builder-pi5: profiles: - pi5 + image: webview-builder-pi5 build: context: . dockerfile: docker/Dockerfile.pi5 @@ -33,6 +35,7 @@ services: builder-pi4-64: profiles: - pi4-64 + image: webview-builder-pi4-64 build: context: . dockerfile: docker/Dockerfile.pi4