mirror of
https://github.com/gmeligio/flutter-docker-image.git
synced 2026-05-24 12:30:34 +00:00
docs: archive p2-image-handoff-via-pr-tag (#452)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
passed
|
||||
+5
-5
@@ -28,11 +28,11 @@
|
||||
|
||||
## 4. Verify on a real PR before merge
|
||||
|
||||
- [ ] 4.1 Push as a non-fork draft PR. Confirm the tag `ghcr.io/<owner>/flutter-android:pr-<N>` appears under GHCR Packages, the job output `image_ref` is populated, and the existing `Test image` and `Scout` steps still pass on the locally-loaded image.
|
||||
- [ ] 4.2 Push from a fork (or simulate by gating the predicate to always-true for one run). Confirm the artifact `image-<run_id>` is uploaded (~2 GB), the output `image_artifact` is populated, and `image_ref` is empty.
|
||||
- [ ] 4.3 Re-run the same PR. Confirm the existing `pr-N` tag is overwritten in place (no duplicate `pr-N-1`, `pr-N-2`, etc.) — satisfies spec scenario "Re-running a PR overwrites the same handoff tag".
|
||||
- [x] 4.1 Push as a non-fork draft PR. Confirm the tag `ghcr.io/<owner>/flutter-android:pr-<N>` appears under GHCR Packages, the job output `image_ref` is populated, and the existing `Test image` and `Scout` steps still pass on the locally-loaded image.
|
||||
- [x] 4.2 Push from a fork (or simulate by gating the predicate to always-true for one run). Confirm the artifact `image-<run_id>` is uploaded (~2 GB), the output `image_artifact` is populated, and `image_ref` is empty.
|
||||
- [x] 4.3 Re-run the same PR. Confirm the existing `pr-N` tag is overwritten in place (no duplicate `pr-N-1`, `pr-N-2`, etc.) — satisfies spec scenario "Re-running a PR overwrites the same handoff tag".
|
||||
|
||||
## 5. Post-merge closure check
|
||||
|
||||
- [ ] 5.1 After 5 post-merge PRs, list GHCR tags matching `pr-*` and confirm they accumulate (cleanup is p4, not this change).
|
||||
- [ ] 5.2 Confirm fork-PR build wall-clock has regressed by ≤ 3 minutes vs. pre-change median — this is the expected cost until p3 redeems it.
|
||||
- [x] 5.1 After 5 post-merge PRs, list GHCR tags matching `pr-*` and confirm they accumulate (cleanup is p4, not this change).
|
||||
- [x] 5.2 Confirm fork-PR build wall-clock has regressed by ≤ 3 minutes vs. pre-change median — this is the expected cost until p3 redeems it.
|
||||
@@ -0,0 +1,77 @@
|
||||
# ci-image-handoff Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Hand off the Flutter Docker image built by the CI `test_image` job to downstream jobs in the same workflow run without rebuilding it, using a registry tag for non-fork events and an artifact tarball for fork PRs.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Build job exposes a handoff for downstream jobs
|
||||
|
||||
The CI job that builds the Flutter Docker image SHALL expose three job outputs that downstream jobs in the same workflow run can consume to access the image without rebuilding it:
|
||||
|
||||
- `image_ref`: the full registry reference (`ghcr.io/<owner>/flutter-android:<tag>`) when the build pushed to GHCR; empty string otherwise.
|
||||
- `image_artifact`: the artifact name (`image-<run_id>`) when the build uploaded a `docker save` tarball instead; empty string otherwise.
|
||||
- `image_local_tag`: the tag the image carries in the local docker daemon (and inside the artifact tarball) — `flutter-android:<flutter-version>`. Always set, regardless of handoff channel.
|
||||
|
||||
Exactly one of `image_ref` and `image_artifact` SHALL be non-empty per run; `image_local_tag` SHALL always be non-empty. A consumer SHALL be able to decide its pull strategy from the outputs alone, without inspecting `github.event` itself.
|
||||
|
||||
The experience context is a maintainer adding a new validation step in a later change — they look at the build job's outputs, see exactly one handoff channel populated, and write a single consumer that branches on which channel. The `image_local_tag` output lets fork-path consumers reference the image by its loaded tag without recomputing it from the version manifest.
|
||||
|
||||
#### Scenario: Outputs encode the handoff kind unambiguously
|
||||
|
||||
- **GIVEN** any successful build run
|
||||
- **WHEN** the run completes
|
||||
- **THEN** exactly one of `image_ref` and `image_artifact` is non-empty
|
||||
- **AND** the non-empty one matches the documented format (`ghcr.io/<owner>/flutter-android:pr-<N>` / `ghcr.io/<owner>/flutter-android:branch-<branch>` or `image-<run_id>`)
|
||||
- **AND** `image_local_tag` is non-empty and matches `flutter-android:<flutter-version>`
|
||||
|
||||
### Requirement: Non-fork PRs and workflow_dispatch use the registry handoff
|
||||
|
||||
For events that have `packages: write` available on `GITHUB_TOKEN` (`pull_request` from a same-repo head or `workflow_dispatch`), the build SHALL push the image to a deterministic GHCR tag and set `image_ref` to the full registry ref.
|
||||
|
||||
The tag format SHALL be:
|
||||
|
||||
- `pr-${{ github.event.pull_request.number }}` for `pull_request` events.
|
||||
- `branch-${{ github.ref_name }}` (with `/` replaced by `-`) for `workflow_dispatch`.
|
||||
|
||||
The experience context is the p4 cleanup workflow operator — they need a tag pattern they can match-and-delete on PR close without scanning the registry.
|
||||
|
||||
#### Scenario: Non-fork PR pushes the handoff tag
|
||||
|
||||
- **GIVEN** a `pull_request` event with `github.event.pull_request.head.repo.full_name == github.repository`
|
||||
- **WHEN** the build job runs and completes successfully
|
||||
- **THEN** `ghcr.io/<owner>/flutter-android:pr-<N>` exists in GHCR with the just-built image
|
||||
- **AND** the job output `image_ref` equals that ref
|
||||
- **AND** the job output `image_artifact` is empty
|
||||
- **AND** the job output `image_local_tag` equals `flutter-android:<flutter-version>`
|
||||
|
||||
#### Scenario: Re-running a PR overwrites the same handoff tag
|
||||
|
||||
- **GIVEN** a PR whose build has already produced `pr-<N>` once
|
||||
- **WHEN** the workflow is re-run for the same PR
|
||||
- **THEN** the tag `pr-<N>` is overwritten in place (no `pr-<N>-2` or similar accumulation)
|
||||
- **AND** the prior image bits are eligible for garbage collection by GHCR's regular GC
|
||||
|
||||
### Requirement: Fork PRs use an artifact handoff
|
||||
|
||||
For `pull_request` events from a fork (where `packages: write` is not available), the build SHALL skip the registry push, save the image with `docker save | gzip`, upload it via `actions/upload-artifact` with retention ≤ 1 day, and set `image_artifact` to the artifact name.
|
||||
|
||||
The experience context is a community contributor opening a fork PR — their PR still gets the parallel-validation benefit from later changes (p3), even though the runner cannot push to GHCR.
|
||||
|
||||
#### Scenario: Fork PR uploads the image artifact
|
||||
|
||||
- **GIVEN** a `pull_request` event with `github.event.pull_request.head.repo.full_name != github.repository`
|
||||
- **WHEN** the build job runs and completes successfully
|
||||
- **THEN** an artifact named `image-<run_id>` exists for the run, containing `image.tar.gz`
|
||||
- **AND** the artifact retention is ≤ 1 day
|
||||
- **AND** the job output `image_artifact` equals `image-<run_id>`
|
||||
- **AND** the job output `image_ref` is empty
|
||||
- **AND** the job output `image_local_tag` equals `flutter-android:<flutter-version>` (the tag carried inside the tarball)
|
||||
|
||||
#### Scenario: Fork PR fallback succeeds even when GHCR is unreachable
|
||||
|
||||
- **GIVEN** a fork PR build
|
||||
- **WHEN** the build completes
|
||||
- **THEN** no GHCR push is attempted
|
||||
- **AND** the build does not fail due to missing `packages: write` permission
|
||||
Reference in New Issue
Block a user