ci(release): build every binary on GitHub and attach to releases

Per decision: no binaries committed to the repo; everything is built on GitHub
Actions and hosted as release artifacts (releases/latest/download/<artifact>).

- .github/workflows/release.yml: matrix builds the agent for
  linux(amd64/arm64/armv7), windows(amd64/arm64), darwin(amd64/arm64); tray for
  linux/windows; helper for windows; the fully-offline Inno setup.exe compiles on
  a windows runner via scripts/setup-build-env.ps1 -SkipGo -Build -CI. The
  publish job merges everything, writes SHA256SUMS, optionally signs with Azure
  Trusted Signing (secret-gated), and attaches to the tag's release.
- Replaces build-windows.yml (removed) — one release pipeline for all platforms.
- Untracks the committed dist binaries (they stay gitignored for local dev and
  are produced by CI now).
- DESIGN-WINDOWS.md §9 updated: consumers (install.sh, the SSO modal) download
  from GitHub release artifacts; SSO may mirror them into /resources for air-gap.
This commit is contained in:
2026-08-09 19:40:12 -07:00
parent d18de7d109
commit 98daa81a92
13 changed files with 157 additions and 126 deletions
-111
View File
@@ -1,111 +0,0 @@
name: build-windows
# Builds the Windows agent, tray, session helper, and the fully-offline Inno
# installer; signs them with Azure Trusted Signing; generates SHA256SUMS; and
# publishes the artifacts to the SSO resource tree (DESIGN-WINDOWS.md §9).
#
# The build itself is driven by scripts/setup-build-env.ps1 (idempotent dev/build
# environment setup: pinned Go version, per-user Inno Setup, checksum-verified
# vendor assets). The workflow only adds signing and publishing on top.
#
# Signing requires GitHub OIDC federation to Azure. If the Azure secrets are
# not configured the workflow still builds and attaches unsigned artifacts.
on:
push:
tags: ["v*"]
workflow_dispatch:
env:
GO_VERSION: "1.22.2"
VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || 'dev' }}
jobs:
build-windows:
runs-on: windows-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
# One idempotent step sets up Inno Setup + fetches/verifies the vendor
# assets and builds binaries + installer + runs the test suite.
- name: Setup build environment and build
shell: pwsh
run: |
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/setup-build-env.ps1 -SkipGo -Build -CI
if ($LASTEXITCODE -ne 0) { throw "setup-build-env failed" }
- name: Generate SHA256SUMS
shell: pwsh
run: |
Get-ChildItem dist -File | Where-Object {
$_.Name -match 'windows-.*\.exe$|setup\.exe$' -or $_.Name -eq 'SHA256SUMS'
} | ForEach-Object {
"{0} {1}" -f (Get-FileHash -Algorithm SHA256 $_.FullName).Hash.ToLower(), $_.Name
} | Set-Content -Path dist/SHA256SUMS
- name: Sign with Azure Trusted Signing
if: env.AZURE_TENANT_ID != ''
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: AzureSignTool (agent, tray, helper, CP, installer)
if: env.AZURE_TENANT_ID != ''
uses: azure/trusted-signing-action@v0.4.0
with:
endpoint: ${{ secrets.AZURE_TS_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.AZURE_TS_ACCOUNT }}
certificate-profile-name: ${{ secrets.AZURE_TS_CERT_PROFILE }}
files: |
dist/theta-agent-windows-*.exe
dist/theta-agent-tray-windows-*.exe
dist/theta-agent-helper-windows-*.exe
dist/theta-agent-*-setup.exe
output-files: ""
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: theta-agent-windows
path: |
dist/theta-agent-windows-*.exe
dist/theta-agent-helper-windows-*.exe
dist/theta-agent-tray-windows-*.exe
dist/theta-agent-*-setup.exe
dist/SHA256SUMS
- name: Attach to GitHub release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: |
dist/theta-agent-windows-*.exe
dist/theta-agent-helper-windows-*.exe
dist/theta-agent-tray-windows-*.exe
dist/theta-agent-*-setup.exe
dist/SHA256SUMS
# DESIGN-WINDOWS.md §9: the SSO holds the release resources. Publish when
# an admin-gated upload endpoint is configured; otherwise the artifacts
# are available from the GitHub release above.
- name: Publish to SSO resource tree
if: env.SSO_RESOURCE_PUBLISH_URL != ''
shell: pwsh
run: |
$headers = @{ Authorization = "Bearer $env:SSO_RESOURCE_PUBLISH_TOKEN" }
foreach ($f in Get-ChildItem dist -File) {
if ($f.Name -notmatch "setup|windows-|SHA256SUMS") { continue }
Invoke-RestMethod -Method Post -Uri "$env:SSO_RESOURCE_PUBLISH_URL/$($f.Name)" -InFile $f.FullName -Headers $headers
}
env:
SSO_RESOURCE_PUBLISH_URL: ${{ secrets.SSO_RESOURCE_PUBLISH_URL }}
SSO_RESOURCE_PUBLISH_TOKEN: ${{ secrets.SSO_RESOURCE_PUBLISH_TOKEN }}
+140
View File
@@ -0,0 +1,140 @@
name: release
# Builds every theta-agent binary on GitHub and attaches them to the release as
# artifacts (DESIGN-WINDOWS.md §9, and the "host binaries as release artifacts"
# decision): the agent for linux/windows/darwin, the tray for linux/windows,
# the session helper for windows, and the fully-offline Inno setup.exe. Nothing
# binary is committed to the repo; consumers (install.sh, the SSO Install Agent
# modal) download from releases/latest/download/<artifact>.
#
# git tag v2.1.0 && git push origin v2.1.0
# -> builds all binaries, compiles the installer, attaches everything
on:
push:
tags: ["v*"]
workflow_dispatch:
env:
GO_VERSION: "1.22.2"
jobs:
build-agent:
name: agent-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarm }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- goos: linux
goarch: amd64
artifact: theta-agent-linux-amd64
- goos: linux
goarch: arm64
artifact: theta-agent-linux-arm64
- goos: linux
goarch: arm
goarm: "7"
artifact: theta-agent-linux-armv7
- goos: windows
goarch: amd64
artifact: theta-agent-windows-amd64.exe
- goos: windows
goarch: arm64
artifact: theta-agent-windows-arm64.exe
- goos: darwin
goarch: amd64
artifact: theta-agent-darwin-amd64
- goos: darwin
goarch: arm64
artifact: theta-agent-darwin-arm64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Build
shell: bash
run: |
CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} GOARM=${{ matrix.goarm }} \
go build -ldflags="-s -w" -o dist/${{ matrix.artifact }} .
- name: Upload
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: dist/${{ matrix.artifact }}
if-no-files-found: error
build-desktop:
name: tray/helper/setup
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
# One idempotent script: installs per-user Inno Setup, fetches the
# checksum-verified vendor assets, builds agent/tray/helper for windows
# amd64+arm64, runs go test, and compiles the offline setup.exe.
- name: Build windows artifacts + installer
shell: pwsh
run: |
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/setup-build-env.ps1 -SkipGo -Build -CI
if ($LASTEXITCODE -ne 0) { throw "setup-build-env failed" }
- name: Upload windows artifacts
uses: actions/upload-artifact@v4
with:
name: windows-desktop
path: |
dist/theta-agent-tray-windows-*.exe
dist/theta-agent-helper-windows-*.exe
dist/theta-agent-windows-*.exe
dist/theta-agent-*-setup.exe
if-no-files-found: error
publish:
name: Attach to GitHub release
needs: [build-agent, build-desktop]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Generate SHA256SUMS
shell: bash
run: |
cd dist
sha256sum * | tee SHA256SUMS
# Optional Azure Trusted Signing (OIDC federation). Runs only when the
# Azure secrets are configured; otherwise the artifacts ship unsigned.
- name: Sign with Azure Trusted Signing
if: env.AZURE_TENANT_ID != ''
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: AzureSignTool
if: env.AZURE_TENANT_ID != ''
uses: azure/trusted-signing-action@v0.4.0
with:
endpoint: ${{ secrets.AZURE_TS_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.AZURE_TS_ACCOUNT }}
certificate-profile-name: ${{ secrets.AZURE_TS_CERT_PROFILE }}
files: dist/**
- name: Attach to release
uses: softprops/action-gh-release@v2
with:
files: dist/**
fail_on_unmatched_files: false
+17 -15
View File
@@ -206,22 +206,24 @@ Install-time behavior:
## 9. Build & release (GitHub Actions + Azure Trusted Signing)
- **Local dev builds** are self-signed / unsigned.
- **No binaries live in the repos.** Every artifact is built on GitHub Actions and
attached to the release (`releases/latest/download/<artifact>`) — see
`.github/workflows/release.yml`. Consumers download from there:
- `install.sh` (Linux) already fetches the agent/tray from `releases/latest/download/`.
- The SSO's Install Agent modal emits a PowerShell one-liner that downloads the
Windows `setup.exe` from `releases/latest/download/`.
- **Production builds** run in GitHub Actions:
- Build matrix: agent `windows-amd64`/`arm64`, tray, helper, OpenCredential CP
(MSBuild/.NET 4.8), then the Inno installer.
- **Azure Trusted Signing** signs the agent, tray, helper, CP DLL, and installer
(workflow federated identity → AzureSignTool). Authenticode chains verify offline,
which suits air-gap; SmartScreen reputation simply won't accumulate, which is expected.
- Release tags (e.g. `v2.0.1`) name the artifacts; `SHA256SUMS` manifest is generated.
- **The SSO holds all resources** (client installer is the deliverable; the server stack
is assumed to already run inside the air-gap):
- New resource tree: `/resources/theta-agent/windows/...` for the installer, loose
binaries, and `SHA256SUMS`.
- Release workflow uploads artifacts (admin-gated publish endpoint or mounted resource
dir); SSO pins a "latest" pointer.
- Self-update feed uses the existing signed `update_binary` flow; the agent's fetch is
made platform-aware (currently `cli.go` hardcodes the Linux artifact name).
- Matrix: agent for `linux`(amd64/arm64/armv7), `windows`(amd64/arm64), `darwin`(amd64/arm64);
tray for linux/windows; helper for windows.
- The fully-offline Inno installer compiles on a `windows-latest` runner via
`scripts/setup-build-env.ps1 -SkipGo -Build -CI` (which also runs `go test ./...`).
- **Azure Trusted Signing** optionally signs everything (workflow federated identity →
AzureSignTool, gated on secrets). Authenticode chains verify offline, which suits
air-gap; SmartScreen reputation simply won't accumulate, which is expected.
- Release tags (e.g. `v2.1.0`) name the artifacts; a `SHA256SUMS` manifest is attached.
- **Self-update** uses the existing signed `update_binary` flow; the agent's fetch is
platform-aware. On an air-gapped LAN the SSO can mirror the release artifacts into
`/resources/theta-agent/` at deploy time; nothing on the target host hits the internet.
## 10. Air-gap considerations
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.