Skip to content

Release

Release #18

Workflow file for this run

name: Release
on:
workflow_dispatch:
permissions: {}
jobs:
release:
name: Build & Draft Release
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
outputs:
version: ${{ steps.version.outputs.version }}
release_tag: ${{ steps.release.outputs.tag }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Extract version from source
id: version
run: |
version=$(grep -m1 'Version.*=' internal/buildinfo/version.go | sed 's/.*"\(.*\)".*/\1/')
if [ -z "$version" ]; then
echo "::error::Could not extract Version from internal/buildinfo/version.go"
exit 1
fi
tag="v${version}"
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
- name: Check tag does not already exist
run: |
if git rev-parse "refs/tags/${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
echo "::error::Tag ${{ steps.version.outputs.tag }} already exists."
exit 1
fi
- name: Create tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}"
git push origin "${{ steps.version.outputs.tag }}"
- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve draft release tag
id: release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# GoReleaser creates draft releases under an "untagged-*" slug,
# so gh release upload by version tag returns 404. Look up the
# actual tag GitHub assigned to the draft.
release_tag=$(gh api "repos/${{ github.repository }}/releases" \
--jq '[.[] | select(.draft and .tag_name == "${{ steps.version.outputs.tag }}")] | first | .tag_name')
if [ -z "$release_tag" ] || [ "$release_tag" = "null" ]; then
echo "::error::Could not find draft release for ${{ steps.version.outputs.tag }}"
exit 1
fi
echo "tag=$release_tag" >> "$GITHUB_OUTPUT"
echo "Resolved draft release tag: $release_tag"
- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Locate binaries and packages
id: binaries
run: |
DARWIN=$(find dist -type f -name '*darwin_unnotarized' | head -1)
WIN_AMD64=$(find dist -type f -name '*.exe' -path '*windows_amd64*' | head -1)
WIN_ARM64=$(find dist -type f -name '*.exe' -path '*windows_arm64*' | head -1)
LINUX_AMD64=$(find dist -type f -name 'stepsecurity-dev-machine-guard' -path '*linux_amd64*' | head -1)
LINUX_ARM64=$(find dist -type f -name 'stepsecurity-dev-machine-guard' -path '*linux_arm64*' | head -1)
DEB_AMD64=$(find dist -type f -name '*-amd64.deb' | head -1)
DEB_ARM64=$(find dist -type f -name '*-arm64.deb' | head -1)
RPM_AMD64=$(find dist -type f -name '*-amd64.rpm' | head -1)
RPM_ARM64=$(find dist -type f -name '*-arm64.rpm' | head -1)
for label in "darwin:${DARWIN}" "windows_amd64:${WIN_AMD64}" "windows_arm64:${WIN_ARM64}" "linux_amd64:${LINUX_AMD64}" "linux_arm64:${LINUX_ARM64}" "deb_amd64:${DEB_AMD64}" "deb_arm64:${DEB_ARM64}" "rpm_amd64:${RPM_AMD64}" "rpm_arm64:${RPM_ARM64}"; do
name="${label%%:*}"
path="${label#*:}"
if [ -z "$path" ] || [ ! -f "$path" ]; then
echo "::error::Artifact not found for ${name}"
find dist -type f
exit 1
fi
done
echo "darwin=$DARWIN" >> "$GITHUB_OUTPUT"
echo "win_amd64=$WIN_AMD64" >> "$GITHUB_OUTPUT"
echo "win_arm64=$WIN_ARM64" >> "$GITHUB_OUTPUT"
echo "linux_amd64=$LINUX_AMD64" >> "$GITHUB_OUTPUT"
echo "linux_arm64=$LINUX_ARM64" >> "$GITHUB_OUTPUT"
echo "deb_amd64=$DEB_AMD64" >> "$GITHUB_OUTPUT"
echo "deb_arm64=$DEB_ARM64" >> "$GITHUB_OUTPUT"
echo "rpm_amd64=$RPM_AMD64" >> "$GITHUB_OUTPUT"
echo "rpm_arm64=$RPM_ARM64" >> "$GITHUB_OUTPUT"
- name: Sign artifacts with Sigstore
shell: bash
run: |
sign_with_retry() {
local blob="$1"
local bundle="$2"
for attempt in 1 2 3; do
if cosign sign-blob "$blob" --bundle "$bundle" --yes; then
return 0
fi
echo "::warning::Signing attempt $attempt failed for $(basename "$blob"), retrying in 10s..."
sleep 10
done
echo "::error::Signing failed for $(basename "$blob") after 3 attempts"
return 1
}
sign_with_retry "${{ steps.binaries.outputs.darwin }}" \
"dist/stepsecurity-dev-machine-guard-darwin_unnotarized.bundle"
sign_with_retry "${{ steps.binaries.outputs.win_amd64 }}" \
"dist/stepsecurity-dev-machine-guard-windows_amd64.exe.bundle"
sign_with_retry "${{ steps.binaries.outputs.win_arm64 }}" \
"dist/stepsecurity-dev-machine-guard-windows_arm64.exe.bundle"
sign_with_retry "${{ steps.binaries.outputs.linux_amd64 }}" \
"dist/stepsecurity-dev-machine-guard-linux_amd64.bundle"
sign_with_retry "${{ steps.binaries.outputs.linux_arm64 }}" \
"dist/stepsecurity-dev-machine-guard-linux_arm64.bundle"
sign_with_retry "${{ steps.binaries.outputs.deb_amd64 }}" \
"${{ steps.binaries.outputs.deb_amd64 }}.bundle"
sign_with_retry "${{ steps.binaries.outputs.deb_arm64 }}" \
"${{ steps.binaries.outputs.deb_arm64 }}.bundle"
sign_with_retry "${{ steps.binaries.outputs.rpm_amd64 }}" \
"${{ steps.binaries.outputs.rpm_amd64 }}.bundle"
sign_with_retry "${{ steps.binaries.outputs.rpm_arm64 }}" \
"${{ steps.binaries.outputs.rpm_arm64 }}.bundle"
- name: Upload cosign bundles
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ steps.release.outputs.tag }}" \
dist/stepsecurity-dev-machine-guard-darwin_unnotarized.bundle \
dist/stepsecurity-dev-machine-guard-windows_amd64.exe.bundle \
dist/stepsecurity-dev-machine-guard-windows_arm64.exe.bundle \
dist/stepsecurity-dev-machine-guard-linux_amd64.bundle \
dist/stepsecurity-dev-machine-guard-linux_arm64.bundle \
"${{ steps.binaries.outputs.deb_amd64 }}.bundle" \
"${{ steps.binaries.outputs.deb_arm64 }}.bundle" \
"${{ steps.binaries.outputs.rpm_amd64 }}.bundle" \
"${{ steps.binaries.outputs.rpm_arm64 }}.bundle" \
--clobber
- name: Attest build provenance
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: |
${{ steps.binaries.outputs.darwin }}
${{ steps.binaries.outputs.win_amd64 }}
${{ steps.binaries.outputs.win_arm64 }}
${{ steps.binaries.outputs.linux_amd64 }}
${{ steps.binaries.outputs.linux_arm64 }}
${{ steps.binaries.outputs.deb_amd64 }}
${{ steps.binaries.outputs.deb_arm64 }}
${{ steps.binaries.outputs.rpm_amd64 }}
${{ steps.binaries.outputs.rpm_arm64 }}
build-msi:
name: Build & Sign MSIs
needs: release
runs-on: windows-latest
permissions:
contents: write
id-token: write
attestations: write
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install WiX 4 + Util extension
# WiX 4 ships as a .NET global tool. The Util extension (WixQuietExec
# and friends) is a separate NuGet package that must be added to the
# global wix tool before referencing util: namespace types.
shell: pwsh
run: |
dotnet tool install --global wix --version 4.0.5
wix --version
wix extension add --global WixToolset.Util.wixext/4.0.5
wix extension list --global
- name: Download Windows .exe assets from draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: pwsh
run: |
$tag = "${{ needs.release.outputs.release_tag }}"
New-Item -ItemType Directory -Path dist -Force | Out-Null
# Goreleaser produces archive names like:
# stepsecurity-dev-machine-guard-<version>-windows_amd64.exe
# We download them by exact pattern to dist/.
gh release download "$tag" `
-R "${{ github.repository }}" `
-p "*-windows_amd64.exe" `
-p "*-windows_arm64.exe" `
-D dist
Get-ChildItem dist | Format-Table Name, Length
- name: Build MSIs (x64 + arm64)
shell: pwsh
run: |
$version = "${{ needs.release.outputs.version }}"
$amd64 = Get-ChildItem dist -Filter "*-windows_amd64.exe" | Select-Object -First 1
$arm64 = Get-ChildItem dist -Filter "*-windows_arm64.exe" | Select-Object -First 1
if (-not $amd64 -or -not $arm64) {
Write-Error "Windows .exe assets missing under dist/"
exit 1
}
wix build packaging/windows/Product.wxs `
-arch x64 `
-ext WixToolset.Util.wixext `
-d Arch=x64 `
-d "Version=$version" `
-d "BinaryPath=$($amd64.FullName)" `
-out "dist/stepsecurity-dev-machine-guard-$version-x64.msi"
wix build packaging/windows/Product.wxs `
-arch arm64 `
-ext WixToolset.Util.wixext `
-d Arch=arm64 `
-d "Version=$version" `
-d "BinaryPath=$($arm64.FullName)" `
-out "dist/stepsecurity-dev-machine-guard-$version-arm64.msi"
Get-ChildItem dist -Filter "*.msi" | Format-Table Name, Length
- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Sign MSIs with Sigstore
shell: bash
run: |
set -euo pipefail
version="${{ needs.release.outputs.version }}"
for arch in x64 arm64; do
msi="dist/stepsecurity-dev-machine-guard-${version}-${arch}.msi"
bundle="${msi}.bundle"
for attempt in 1 2 3; do
if cosign sign-blob "$msi" --bundle "$bundle" --yes; then
echo "Signed $msi"
break
fi
echo "::warning::Sign attempt $attempt failed for $msi, retrying in 10s..."
sleep 10
done
test -f "$bundle" || { echo "::error::Failed to sign $msi"; exit 1; }
done
- name: Upload MSIs and bundles to draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
tag="${{ needs.release.outputs.release_tag }}"
version="${{ needs.release.outputs.version }}"
gh release upload "$tag" \
"dist/stepsecurity-dev-machine-guard-${version}-x64.msi" \
"dist/stepsecurity-dev-machine-guard-${version}-arm64.msi" \
"dist/stepsecurity-dev-machine-guard-${version}-x64.msi.bundle" \
"dist/stepsecurity-dev-machine-guard-${version}-arm64.msi.bundle" \
--clobber
- name: Attest MSI build provenance
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: |
dist/stepsecurity-dev-machine-guard-${{ needs.release.outputs.version }}-x64.msi
dist/stepsecurity-dev-machine-guard-${{ needs.release.outputs.version }}-arm64.msi