Skip to content

5402: Switch image build pipeline to GHCR - #410

Merged
turegjorup merged 7 commits into
feature/update-infrastructure-for-mono-repofrom
feature/5402-build-pipeline-ghcr
Apr 29, 2026
Merged

5402: Switch image build pipeline to GHCR#410
turegjorup merged 7 commits into
feature/update-infrastructure-for-mono-repofrom
feature/5402-build-pipeline-ghcr

Conversation

@turegjorup

@turegjorup turegjorup commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Consolidates the two DockerHub-targeted workflows (docker_build_images_from_develop.yml and docker_build_images_from_tag.yml) into a single build-images.yml that pushes to ghcr.io/os2display/*. Authenticates with the built-in GITHUB_TOKEN — no DockerHub secrets to manage.
  • APP_VERSION build-arg is sourced from docker/metadata-action's computed version, so it always matches the image tag (no refs/tags/3.0.0 leakage).
  • Adds GHA layer cache (mode=max, scoped per image) so multi-arch builds reuse client_app_builder and api_app_builder stages instead of re-running npm ci + composer install per platform per run.
  • Aligns infrastructure/build-n-push.sh with the workflow (single-pass buildx --push, GHCR), and adds a BUILD_LOAD=1 flag for local builds that load into the host docker daemon.
  • Folds the post-COPY chmod in infrastructure/nginx/Dockerfile into the COPY itself, eliminating a duplicate-content layer. Nginx image: 135 MB → 108 MB (efficiency 78.7% → 99.5% per dive).
  • Expands .dockerignore so editing dev-tooling configs (compose files, Taskfile, phpstan/psalm/rector, Playwright config, IDE state, broader .docker/) no longer invalidates the image build cache.
  • Bumps GitHub Actions across all workflows to current major versions (actions/checkout@v6, actions/upload-artifact@v7, go-task/setup-task@v2, all docker/* actions to their current majors).

Resulting trigger semantics

Trigger Image tags produced
Push to develop :develop
Tag 3.0.0-RC1 (RC, on a release branch) :3.0.0-RC1 (no :latest update)
Tag 3.0.0 (final release) :3.0.0, :latest

flavor: latest=auto on docker/metadata-action is what makes :latest track non-prerelease semver tags only. RC tags follow normal semver pre-release conventions and are excluded automatically.

Bugs fixed in passing

The two deleted workflows had several issues that the new file does not inherit:

  • Tag-push API build was missing platforms:, so production tag images were silently amd64-only.
  • Tag workflow passed ${{ github.ref }} (refs/tags/3.0.0) into APP_VERSION. Now uses steps.meta-*.outputs.version (3.0.0).
  • Tag workflow used the deprecated echo ::set-output syntax.
  • Tag workflow pinned docker/build-push-action@v5 while develop pinned @v6. Now uniform on @v7.
  • Neither old workflow supplied build-contexts: repository-root=., which the Dockerfiles' COPY --from=repository-root steps require.

Local validation

  • BUILD_LOAD=1 infrastructure/build-n-push.sh produces working API + Nginx images (single-arch native).
  • dive --ci on both images: 99.32% (API) and 99.46% (Nginx) efficiency.
  • Local --cache-from/--cache-to type=local,mode=max round-trip: cold 41 s → warm 17 s (-59%); after a real PHP source change, 21.5 s with npm ci, npm run build, and the initial composer install --no-scripts all cache-hit.

Test plan

  • Merge into the feature branch; confirm the workflow runs on the next push to develop and produces ghcr.io/os2display/display-api-service:develop and …-nginx:develop (multi-arch manifest).
  • On a tag push of 3.0.0-RC1 (cut from a release branch), confirm :3.0.0-RC1 is produced and :latest is not updated.
  • On a tag push of 3.0.0, confirm :3.0.0 is produced and :latest is updated.
  • Second run of any of the above completes substantially faster (cache hits visible in build logs).
  • Verify actions/upload-artifact@v7 in playwright.yaml still uploads the report on a Playwright run (no matrix, single artifact name → no immutability conflict expected).

Commits

  1. 5402: Bump GitHub Actions to current major versions
  2. 5402: Switch image build pipeline to GHCR
  3. 5402: Fold chmod into COPY to drop duplicate image layers
  4. 5402: Expand .dockerignore for build-cache stability
  5. 5402: Make RC builds tag-only and adopt rolling latest tag

🤖 Generated with Claude Code

turegjorup and others added 4 commits April 28, 2026 13:23
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consolidate the develop and tag DockerHub workflows into a single
multi-arch build that pushes to ghcr.io/os2display/*, authenticated via
GITHUB_TOKEN. Provide repository-root as a named build context so the
Dockerfiles' COPY --from=repository-root steps resolve. Add per-image
GHA cache (mode=max) so multi-arch builds reuse builder stages across
runs instead of re-running npm ci + composer install per platform.

Align infrastructure/build-n-push.sh with the workflow: drop the
turegjorup namespace, single-pass docker buildx --push for multi-arch
manifests, and add a BUILD_LOAD=1 flag for local builds that load into
the host docker daemon instead of pushing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The nginx Dockerfile had:
    COPY --from=api_app_builder ... ./public
    RUN chmod -R g+w /var/www/html

The RUN re-wrote every file's permissions, producing a second 19 MB
layer with byte-identical content. Replacing with --chmod on the COPY
itself drops the duplicate layer; image goes from 135 MB to 108 MB,
dive efficiency from 78.7% to 99.5%.

Apply the same pattern to docker-entrypoint.sh in the API Dockerfile
for consistency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The API Dockerfile does a wholesale 'COPY --from=repository-root ./'
that brings in everything not excluded. Without these additions,
editing a docker-compose.yml, Taskfile.yml, or any quality-tool config
(phpstan, psalm, rector, playwright) invalidated the cache from that
COPY onward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@turegjorup turegjorup self-assigned this Apr 28, 2026
@turegjorup
turegjorup requested a review from tuj April 28, 2026 11:30
turegjorup and others added 3 commits April 29, 2026 10:29
RC images are now produced by tagging on a release branch (e.g. tag
'3.0.0-RC1' on release/3.0.0), not by every push to release/*. This
keeps RC builds explicit and reviewable instead of running on every
push to a release branch.

Add 'flavor: latest=auto' so non-prerelease semver tags ('3.0.0') also
update ':latest', while pre-release semver tags ('3.0.0-RC1') do not.
This is the metadata-action default but is set explicitly with a
comment so the behavior is obvious.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r-mono-repo' into feature/5402-build-pipeline-ghcr

# Conflicts:
#	CHANGELOG.md
…r-mono-repo' into feature/5402-build-pipeline-ghcr

# Conflicts:
#	CHANGELOG.md
@turegjorup
turegjorup merged commit 795d70c into feature/update-infrastructure-for-mono-repo Apr 29, 2026
18 checks passed
@turegjorup
turegjorup deleted the feature/5402-build-pipeline-ghcr branch April 29, 2026 09:09
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.

1 participant