@@ -16,15 +16,18 @@ jobs:
1616 e2e :
1717 runs-on : ubuntu-latest
1818 timeout-minutes : 30
19+ outputs :
20+ tag : ${{ steps.release.outputs.tag }}
21+ digest : ${{ steps.release.outputs.digest }}
1922 steps :
2023 - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2124
2225 - name : Set up Go
2326 uses : actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
2427 with :
25- go-version : stable
28+ go-version-file : go.mod # reproducibility: same Go as local builds
2629
27- - name : Install k3d, kubectl, crane
30+ - name : Install k3d, kubectl, crane, yq
2831 run : |
2932 set -euxo pipefail
3033 # k3d is the only provisioner that works in GHA; kind, minikube
3740 # crane converts our OCI layout to a registry push without a
3841 # docker daemon round-trip.
3942 go install github.com/google/go-containerregistry/cmd/crane@v0.20.6
43+ # yq parses the pinned tag + digest out of the release kustomization.
44+ go install github.com/mikefarah/yq/v4@v4.46.1
4045 echo "$HOME/go/bin" >>"$GITHUB_PATH"
4146 "$HOME/go/bin/crane" version
47+ "$HOME/go/bin/yq" --version
48+
49+ - name : Read pinned release
50+ id : release
51+ run : |
52+ set -euxo pipefail
53+ K=deploy/kustomize/release/kustomization.yaml
54+ T=$(yq '.images[] | select(.name == "ghcr.io/yolean/buckety-controller") | .newTag' "$K")
55+ D=$(yq '.images[] | select(.name == "ghcr.io/yolean/buckety-controller") | .digest' "$K")
56+ [[ -n "$T" && "$T" != null ]] || { echo "$K: no newTag" >&2; exit 1; }
57+ [[ "$D" == sha256:* ]] || { echo "$K: bad digest $D" >&2; exit 1; }
58+ echo "tag=$T" >> "$GITHUB_OUTPUT"
59+ echo "digest=$D" >> "$GITHUB_OUTPUT"
4260
4361 - name : Start k3d cluster with local registry
4462 run : |
@@ -66,26 +84,44 @@ jobs:
6684 - name : Set up contain
6785 uses : solsson/setup-contain@49cc4cce1498df8a59e88fff52c1a9b747f11f08 # v1
6886
69- - name : Build controller image (contain)
87+ - name : Build controller image (contain) with pinned tag
88+ env :
89+ T : ${{ steps.release.outputs.tag }}
7090 run : |
7191 set -euxo pipefail
7292 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
7393 -trimpath \
74- -ldflags "-s -w -X main.version=$(git describe --always --dirty)" \
94+ -buildvcs=false \
95+ -ldflags "-s -w -X main.version=${T}" \
7596 -o target/linux/amd64/buckety \
7697 ./cmd/buckety
77- IMAGE=ghcr.io/yolean/buckety-controller:${{ github.sha }} \
98+ IMAGE=" ghcr.io/yolean/buckety-controller:${T}" \
7899 contain build --output ./oci --push=false
79100
101+ - name : Assert produced digest matches pinned
102+ env :
103+ PINNED : ${{ steps.release.outputs.digest }}
104+ run : |
105+ set -euxo pipefail
106+ BUILT=$(jq -r '.manifests[0].digest' oci/index.json)
107+ if [[ "$BUILT" != "$PINNED" ]]; then
108+ echo "Pinned digest in deploy/kustomize/release/kustomization.yaml" >&2
109+ echo " ${PINNED}" >&2
110+ echo "does not match the digest produced by this build:" >&2
111+ echo " ${BUILT}" >&2
112+ echo "Re-run scripts/bump-release.sh and commit the result." >&2
113+ exit 1
114+ fi
115+
80116 - name : Run e2e harness
81117 env :
82118 IMPLEMENTATIONS : redpanda,versitygw,minio
83119 OCI_DIR : ./oci
84120 # PUSH_AS reaches the k3d local registry via its host-published
85121 # port; CONTROLLER_IMAGE uses the same registry's in-cluster
86122 # hostname so node containerd can pull it.
87- PUSH_AS : localhost:5000/yolean/buckety-controller:${{ github.sha }}
88- CONTROLLER_IMAGE : k3d-buckety-registry:5000/yolean/buckety-controller:${{ github.sha }}
123+ PUSH_AS : localhost:5000/yolean/buckety-controller:${{ steps.release.outputs.tag }}
124+ CONTROLLER_IMAGE : k3d-buckety-registry:5000/yolean/buckety-controller:${{ steps.release.outputs.tag }}
89125 run : ./test/e2e/run.sh
90126
91127 - name : Surface controller logs on failure
@@ -98,11 +134,10 @@ jobs:
98134 kubectl -n blobs describe pod -l app=minio || true
99135
100136 # The image push job runs only on main-branch builds in the canonical
101- # Yolean org with a successful e2e; it pins by digest into
102- # deploy/kustomize/release/ so consumers vendor a self-consistent base.
103- # The repository_owner gate means YoleanAgents (and forks) can iterate
104- # on the e2e job without publishing images or committing release pins.
105- # Stubbed for phase 1.
137+ # Yolean org with a successful e2e. It rebuilds (proves reproducibility),
138+ # asserts the same digest the e2e job verified, then crane pushes to
139+ # ghcr.io. No commit-back: the digest is pinned in the commit being
140+ # built. repository_owner gates YoleanAgents and forks from publishing.
106141 publish :
107142 if : >-
108143 github.ref == 'refs/heads/main'
@@ -111,13 +146,61 @@ jobs:
111146 needs : e2e
112147 runs-on : ubuntu-latest
113148 permissions :
114- contents : write
149+ contents : read
115150 packages : write
116151 steps :
117152 - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
118- - name : contain push (stub)
153+
154+ - name : Set up Go
155+ uses : actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
156+ with :
157+ go-version-file : go.mod # reproducibility: same Go as local builds
158+
159+ - name : Install crane
160+ run : |
161+ set -euxo pipefail
162+ go install github.com/google/go-containerregistry/cmd/crane@v0.20.6
163+ echo "$HOME/go/bin" >>"$GITHUB_PATH"
164+
165+ - name : Set up contain
166+ uses : solsson/setup-contain@49cc4cce1498df8a59e88fff52c1a9b747f11f08 # v1
167+
168+ - name : Rebuild controller image with pinned tag
169+ env :
170+ T : ${{ needs.e2e.outputs.tag }}
119171 run : |
120- echo "TODO contain build --push --image ghcr.io/yolean/buckety-controller@..."
121- - name : template release base (stub)
172+ set -euxo pipefail
173+ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
174+ -trimpath \
175+ -buildvcs=false \
176+ -ldflags "-s -w -X main.version=${T}" \
177+ -o target/linux/amd64/buckety \
178+ ./cmd/buckety
179+ IMAGE="ghcr.io/yolean/buckety-controller:${T}" \
180+ contain build --output ./oci --push=false
181+
182+ - name : Assert publish-time digest matches the e2e-tested digest
183+ env :
184+ PINNED : ${{ needs.e2e.outputs.digest }}
185+ run : |
186+ set -euxo pipefail
187+ BUILT=$(jq -r '.manifests[0].digest' oci/index.json)
188+ if [[ "$BUILT" != "$PINNED" ]]; then
189+ echo "publish rebuild produced ${BUILT}, e2e verified ${PINNED}" >&2
190+ exit 1
191+ fi
192+
193+ - name : Login to ghcr.io
194+ env :
195+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
122196 run : |
123- echo "TODO template deploy/kustomize/release/ with the pushed digest, commit back"
197+ set -euo pipefail
198+ echo "$GITHUB_TOKEN" | crane auth login ghcr.io \
199+ -u "${{ github.actor }}" --password-stdin
200+
201+ - name : Push image to ghcr.io
202+ env :
203+ T : ${{ needs.e2e.outputs.tag }}
204+ run : |
205+ set -euxo pipefail
206+ crane push ./oci "ghcr.io/yolean/buckety-controller:${T}"
0 commit comments