Skip to content

Commit 435bae8

Browse files
committed
Fix release publish workflow footguns
1 parent 51cfd6c commit 435bae8

3 files changed

Lines changed: 127 additions & 22 deletions

File tree

.github/workflows/publish-executor-package.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Publish Executor
22
run-name: "${{ format('publish executor {0}', github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name) }}"
33

44
on:
5+
# Release pushes a validated v* tag and lets this trigger handle automatic
6+
# publishing. workflow_dispatch stays available for explicit repair runs.
57
push:
68
tags:
79
- "v*"

.github/workflows/publish-selfhost-docker.yml

Lines changed: 118 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ concurrency:
1717
cancel-in-progress: false
1818

1919
jobs:
20-
publish:
20+
metadata:
2121
runs-on: blacksmith-4vcpu-ubuntu-2404
2222
permissions:
2323
contents: read
24-
packages: write
25-
env:
26-
HAS_LEGACY_TOKEN: ${{ secrets.GHCR_LEGACY_TOKEN != '' }}
24+
outputs:
25+
release_tag: ${{ steps.validate.outputs.tag }}
26+
image: ${{ steps.image.outputs.image }}
27+
tags: ${{ steps.image.outputs.tags }}
28+
legacy_tags: ${{ steps.image.outputs.legacy_tags }}
29+
labels: ${{ steps.image.outputs.labels }}
2730

2831
steps:
2932
- name: Checkout
@@ -38,9 +41,10 @@ jobs:
3841
bun-version: 1.3.11
3942

4043
- name: Validate release tag
44+
id: validate
4145
env:
4246
RAW_RELEASE_TAG: ${{ inputs.tag }}
43-
run: bun run scripts/validate-release-ref.ts --tag-env RAW_RELEASE_TAG --write-env RELEASE_TAG
47+
run: bun run scripts/validate-release-ref.ts --tag-env RAW_RELEASE_TAG --write-env RELEASE_TAG --output tag
4448

4549
- name: Checkout release tag
4650
env:
@@ -91,8 +95,30 @@ jobs:
9195
echo "LABELS"
9296
} >> "$GITHUB_OUTPUT"
9397
94-
- name: Set up QEMU
95-
uses: docker/setup-qemu-action@v3
98+
build:
99+
needs: metadata
100+
strategy:
101+
fail-fast: false
102+
matrix:
103+
include:
104+
- arch: amd64
105+
platform: linux/amd64
106+
runner: blacksmith-4vcpu-ubuntu-2404
107+
- arch: arm64
108+
platform: linux/arm64
109+
runner: blacksmith-4vcpu-ubuntu-2404-arm
110+
runs-on: ${{ matrix.runner }}
111+
permissions:
112+
contents: read
113+
packages: write
114+
115+
steps:
116+
- name: Checkout release tag
117+
uses: actions/checkout@v4
118+
with:
119+
ref: ${{ needs.metadata.outputs.release_tag }}
120+
fetch-depth: 0
121+
persist-credentials: false
96122

97123
- name: Setup Blacksmith Builder
98124
uses: useblacksmith/setup-docker-builder@v1
@@ -104,16 +130,94 @@ jobs:
104130
username: ${{ github.actor }}
105131
password: ${{ secrets.GITHUB_TOKEN }}
106132

