Skip to content

ci(docker): push web container images on release - #615

Open
tseaver wants to merge 1 commit into
feat-612-cache-bust-flutter-assetsfrom
ci-613-publish-web-image-on-release
Open

tseaver wants to merge 1 commit into
feat-612-cache-bust-flutter-assetsfrom
ci-613-publish-web-image-on-release

Conversation

@tseaver

@tseaver tseaver commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Publish the cache-busted web build as a container image,
ghcr.io/soliplex/frontend-web, on each release. Downstream images, such as
soliplex-template's nginx image (soliplex/soliplex-template#205), can then
COPY --from= a versioned build instead of building Flutter themselves.

Stacked on #614 (cache busting, #612), which this builds on: the image
carries that PR's cache-busted tree. Review and merge #614 first. This PR's
own change is the top commit only.

Changes

  • Dockerfile:
    • New FROM scratch AS web stage holding only /build/web. The nginx
      stage stays last, so docker build . still produces the runnable image.
    • The builder runs --platform=$BUILDPLATFORM. The output is static
      files, so a multi-platform build runs Flutter once, with no emulation.
    • The build flags are a FLUTTER_BUILD_ARGS arg, which now adds
      --no-web-resources-cdn, so CanvasKit is bundled rather than loaded from
      Google's CDN.
  • New .github/workflows/image.yaml, run on release: published, or manually
    with a release tag:
    • Maps v0.109.0+95 to image tags 0.109.0, 0.109.0-95, 0.109, and
      latest (+ isn't valid in an OCI tag). The full tag is kept in the
      org.opencontainers.image.version label.
    • Builds --target web for linux/amd64 and linux/arm64, with
      RELEASE_HASH set to the release tag.
    • A prerelease, or a manual run without floating: true, doesn't move
      0.109 or latest, so republishing an old release can't pull them
      backwards.
    • Slack notification on failure, as in flutter.yaml.
  • AGENTS.md: the CI section mentions the new workflow.

Test Plan

  • actionlint and markdownlint (via their container images): no
    findings. pre-commit YAML, merge-conflict, and gitleaks hooks pass.
  • Tag parsing tested locally. It accepts v0.109.0+95 and v0.109.0, and
    rejects v0.109.0-rc1, 0.109.0+95, and shell metacharacters.
  • Multi-platform build with a docker-container buildx builder:
    Flutter ran once (amd64), and the amd64 and arm64 manifests share one
    identical layer holding the cache-busted tree at /build/web.
  • Served that layer with nginx and loaded it in headless Chromium: all
    requests 200 from /v0.109.0+95/.
  • The workflow's GHCR login and push. These run only in Actions; a
    workflow_dispatch with an existing tag is the cheapest first check.
  • After the first publish, an org admin makes the frontend-web package
    public (GITHUB_TOKEN can't change visibility).

Known limitation (not fixed here): --no-web-resources-cdn bundles
CanvasKit only. Text fonts still come from fonts.gstatic.com (the engine's
fontFallbackBaseUrl); the build bundles only MaterialIcons. With external DNS
blocked, the client renders icons but no text. #613 overstated the flag on
this point. Fully offline text needs an app change: bundle a font, or self-host
the fallbacks and set fontFallbackBaseUrl.

Related Issues

Closes #613

🤖 Generated with Claude Code

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