diff --git a/.gitea/workflows/build-release.yml b/.gitea/workflows/build-release.yml new file mode 100644 index 0000000..03c3408 --- /dev/null +++ b/.gitea/workflows/build-release.yml @@ -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/" diff --git a/main.go b/main.go index d44a7d3..9e46232 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,9 @@ import ( "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 var assets embed.FS