107-
- name: Build and push self-host image
133+
- name: Build and push self-host image by digest
108134
id: build
109135
uses: useblacksmith/build-push-action@v2
110136
with:
111137
context: .
112138
file: apps/host-selfhost/Dockerfile
113-
platforms: linux/amd64,linux/arm64
114-
push: true
115-
tags: ${{ steps.image.outputs.tags }}
116-
labels: ${{ steps.image.outputs.labels }}
139+
platforms: ${{ matrix.platform }}
140+
labels: ${{ needs.metadata.outputs.labels }}
141+
outputs: type=image,name=${{ needs.metadata.outputs.image }},push-by-digest=true,name-canonical=true,push=true
142+
143+
- name: Export image digest
144+
shell: bash
145+
env:
146+
DIGEST: ${{ steps.build.outputs.digest }}
147+
run: |
148+
set -euo pipefail
149+
150+
if [ -z "$DIGEST" ]; then
151+
echo "Build did not return a digest."
152+
exit 1
153+
fi
154+
155+
mkdir -p "$RUNNER_TEMP/digests"
156+
touch "$RUNNER_TEMP/digests/${DIGEST#sha256:}"
157+
158+
- name: Upload image digest
159+
uses: actions/upload-artifact@v4
160+
with:
161+
name: digests-${{ matrix.arch }}
162+
path: ${{ runner.temp }}/digests/*
163+
if-no-files-found: error
164+
retention-days: 1
165+
166+
merge:
167+
needs:
168+
- metadata
169+
- build
170+
runs-on: blacksmith-4vcpu-ubuntu-2404
171+
permissions:
172+
packages: write
173+
env:
174+
HAS_LEGACY_TOKEN: ${{ secrets.GHCR_LEGACY_TOKEN != '' }}
175+
176+
steps:
177+
- name: Download image digests
178+
uses: actions/download-artifact@v4
179+
with:
180+
pattern: digests-*
181+
path: ${{ runner.temp }}/digests
182+
merge-multiple: true
183+
184+
- name: Set up Docker Buildx
185+
uses: docker/setup-buildx-action@v3
186+
187+
- name: Log in to GHCR
188+
uses: docker/login-action@v3
189+
with:
190+
registry: ghcr.io
191+
username: ${{ github.actor }}
192+
password: ${{ secrets.GITHUB_TOKEN }}
193+
194+
- name: Publish multi-arch self-host image
195+
shell: bash
196+
env:
197+
IMAGE: ${{ needs.metadata.outputs.image }}
198+
TAGS: ${{ needs.metadata.outputs.tags }}
199+
run: |
200+
set -euo pipefail
201+
202+
sources=()
203+
while IFS= read -r digest_file; do
204+
digest="$(basename "$digest_file")"
205+
[[ -n "$digest" ]] || continue
206+
sources+=("${IMAGE}@sha256:${digest}")
207+
done < <(find "$RUNNER_TEMP/digests" -maxdepth 1 -type f | sort)
208+
209+
if [ "${#sources[@]}" -eq 0 ]; then
210+
echo "No image digests were downloaded."
211+
exit 1
212+
fi
213+
214+
tag_args=()
215+
while IFS= read -r tag; do
216+
[[ -n "$tag" ]] || continue
217+
tag_args+=("-t" "$tag")
218+
done <<< "$TAGS"
219+
220+
docker buildx imagetools create "${tag_args[@]}" "${sources[@]}"
117221
118222
- name: Skip legacy GHCR mirror (GHCR_LEGACY_TOKEN not configured)
119223
if: env.HAS_LEGACY_TOKEN != 'true'
@@ -131,8 +235,8 @@ jobs:
131235
if: env.HAS_LEGACY_TOKEN == 'true'
132236
shell: bash
133237
env:
134-
SOURCE_IMAGE: ${{ steps.image.outputs.image }}@${{ steps.build.outputs.digest }}
135-
LEGACY_TAGS: ${{ steps.image.outputs.legacy_tags }}
238+
SOURCE_IMAGE: ${{ needs.metadata.outputs.image }}:${{ needs.metadata.outputs.release_tag }}
239+
LEGACY_TAGS: ${{ needs.metadata.outputs.legacy_tags }}
136240
run: |
137241
set -euo pipefail
138242

.github/workflows/release.yml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ jobs:
5252
id: changesets
5353
uses: changesets/action@v1
5454
with:
55-
version: bun run changeset:version
55+
# @changesets/changelog-github occasionally receives truncated
56+
# GitHub GraphQL responses. Retry only that transient failure mode.
57+
version: >-
58+
bash -c 'for attempt in 1 2 3; do log="$(mktemp)"; if bun run changeset:version >"$log" 2>&1; then cat "$log"; rm -f "$log"; exit 0; fi; status=$?; cat "$log"; if ! grep -Eq "Failed to parse data from GitHub|Premature close" "$log" || [ "$attempt" -eq 3 ]; then rm -f "$log"; exit "$status"; fi; echo "::warning::changeset:version hit a transient GitHub GraphQL failure on attempt $attempt; resetting generated files before retry."; rm -f "$log"; git reset --hard HEAD; done'
5659
commit: Version Packages
5760
title: Version Packages
5861
createGithubReleases: false
@@ -124,13 +127,9 @@ jobs:
124127
git tag "$RELEASE_TAG"
125128
git push "$auth_remote" "$RELEASE_TAG"
126129
127-
- name: Trigger CLI publish
128-
if: steps.changesets.outputs.hasChangesets == 'false' && steps.detect_release.outputs.changed == 'true'
129-
env:
130-
GH_TOKEN: ${{ github.token }}
131-
RELEASE_TAG: ${{ steps.validate_release.outputs.tag }}
132-
run: |
133-
gh workflow run publish-executor-package.yml --ref "$RELEASE_TAG" -f tag="$RELEASE_TAG"
130+
# Publishing is intentionally tag-driven: pushing the validated v* tag
131+
# triggers publish-executor-package.yml. A second workflow_dispatch path
132+
# races a duplicate publish for the same release.
134133

135134
# Desktop build downloads CLI binaries from the release, so it must
136135
# run after CLI publish completes. Trigger it from the CLI workflow

0 commit comments

Comments
 (0)