- Archives `p2-release-windows-image` to `openspec/changes/archive/2026-05-20-p2-release-windows-image/`. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.9 KiB
Requirement: Tag push publishes a flutter-windows image to all release registries
When a tag matching * is pushed to the repository, the release_windows job in .github/workflows/release.yml SHALL build windows.Dockerfile with --target flutter and --build-arg flutter_version=<tag>, and SHALL push the resulting image to Docker Hub, GitHub Container Registry, and Quay.io under the repository name flutter-windows with the tag equal to the Flutter version.
The experience context is the CI engineer who, on the day a new Flutter stable lands, expects to run docker pull docker.io/<org>/flutter-windows:<version> and find the image at the same tag they already use for flutter-android.
Scenario: Tag push fans out to all three registries
- GIVEN a tag
X.Y.Zis pushed to the repository - WHEN the
release_windowsjob completes successfully - THEN
docker.io/<org>/flutter-windows:X.Y.Zexists - AND
ghcr.io/<org>/flutter-windows:X.Y.Zexists - AND
quay.io/<org>/flutter-windows:X.Y.Zexists
Scenario: Tag-image consistency
- WHEN any of the three published
flutter-windows:X.Y.Zimages is pulled andflutter --versionis invoked inside it - THEN the reported Flutter version is exactly
X.Y.Z
Requirement: Windows release runs in parallel with Android release
The release_windows job SHALL NOT declare a needs: dependency on release_android, and release_android SHALL NOT declare a needs: dependency on release_windows. A failure in one SHALL NOT cancel the other.
The experience context is the maintainer cutting a release: they accept that one architecture may publish while the other fails, and prefer fixing the failed one in a follow-up tag rather than blocking both.
Scenario: Android publishes when Windows build fails
- GIVEN a tag is pushed
- AND the
release_windowsjob fails (e.g., transientwindows-2025runner issue) - AND the
release_androidjob succeeds - WHEN the workflow run completes
- THEN Android images are published at all three registries
- AND the workflow run is reported as failed (because at least one job failed)
- AND the failure surface is the
release_windowsjob specifically, notrelease_android
Requirement: Windows release uses the same metadata conventions as Android release
The release_windows job SHALL use docker/metadata-action with the images input set to the same three registry namespaces and the tags input set to type=raw,value=${{ env.FLUTTER_VERSION }}, mirroring the Android job. The image labels (org.opencontainers.image.*) produced by metadata-action SHALL be applied to the built image (e.g., as --label arguments to docker build), so that docker inspect reports the same OCI label set as the Android image. docker/build-push-action is not a viable mechanism here because it does not support Windows containers (tracked at https://github.com/docker/build-push-action/issues/18).
The experience context is the operator inspecting docker inspect <org>/flutter-windows:X.Y.Z and docker inspect <org>/flutter-android:X.Y.Z and finding the same set of OCI labels (description, source, revision, version) populated with the same values.
Scenario: Labels match Android conventions
- GIVEN a successful
release_windowsrun for tagX.Y.Z - WHEN an operator runs
docker inspect docker.io/<org>/flutter-windows:X.Y.Zand inspects theLabelsmap - THEN the keys
org.opencontainers.image.source,org.opencontainers.image.revision,org.opencontainers.image.version, andorg.opencontainers.image.titleare all present - AND
org.opencontainers.image.versionequalsX.Y.Z - AND
org.opencontainers.image.revisionequals the commit SHA of the tag
Requirement: Manual workflow_dispatch rebuild is Windows-only
The release.yml workflow SHALL continue to declare workflow_dispatch:. On workflow_dispatch, only the release_windows job SHALL execute; release_android and its downstream jobs (update_description, record_image, set_bootstrap_image, create_github_release) SHALL be skipped via an if: github.event_name == 'push' guard on release_android (the four downstream jobs auto-skip via their existing needs: release_android). The FLUTTER_VERSION env var SHALL be set from github.ref_name, so that a maintainer can rebuild a single tag's Windows image without re-cutting the Git tag and without re-publishing the Android image, re-pushing the Docker Hub readme, or re-attempting gh release create (which would fail because the release already exists).
The experience context is the maintainer recovering from a transient Windows runner failure: they re-run the workflow on the existing tag instead of force-pushing a new one. Android recovery, by contrast, is the established fix-forward + re-tag pattern (see release.yml run history) and does not need a workflow_dispatch path.
Scenario: Manual rebuild produces a fresh Windows image
- GIVEN a tag
X.Y.Zexists in the repository - AND the prior
release_windowsrun for that tag failed - WHEN a maintainer triggers
release.ymlviaworkflow_dispatchselecting refX.Y.Z - THEN
release_windowsbuilds and pushesflutter-windows:X.Y.Zto all three registries - AND the existing Windows image digests at those tags are overwritten by the new digests
Scenario: Manual rebuild leaves the Android digest untouched
- GIVEN a tag
X.Y.Zexists and was previously published with Android digestD_a - WHEN a maintainer triggers
release.ymlviaworkflow_dispatchselecting refX.Y.Z - THEN
release_androidis reported asskipped - AND
update_description,record_image,set_bootstrap_image, andcreate_github_releaseare reported asskipped - AND the digest at
docker.io/<org>/flutter-android:X.Y.ZremainsD_a - AND the run is reported as success (no failed jobs)