Skip to content

docs: the finish-the-release runbook and the approved release notes #441

docs: the finish-the-release runbook and the approved release notes

docs: the finish-the-release runbook and the approved release notes #441

Workflow file for this run

# AsyncAO CI: lint + race tests + alloc-gated benchmarks, then binary builds
# for every platform. Pull requests get downloadable binaries as artifacts.
#
# Each platform builds TWICE via a `variant` matrix: the full default binary
# (Discord Rich Presence bundled — pure stdlib IPC, no DLL, off by default; voice
# chat included) and a lean `-tags "nodiscord novoice"` binary with BOTH the
# Discord integration and the LemmyAO/Nyathena voice chat compiled out entirely.
# Neither is ever a forced dependency; the second artifact exists for anyone who
# wants a binary with zero Discord and zero voice code at all.
name: CI
on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:
env:
GO_VERSION: "1.24"
jobs:
test:
name: Lint & race tests (Linux)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install SDL2 + libwebp + libavif dev packages
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libwebp-dev libavif-dev libopus-dev
- name: go vet
run: go vet ./...
- name: staticcheck
uses: dominikh/staticcheck-action@v1
with:
version: "latest"
install-go: false
- name: Tests (race detector — §17.8 gate)
run: go test -race -timeout 600s -count=1 ./...
- name: "Tests (lean build — Discord + voice compiled out; nodiscord+novoice stubs must build & pass)"
run: go test -tags "nodiscord novoice" -timeout 600s -count=1 ./...
- name: Benchmarks (alloc gates — §15)
run: |
go test -run=NONE -bench=. -benchmem -benchtime=200ms -count=1 ./... | tee bench.txt
# Gate: the learned resolver path must stay ≤ 1 alloc/op and the
# cache hits + render frame at 0 allocs/op.
! grep -E "BenchmarkBuildCandidates_Learned.* *[2-9] allocs/op" bench.txt
! grep -E "BenchmarkCacheHit_T[12].* *[1-9] allocs/op" bench.txt
! grep -E "BenchmarkRenderFrame.* *[1-9] allocs/op" bench.txt
- name: Upload benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmarks
path: bench.txt
build-linux:
name: Binary (Linux x86_64)${{ matrix.variant.label }}
runs-on: ubuntu-latest
needs: test
strategy:
matrix:
variant:
- { label: "", tags: "", suffix: "" }
- { label: " - Discord-free, no voice", tags: "nodiscord novoice", suffix: "-nodiscord" }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install SDL2 + libwebp + libavif dev packages
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libwebp-dev libavif-dev libopus-dev
- name: Build
run: |
go build -tags "${{ matrix.variant.tags }}" -pgo=auto -trimpath -ldflags "-s -w" -o asyncao ./cmd/asyncao
- uses: actions/upload-artifact@v4
with:
name: asyncao-linux-x86_64${{ matrix.variant.suffix }}
path: asyncao
build-windows:
name: Binary (Windows x86_64)${{ matrix.variant.label }}
runs-on: windows-latest
needs: test
strategy:
matrix:
variant:
- { label: "", tags: "", suffix: "" }
- { label: " - Discord-free, no voice", tags: "nodiscord novoice", suffix: "-nodiscord" }
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-pkgconf
mingw-w64-ucrt-x86_64-SDL2
mingw-w64-ucrt-x86_64-SDL2_ttf
mingw-w64-ucrt-x86_64-SDL2_mixer
mingw-w64-ucrt-x86_64-libwebp
mingw-w64-ucrt-x86_64-libavif
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
# setup-go only edits the *Windows* PATH and no longer exports GOROOT
# (that stopped at Go 1.9), while the msys2 shell rebuilds a minimal
# PATH of its own — so the toolchain is invisible in the build step.
# Publish GOROOT via GITHUB_ENV; non-PATH env vars do cross into MSYS2.
- name: Expose GOROOT to the MSYS2 shell
shell: pwsh
run: |
"GOROOT=$(go env GOROOT)" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Build + bundle DLLs
run: |
export PATH="$(cygpath -u "$GOROOT")/bin:$PATH"
export CGO_ENABLED=1
go build -tags "${{ matrix.variant.tags }}" -pgo=auto -trimpath -ldflags "-s -w" -o dist/asyncao.exe ./cmd/asyncao
# Ship the runtime DLLs next to the exe so it runs anywhere.
ldd dist/asyncao.exe | grep -io '/ucrt64/bin/[a-z0-9_.+-]*\.dll' | sort -u | while read -r dll; do
cp "$dll" dist/
done
- uses: actions/upload-artifact@v4
with:
name: asyncao-windows-x86_64${{ matrix.variant.suffix }}
path: dist/
build-macos:
name: Binary (macOS arm64)${{ matrix.variant.label }}
runs-on: macos-latest
needs: test
strategy:
matrix:
variant:
- { label: "", tags: "", suffix: "" }
- { label: " - Discord-free, no voice", tags: "nodiscord novoice", suffix: "-nodiscord" }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
# dylibbundler is needed by scripts/bundle-macos.sh. Ogg-Opus MUSIC
# decode/seek comes from the opusfile FORMULA, hard-linked into
# libSDL2_mixer by its formula (--enable-music-opus with the shared/dlopen
# path disabled); the bare `opus` formula is only the codec (voice chat)
# and provides no Ogg demuxing/seeking. brew already pulls opusfile as an
# sdl2_mixer dependency — naming it here pins that requirement explicitly
# (release.yml installs the same list — keep them consistent).
- name: Install SDL2 + libwebp + libavif
run: brew install sdl2 sdl2_ttf sdl2_mixer webp libavif opus opusfile pkg-config dylibbundler
- name: Build
run: |
export CGO_ENABLED=1
go build -tags "${{ matrix.variant.tags }}" -pgo=auto -trimpath -ldflags "-s -w" -o asyncao ./cmd/asyncao
# Run the release-time dylib bundler here too, BLOCKING, purely for its
# built-in otool assertion: if a new dependency ever links against a
# Homebrew path that the bundler can't rewrite, this fails the build and
# catches the "Library not loaded on a clean Mac" regression before release.
- name: Bundle dylibs + assert no Homebrew paths remain
run: |
bash scripts/bundle-macos.sh asyncao macos-stage
- uses: actions/upload-artifact@v4
with:
name: asyncao-macos-arm64${{ matrix.variant.suffix }}
path: asyncao
# The bare Linux binary above runs only where SDL2/webp/avif are already
# installed. This job packages a self-contained AppImage — one executable that
# bundles the runtime libraries — so non-Windows users get a download that
# "just works" like the Windows DLL bundle does. (Default variant; the
# Discord-free flavor remains available as the bare binary artifact.)
appimage-linux:
name: AppImage (Linux x86_64)${{ matrix.variant.label }}
runs-on: ubuntu-latest
needs: test
strategy:
matrix:
variant:
- { label: "", tags: "", suffix: "", output: "AsyncAO-x86_64.AppImage" }
- { label: " - Discord-free, no voice", tags: "nodiscord novoice", suffix: "-nodiscord", output: "AsyncAO-nodiscord-x86_64.AppImage" }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install SDL2 + libwebp + libavif dev packages + AppImage tooling
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libwebp-dev libavif-dev libopus-dev \
patchelf desktop-file-utils
- name: Build the binary
run: |
export CGO_ENABLED=1
go build -tags "${{ matrix.variant.tags }}" -pgo=auto -trimpath -ldflags "-s -w" -o asyncao ./cmd/asyncao
- name: Package the AppImage
run: |
chmod +x scripts/build-appimage.sh
APPIMAGE_OUTPUT="${{ matrix.variant.output }}" scripts/build-appimage.sh ./asyncao
- uses: actions/upload-artifact@v4
with:
name: asyncao-linux-x86_64-AppImage${{ matrix.variant.suffix }}
path: dist/${{ matrix.variant.output }}