Skip to content

Commit 81b876c

Browse files
ashishkurmiclaude
andcommitted
ci: add workflow to build test binaries and MSIs on demand
Adds .github/workflows/test-build.yml, a workflow_dispatch-only workflow that produces test binaries and MSIs from an arbitrary commit without cutting a release tag. The existing release.yml is intentionally left untouched — it remains the single source of truth for tagged releases (cosign signing, build-provenance attestation, draft GitHub release publishing). Motivation ---------- This fork needs a way to hand out test artifacts for in-progress changes (e.g. a pre-merge PR review build, an ad-hoc smoke test of a particular commit) without polluting the release pipeline or incrementing the version in internal/buildinfo/version.go. Workflow shape -------------- Triggered only via workflow_dispatch, with two inputs: - commit_id (required): the SHA to check out and build from. - pr_id (optional): a PR number to comment on with the run link. Three jobs: build (ubuntu-latest) - Checks out the requested commit at fetch-depth: 0. - Reads the build version from internal/buildinfo/version.go for use by the MSI step. - Runs `goreleaser release --snapshot --clean --skip=publish` so no tag is created or pushed, and no draft release is opened on GitHub. Cosign signing and attestation are deliberately skipped — they are release-only concerns and would just add cost and latency for a test build. - Goreleaser snapshot leaves the per-OS/arch binaries inside their builder subdirectories (dist/universal_darwin_all/..., dist/stepsecurity-dev-machine-guard_linux_amd64_v1/..., etc.). A staging step locates each one via find (mirroring release.yml's lookup style) and copies it into a flat staging/ directory with release-style filenames, so artifacts look the same as a real release and the MSI job's Get-ChildItem filter keeps working. - Uploads three per-platform artifacts: * darwin — universal darwin binary * linux — linux_amd64 + linux_arm64 binaries, .deb, .rpm * windows-exes — agent + launcher .exes for amd64 and arm64 build-msi (windows-latest) - Installs WiX 4 + the Util extension (same versions release.yml uses). - Downloads the windows-exes artifact into dist/. - Builds x64 + arm64 MSIs with the same `wix build` invocation as release.yml. - Uploads windows-msis as a fourth artifact. comment-on-pr (ubuntu-latest) - Runs only when pr_id is non-empty and both build jobs succeed. - Posts a comment on the PR linking the short SHA (to github.com/<repo>/commit/<sha>) and the workflow run URL where reviewers can download the artifacts. - pull-requests:write is scoped to this job only; the build jobs run with contents:read. Hardening --------- - step-security/harden-runner runs first in every job with egress-policy: audit. - All third-party actions are pinned to full commit SHAs with the version in a trailing comment, matching release.yml and msi-smoke.yml. - The top-level permissions block is empty; each job opts into only the scopes it needs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 85f35b8 commit 81b876c

1 file changed

Lines changed: 247 additions & 0 deletions

File tree

.github/workflows/test-build.yml

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
name: Test Build (Binaries & MSIs)
2+
3+
# Build test binaries and MSIs from an arbitrary commit, without cutting a
4+
# release tag or touching the upstream release pipeline. Artifacts are
5+
# uploaded to the workflow run for download. Optionally posts a comment on
6+
# a PR linking to the run and the commit it was built from.
7+
8+
on:
9+
workflow_dispatch:
10+
inputs:
11+
commit_id:
12+
description: "Commit SHA to build the binaries from"
13+
required: true
14+
type: string
15+
pr_id:
16+
description: "Optional PR number to comment on with the artifact location"
17+
required: false
18+
type: string
19+
20+
permissions: {}
21+
22+
jobs:
23+
build:
24+
name: Build binaries (goreleaser snapshot)
25+
runs-on: ubuntu-latest
26+
permissions:
27+
contents: read
28+
outputs:
29+
version: ${{ steps.version.outputs.version }}
30+
31+
steps:
32+
- name: Harden the runner (Audit all outbound calls)
33+
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3
34+
with:
35+
egress-policy: audit
36+
37+
- name: Checkout repository at requested commit
38+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
39+
with:
40+
ref: ${{ inputs.commit_id }}
41+
fetch-depth: 0
42+
43+
- name: Extract version from source
44+
id: version
45+
run: |
46+
version=$(grep -m1 'Version.*=' internal/buildinfo/version.go | sed 's/.*"\(.*\)".*/\1/')
47+
if [ -z "$version" ]; then
48+
echo "::error::Could not extract Version from internal/buildinfo/version.go"
49+
exit 1
50+
fi
51+
echo "version=${version}" >> "$GITHUB_OUTPUT"
52+
53+
- name: Set up Go
54+
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
55+
with:
56+
go-version-file: go.mod
57+
58+
- name: Run GoReleaser (snapshot, no publish)
59+
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
60+
with:
61+
distribution: goreleaser
62+
version: latest
63+
args: release --snapshot --clean --skip=publish
64+
env:
65+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66+
67+
- name: List dist/
68+
run: find dist -type f
69+
70+
- name: Stage artifacts with release-style names
71+
run: |
72+
set -euo pipefail
73+
version="${{ steps.version.outputs.version }}"
74+
mkdir -p staging
75+
76+
# Goreleaser snapshot leaves the per-OS/arch binaries inside their
77+
# builder subdirectories (e.g. dist/stepsecurity-dev-machine-guard_linux_amd64_v1/...).
78+
# Locate each by name+arch path and copy out to staging/ with the
79+
# release-style filename, so the artifacts look the same as a real
80+
# release and the MSI job's Get-ChildItem filter still matches.
81+
find_one() {
82+
local result
83+
result=$(find dist -type f "$@" | head -1)
84+
if [ -z "$result" ] || [ ! -f "$result" ]; then
85+
echo "::error::No file matched: $*"
86+
find dist -type f
87+
exit 1
88+
fi
89+
printf '%s\n' "$result"
90+
}
91+
92+
darwin_src=$(find_one -name '*-darwin_unnotarized')
93+
linux_amd64_src=$(find_one -name 'stepsecurity-dev-machine-guard' -path '*linux_amd64*')
94+
linux_arm64_src=$(find_one -name 'stepsecurity-dev-machine-guard' -path '*linux_arm64*')
95+
win_amd64_src=$(find_one -name 'stepsecurity-dev-machine-guard.exe' -path '*windows_amd64*')
96+
win_arm64_src=$(find_one -name 'stepsecurity-dev-machine-guard.exe' -path '*windows_arm64*')
97+
win_task_amd64_src=$(find_one -name 'stepsecurity-dev-machine-guard-task.exe' -path '*windows_amd64*')
98+
win_task_arm64_src=$(find_one -name 'stepsecurity-dev-machine-guard-task.exe' -path '*windows_arm64*')
99+
100+
cp "$darwin_src" "staging/stepsecurity-dev-machine-guard-${version}-darwin_unnotarized"
101+
cp "$linux_amd64_src" "staging/stepsecurity-dev-machine-guard-${version}-linux_amd64"
102+
cp "$linux_arm64_src" "staging/stepsecurity-dev-machine-guard-${version}-linux_arm64"
103+
cp "$win_amd64_src" "staging/stepsecurity-dev-machine-guard-${version}-windows_amd64.exe"
104+
cp "$win_arm64_src" "staging/stepsecurity-dev-machine-guard-${version}-windows_arm64.exe"
105+
cp "$win_task_amd64_src" "staging/stepsecurity-dev-machine-guard-task-${version}-windows_amd64.exe"
106+
cp "$win_task_arm64_src" "staging/stepsecurity-dev-machine-guard-task-${version}-windows_arm64.exe"
107+
cp dist/*.deb staging/
108+
cp dist/*.rpm staging/
109+
ls -la staging/
110+
111+
- name: Upload macOS binary
112+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
113+
with:
114+
name: darwin
115+
path: staging/stepsecurity-dev-machine-guard-*-darwin_unnotarized
116+
if-no-files-found: error
117+
118+
- name: Upload Linux binaries and packages
119+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
120+
with:
121+
name: linux
122+
path: |
123+
staging/stepsecurity-dev-machine-guard-*-linux_amd64
124+
staging/stepsecurity-dev-machine-guard-*-linux_arm64
125+
staging/*.deb
126+
staging/*.rpm
127+
if-no-files-found: error
128+
129+
- name: Upload Windows .exe binaries
130+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
131+
with:
132+
name: windows-exes
133+
path: |
134+
staging/stepsecurity-dev-machine-guard-*-windows_amd64.exe
135+
staging/stepsecurity-dev-machine-guard-*-windows_arm64.exe
136+
staging/stepsecurity-dev-machine-guard-task-*-windows_amd64.exe
137+
staging/stepsecurity-dev-machine-guard-task-*-windows_arm64.exe
138+
if-no-files-found: error
139+
140+
build-msi:
141+
name: Build MSIs
142+
needs: build
143+
runs-on: windows-latest
144+
permissions:
145+
contents: read
146+
147+
steps:
148+
- name: Harden the runner (Audit all outbound calls)
149+
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3
150+
with:
151+
egress-policy: audit
152+
153+
- name: Checkout repository at requested commit
154+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
155+
with:
156+
ref: ${{ inputs.commit_id }}
157+
158+
- name: Install WiX 4 + Util extension
159+
shell: pwsh
160+
run: |
161+
dotnet tool install --global wix --version 4.0.5
162+
wix --version
163+
wix extension add --global WixToolset.Util.wixext/4.0.5
164+
wix extension list --global
165+
166+
- name: Download Windows .exes from build job
167+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
168+
with:
169+
name: windows-exes
170+
path: dist
171+
172+
- name: Build MSIs (x64 + arm64)
173+
shell: pwsh
174+
run: |
175+
$version = "${{ needs.build.outputs.version }}"
176+
# Both agent and launcher .exes share the *-windows_<arch>.exe
177+
# suffix; filter on the -task- segment to tell them apart.
178+
$amd64 = Get-ChildItem dist -Filter "stepsecurity-dev-machine-guard-*-windows_amd64.exe" | Where-Object Name -NotLike '*-task-*' | Select-Object -First 1
179+
$arm64 = Get-ChildItem dist -Filter "stepsecurity-dev-machine-guard-*-windows_arm64.exe" | Where-Object Name -NotLike '*-task-*' | Select-Object -First 1
180+
$taskAmd64 = Get-ChildItem dist -Filter "stepsecurity-dev-machine-guard-task-*-windows_amd64.exe" | Select-Object -First 1
181+
$taskArm64 = Get-ChildItem dist -Filter "stepsecurity-dev-machine-guard-task-*-windows_arm64.exe" | Select-Object -First 1
182+
if (-not $amd64 -or -not $arm64 -or -not $taskAmd64 -or -not $taskArm64) {
183+
Write-Error "Windows .exe assets (agent + launcher, both arches) missing under dist/"
184+
Get-ChildItem dist | Format-Table Name
185+
exit 1
186+
}
187+
188+
wix build packaging/windows/Product.wxs `
189+
-arch x64 `
190+
-ext WixToolset.Util.wixext `
191+
-d Arch=x64 `
192+
-d "Version=$version" `
193+
-d "BinaryPath=$($amd64.FullName)" `
194+
-d "LauncherPath=$($taskAmd64.FullName)" `
195+
-out "dist/stepsecurity-dev-machine-guard-$version-x64.msi"
196+
197+
wix build packaging/windows/Product.wxs `
198+
-arch arm64 `
199+
-ext WixToolset.Util.wixext `
200+
-d Arch=arm64 `
201+
-d "Version=$version" `
202+
-d "BinaryPath=$($arm64.FullName)" `
203+
-d "LauncherPath=$($taskArm64.FullName)" `
204+
-out "dist/stepsecurity-dev-machine-guard-$version-arm64.msi"
205+
206+
Get-ChildItem dist -Filter "*.msi" | Format-Table Name, Length
207+
208+
- name: Upload MSIs
209+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
210+
with:
211+
name: windows-msis
212+
path: dist/*.msi
213+
if-no-files-found: error
214+
215+
comment-on-pr:
216+
name: Comment on PR
217+
needs: [build, build-msi]
218+
if: ${{ inputs.pr_id != '' }}
219+
runs-on: ubuntu-latest
220+
permissions:
221+
pull-requests: write
222+
223+
steps:
224+
- name: Harden the runner (Audit all outbound calls)
225+
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3
226+
with:
227+
egress-policy: audit
228+
229+
- name: Post comment with run + commit links
230+
env:
231+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
232+
COMMIT_ID: ${{ inputs.commit_id }}
233+
PR_ID: ${{ inputs.pr_id }}
234+
RUN_ID: ${{ github.run_id }}
235+
REPO: ${{ github.repository }}
236+
run: |
237+
short_sha="${COMMIT_ID:0:7}"
238+
commit_url="https://github.com/${REPO}/commit/${COMMIT_ID}"
239+
run_url="https://github.com/${REPO}/actions/runs/${RUN_ID}"
240+
body=$(cat <<EOF
241+
Test binaries and MSIs are ready.
242+
243+
- Built from commit: [\`${short_sha}\`](${commit_url})
244+
- Workflow run (download artifacts here): [${RUN_ID}](${run_url})
245+
EOF
246+
)
247+
gh pr comment "$PR_ID" --repo "$REPO" --body "$body"

0 commit comments

Comments
 (0)