Skip to content

Commit 100b955

Browse files
committed
feat: add github release workflow
1 parent e82058b commit 100b955

5 files changed

Lines changed: 411 additions & 86 deletions

File tree

.github/workflows/release.yml

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: Existing tag to release, for example v0.1.0
11+
required: true
12+
type: string
13+
14+
permissions:
15+
contents: write
16+
packages: write
17+
18+
concurrency:
19+
group: release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
20+
cancel-in-progress: false
21+
22+
env:
23+
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
24+
RELEASE_REF: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
25+
26+
jobs:
27+
verify:
28+
name: Release verification
29+
runs-on: ubuntu-latest
30+
timeout-minutes: 40
31+
steps:
32+
- name: Check out repository
33+
uses: actions/checkout@v5
34+
with:
35+
ref: ${{ env.RELEASE_REF }}
36+
37+
- name: Set up Go
38+
uses: actions/setup-go@v6
39+
with:
40+
go-version-file: go.mod
41+
cache: true
42+
43+
- name: Install Helm 3
44+
shell: bash
45+
run: |
46+
set -euo pipefail
47+
HELM_VERSION="v3.17.3"
48+
curl -fsSLo /tmp/helm.tgz "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz"
49+
tar -C /tmp -xzf /tmp/helm.tgz
50+
sudo install -m 0755 /tmp/linux-amd64/helm /usr/local/bin/helm
51+
52+
- name: Check formatting
53+
shell: bash
54+
run: |
55+
set -euo pipefail
56+
if [[ -n "$(gofmt -l .)" ]]; then
57+
echo "gofmt reported unformatted files" >&2
58+
gofmt -l .
59+
exit 1
60+
fi
61+
62+
- name: Run Go test suite
63+
shell: bash
64+
run: go test ./... -p 1
65+
66+
- name: Verify docs and CI examples
67+
shell: bash
68+
run: bash scripts/verify-docs-and-ci.sh
69+
70+
- name: Verify Helm chart
71+
shell: bash
72+
run: bash scripts/verify-helm-chart.sh
73+
74+
- name: Verify Docker Compose smoke flow
75+
shell: bash
76+
run: bash scripts/verify-compose-e2e.sh
77+
78+
build-release-assets:
79+
name: Build release assets
80+
runs-on: ubuntu-latest
81+
needs: verify
82+
timeout-minutes: 20
83+
steps:
84+
- name: Check out repository
85+
uses: actions/checkout@v5
86+
with:
87+
ref: ${{ env.RELEASE_REF }}
88+
89+
- name: Set up Go
90+
uses: actions/setup-go@v6
91+
with:
92+
go-version-file: go.mod
93+
cache: true
94+
95+
- name: Build archives and checksums
96+
shell: bash
97+
run: |
98+
set -euo pipefail
99+
100+
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
101+
echo "RELEASE_TAG must look like vX.Y.Z or vX.Y.Z-suffix, got: ${RELEASE_TAG}" >&2
102+
exit 1
103+
fi
104+
105+
version="${RELEASE_TAG#v}"
106+
mkdir -p dist/bin dist/release
107+
108+
targets=(
109+
"linux amd64"
110+
"linux arm64"
111+
"darwin amd64"
112+
"darwin arm64"
113+
"windows amd64"
114+
)
115+
116+
for target in "${targets[@]}"; do
117+
read -r goos goarch <<<"$target"
118+
outdir="dist/bin/${goos}-${goarch}"
119+
mkdir -p "$outdir"
120+
121+
binary_name="testimony"
122+
archive_base="dist/release/testimony_${version}_${goos}_${goarch}"
123+
if [[ "$goos" == "windows" ]]; then
124+
binary_name="testimony.exe"
125+
fi
126+
127+
GOOS="$goos" GOARCH="$goarch" CGO_ENABLED=0 \
128+
go build -trimpath -ldflags='-s -w' -o "$outdir/$binary_name" ./cmd/testimony
129+
130+
if [[ "$goos" == "windows" ]]; then
131+
(cd "$outdir" && zip -q "$(pwd)/../../../${archive_base}.zip" "$binary_name")
132+
else
133+
tar -C "$outdir" -czf "${archive_base}.tar.gz" "$binary_name"
134+
fi
135+
done
136+
137+
(
138+
cd dist/release
139+
sha256sum * > SHA256SUMS
140+
)
141+
142+
- name: Upload release artifacts
143+
uses: actions/upload-artifact@v4
144+
with:
145+
name: release-assets-${{ env.RELEASE_TAG }}
146+
path: dist/release/*
147+
if-no-files-found: error
148+
149+
publish-images:
150+
name: Publish GHCR images
151+
runs-on: ubuntu-latest
152+
needs: verify
153+
timeout-minutes: 30
154+
steps:
155+
- name: Check out repository
156+
uses: actions/checkout@v5
157+
with:
158+
ref: ${{ env.RELEASE_REF }}
159+
160+
- name: Set up QEMU
161+
uses: docker/setup-qemu-action@v3
162+
163+
- name: Set up Docker Buildx
164+
uses: docker/setup-buildx-action@v3
165+
166+
- name: Log in to GHCR
167+
uses: docker/login-action@v3
168+
with:
169+
registry: ghcr.io
170+
username: ${{ github.actor }}
171+
password: ${{ github.token }}
172+
173+
- name: Compute image tags and labels
174+
id: meta
175+
shell: bash
176+
run: |
177+
set -euo pipefail
178+
179+
if [[ ! "${RELEASE_TAG}" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
180+
echo "Release image publication currently requires a stable semver tag like v1.2.3. Got: ${RELEASE_TAG}" >&2
181+
exit 1
182+
fi
183+
184+
major="${BASH_REMATCH[1]}"
185+
minor="${BASH_REMATCH[2]}"
186+
patch="${BASH_REMATCH[3]}"
187+
repo="ghcr.io/${GITHUB_REPOSITORY,,}"
188+
version="v${major}.${minor}.${patch}"
189+
190+
default_tags=$(cat <<EOF
191+
${repo}:${version}
192+
${repo}:v${major}.${minor}
193+
${repo}:v${major}
194+
${repo}:latest
195+
EOF
196+
)
197+
198+
allure3_tags=$(cat <<EOF
199+
${repo}:${version}-allure3
200+
${repo}:v${major}.${minor}-allure3
201+
${repo}:v${major}-allure3
202+
${repo}:latest-allure3
203+
EOF
204+
)
205+
206+
labels=$(cat <<EOF
207+
org.opencontainers.image.title=Testimony
208+
org.opencontainers.image.description=Single-binary service for publishing Allure reports without turning your CI runner into a report host.
209+
org.opencontainers.image.source=https://github.com/${GITHUB_REPOSITORY}
210+
org.opencontainers.image.url=https://github.com/${GITHUB_REPOSITORY}
211+
org.opencontainers.image.version=${version}
212+
org.opencontainers.image.revision=${GITHUB_SHA}
213+
EOF
214+
)
215+
216+
{
217+
echo 'default_tags<<EOF'
218+
echo "$default_tags"
219+
echo 'EOF'
220+
echo 'allure3_tags<<EOF'
221+
echo "$allure3_tags"
222+
echo 'EOF'
223+
echo 'labels<<EOF'
224+
echo "$labels"
225+
echo 'EOF'
226+
} >> "$GITHUB_OUTPUT"
227+
228+
- name: Build and push default image
229+
uses: docker/build-push-action@v7
230+
with:
231+
context: .
232+
file: ./Dockerfile
233+
push: true
234+
platforms: linux/amd64,linux/arm64
235+
provenance: false
236+
tags: ${{ steps.meta.outputs.default_tags }}
237+
labels: ${{ steps.meta.outputs.labels }}
238+
239+
- name: Build and push Allure 3 image
240+
uses: docker/build-push-action@v7
241+
with:
242+
context: .
243+
file: ./Dockerfile.allure3
244+
push: true
245+
platforms: linux/amd64,linux/arm64
246+
provenance: false
247+
tags: ${{ steps.meta.outputs.allure3_tags }}
248+
labels: ${{ steps.meta.outputs.labels }}
249+
250+
publish-release:
251+
name: Publish GitHub release
252+
runs-on: ubuntu-latest
253+
needs:
254+
- build-release-assets
255+
- publish-images
256+
timeout-minutes: 10
257+
steps:
258+
- name: Download release artifacts
259+
uses: actions/download-artifact@v4
260+
with:
261+
name: release-assets-${{ env.RELEASE_TAG }}
262+
path: dist/release
263+
264+
- name: Create or update GitHub Release
265+
shell: bash
266+
env:
267+
GH_TOKEN: ${{ github.token }}
268+
run: |
269+
set -euo pipefail
270+
271+
notes_file="$RUNNER_TEMP/release-notes.md"
272+
cat >"$notes_file" <<EOF
273+
## Summary
274+
- Release ${RELEASE_TAG}
275+
276+
## What changed
277+
- Built release archives for Linux, macOS, and Windows.
278+
- Published multi-arch container images for the default runtime and the \`-allure3\` runtime variant.
279+
- Attached SHA256 checksums for the release assets.
280+
- Re-ran the repository verification gates in GitHub Actions before publication.
281+
282+
## Container images
283+
- \`ghcr.io/${GITHUB_REPOSITORY,,}:${RELEASE_TAG}\`
284+
- \`ghcr.io/${GITHUB_REPOSITORY,,}:${RELEASE_TAG}-allure3\`
285+
286+
## Verification
287+
- \`go test ./... -p 1\`
288+
- \`bash scripts/verify-docs-and-ci.sh\`
289+
- \`bash scripts/verify-helm-chart.sh\`
290+
- \`bash scripts/verify-compose-e2e.sh\`
291+
292+
## Breaking changes
293+
- None documented by the release workflow. Review the merged changes behind ${RELEASE_TAG} for upgrade notes.
294+
EOF
295+
296+
if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
297+
gh release upload "$RELEASE_TAG" dist/release/* --repo "$GITHUB_REPOSITORY" --clobber
298+
gh release edit "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --title "$RELEASE_TAG" --notes-file "$notes_file"
299+
else
300+
gh release create "$RELEASE_TAG" dist/release/* --repo "$GITHUB_REPOSITORY" --title "$RELEASE_TAG" --notes-file "$notes_file"
301+
fi

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ The CI upload examples under [examples/ci/](examples/ci/) are meant for downstre
225225
- [docs/architecture.md](docs/architecture.md) — runtime topology, request/data flow, storage layout, and observability surfaces
226226
- [docs/configuration.md](docs/configuration.md) — full `TESTIMONY_*` configuration reference
227227
- [docs/github-repository.md](docs/github-repository.md) — repository-level GitHub CI, release, and metadata baseline
228-
- [docs/release-guide.md](docs/release-guide.md)current manual release process and future automation target
228+
- [docs/release-guide.md](docs/release-guide.md)tag-driven release workflow, GHCR tag policy, and release operator guide
229229
- [CONTRIBUTING.md](CONTRIBUTING.md) — contributor workflow, verification commands, and PR expectations
230230
- [examples/ci/](examples/ci/) — GitHub Actions and GitLab CI upload examples
231231
- [LICENSE](LICENSE) — Apache-2.0 license

0 commit comments

Comments
 (0)