ci: build + sign workflow for Windows release (Azure Key Vault + jsign)

Add Gitea Actions workflow that builds wraith.exe for Windows amd64,
signs it with an Azure Key Vault certificate via jsign, and uploads
the signed binary with a version.json manifest.

Also adds a `version` var to main.go for -ldflags injection at build time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-17 06:38:49 -04:00
parent cb4b8ec136
commit fe19ee73e2
2 changed files with 188 additions and 0 deletions

View File

@ -0,0 +1,185 @@
# =============================================================================
# Wraith — Build & Sign Release
# =============================================================================
# Builds the Wails v3 desktop app for Windows amd64, signs it with an
# Azure Key Vault code-signing certificate via jsign, then uploads the
# signed binary and version manifest as release artifacts.
#
# Trigger: push a tag matching v* (e.g. v1.0.0) or run manually.
#
# Required secrets:
# AZURE_TENANT_ID — Azure AD tenant
# AZURE_CLIENT_ID — Service principal client ID
# AZURE_CLIENT_SECRET — Service principal secret
# AZURE_KEY_VAULT_URL — e.g. https://my-vault.vault.azure.net
# AZURE_CERT_NAME — Certificate/key name in the vault
# GIT_TOKEN — PAT for cloning private repo
# =============================================================================
name: Build & Sign Wraith
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
build-and-sign:
name: Build Windows + Sign
runs-on: linux
steps:
# ---------------------------------------------------------------
# Checkout
# ---------------------------------------------------------------
- name: Checkout code
run: |
git clone --depth 1 --branch ${{ github.ref_name }} \
https://${{ secrets.GIT_TOKEN }}@git.command.vigilcyber.com/vigilcyber/wraith.git .
# ---------------------------------------------------------------
# Extract version from tag
# ---------------------------------------------------------------
- name: Get version from tag
id: version
run: |
# Strip leading "v" from the tag (v1.2.3 -> 1.2.3)
TAG=$(echo "${{ github.ref_name }}" | sed 's/^v//')
echo "version=${TAG}" >> $GITHUB_OUTPUT
echo "Building version: ${TAG}"
# ---------------------------------------------------------------
# Install toolchain: Go, Node, npm
# ---------------------------------------------------------------
- name: Install Node.js
run: |
# Install Node.js LTS if not present
if ! command -v node >/dev/null 2>&1; then
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y -qq nodejs
fi
node --version
npm --version
# ---------------------------------------------------------------
# Build frontend assets
# ---------------------------------------------------------------
- name: Build frontend
run: |
cd frontend
npm ci
npm run build
echo "Frontend build complete:"
ls -la dist/
# ---------------------------------------------------------------
# Build Windows amd64 binary
# ---------------------------------------------------------------
# Wails v3 cross-compilation from Linux to Windows requires CGO
# for the webview2 bindings. Rather than pull in a full MinGW
# cross-compiler, we build the Go binary directly — the frontend
# assets are embedded via //go:embed in main.go, so the result is
# a single self-contained .exe.
# ---------------------------------------------------------------
- name: Build wraith.exe (Windows amd64)
run: |
VERSION="${{ steps.version.outputs.version }}"
echo "=== Cross-compiling wraith.exe for Windows amd64 ==="
mkdir -p dist
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \
go build \
-ldflags="-s -w -X main.version=${VERSION}" \
-o dist/wraith.exe \
.
ls -la dist/wraith.exe
# ---------------------------------------------------------------
# Code signing — jsign + Azure Key Vault
# ---------------------------------------------------------------
- name: Install jsign
run: |
# jsign: Java-based Authenticode signing tool that runs on
# Linux and supports Azure Key Vault as a keystore.
JSIGN_VERSION="7.0"
curl -sSL -o /usr/local/bin/jsign.jar \
"https://github.com/ebourg/jsign/releases/download/${JSIGN_VERSION}/jsign-${JSIGN_VERSION}.jar"
# Ensure a JRE is available (jsign needs Java)
command -v java >/dev/null 2>&1 || {
apt-get update -qq && apt-get install -y -qq default-jre-headless
}
- name: Get Azure Key Vault access token
id: azure-token
run: |
# OAuth2 client credentials flow for Azure Key Vault access
TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/${{ secrets.AZURE_TENANT_ID }}/oauth2/v2.0/token" \
-d "client_id=${{ secrets.AZURE_CLIENT_ID }}" \
-d "client_secret=${{ secrets.AZURE_CLIENT_SECRET }}" \
-d "scope=https://vault.azure.net/.default" \
-d "grant_type=client_credentials" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "::add-mask::${TOKEN}"
echo "token=${TOKEN}" >> $GITHUB_OUTPUT
- name: Sign wraith.exe
run: |
echo "=== Signing wraith.exe with Azure Key Vault certificate ==="
java -jar /usr/local/bin/jsign.jar \
--storetype AZUREKEYVAULT \
--keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" \
--storepass "${{ steps.azure-token.outputs.token }}" \
--alias "${{ secrets.AZURE_CERT_NAME }}" \
--tsaurl http://timestamp.digicert.com \
--tsmode RFC3161 \
dist/wraith.exe
echo "Signing complete."
# ---------------------------------------------------------------
# Version manifest
# ---------------------------------------------------------------
- name: Create version.json
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA256=$(sha256sum dist/wraith.exe | awk '{print $1}')
cat > dist/version.json << EOF
{
"version": "${VERSION}",
"filename": "wraith.exe",
"sha256": "${SHA256}",
"platform": "windows",
"architecture": "amd64",
"released": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"signed": true
}
EOF
echo "=== version.json ==="
cat dist/version.json
# ---------------------------------------------------------------
# Upload release artifacts
# ---------------------------------------------------------------
- name: Upload release artifacts
run: |
VERSION="${{ steps.version.outputs.version }}"
ENDPOINT="https://files.command.vigilcyber.com"
echo "=== Uploading Wraith ${VERSION} ==="
# Versioned path
aws s3 cp dist/ "s3://agents/wraith/${VERSION}/windows/amd64/" \
--recursive --endpoint-url "$ENDPOINT" --no-sign-request
# Latest path (overwritten on each release)
aws s3 sync dist/ "s3://agents/wraith/latest/windows/amd64/" \
--delete --endpoint-url "$ENDPOINT" --no-sign-request
echo "=== Upload complete ==="
echo "Versioned: ${ENDPOINT}/agents/wraith/${VERSION}/windows/amd64/"
echo "Latest: ${ENDPOINT}/agents/wraith/latest/windows/amd64/"

View File

@ -9,6 +9,9 @@ import (
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
) )
// version is set at build time via -ldflags "-X main.version=..."
var version = "dev"
//go:embed all:frontend/dist //go:embed all:frontend/dist
var assets embed.FS var assets embed.FS