name: Release on: push: tags: - 'v*' permissions: contents: read jobs: build-linux: strategy: fail-fast: false matrix: include: - goarch: amd64 runner: ubuntu-latest container: quay.io/pypa/manylinux_2_28_x86_64@sha256:853663dc8253b62be437bb52a5caecffd020792af4442f55d927d22e0ea795ae - goarch: arm64 runner: ubuntu-24.04-arm container: quay.io/pypa/manylinux_2_28_aarch64@sha256:ca1f9d96910e129d38f999644a1f211a6cdb4147da9f19157a49d72c5092453a runs-on: ${{ matrix.runner }} container: ${{ matrix.container }} steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false - name: Trust git checkout shell: bash run: | git config --global --add safe.directory "$GITHUB_WORKSPACE" git status --short >/dev/null - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: go.mod - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" - name: Build frontend run: npm ci && npm run build working-directory: frontend - name: Embed frontend shell: bash run: | rm -rf internal/web/dist cp -r frontend/dist internal/web/dist - name: Restore pricing snapshot run: go run ./internal/pricing/cmd/litellm-snapshot -restore - name: Build shell: bash env: GOOS: linux GOARCH: ${{ matrix.goarch }} CGO_ENABLED: "1" run: | VERSION=${GITHUB_REF#refs/tags/v} COMMIT=${GITHUB_SHA::8} BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") # sqlite-vec's cgo bindings #include "sqlite3.h". The manylinux # image has no SQLite headers, and AlmaLinux 8's sqlite-devel # (3.26) is old enough that sqlite-vec would silently compile out # its vtab_in support. Use the header of the exact amalgamation # bundled and statically linked by mattn/go-sqlite3. # "-O2 -g" restates Go's built-in default, which setting # CGO_CFLAGS would otherwise replace, leaving the SQLite # amalgamation unoptimized (2-3x slower queries). go mod download github.com/mattn/go-sqlite3 mkdir -p .sqlite-include cp "$(go list -m -f '{{.Dir}}' github.com/mattn/go-sqlite3)/sqlite3-binding.h" .sqlite-include/sqlite3.h export CGO_CFLAGS="-O2 -g -I${PWD}/.sqlite-include" mkdir -p dist LDFLAGS="-s -w -X main.version=v${VERSION} -X main.commit=${COMMIT} -X main.buildDate=${BUILD_DATE}" go build -tags fts5 -buildvcs=false -ldflags="$LDFLAGS" -trimpath \ -o dist/agentsview ./cmd/agentsview # Smoke test (native builds — no cross-compilation) ./dist/agentsview --version # Verify glibc compatibility MAX_GLIBC="GLIBC_2.28" HIGHEST=$(objdump -T dist/agentsview | grep -oP 'GLIBC_\d+\.\d+' | sort -t. -k1,1n -k2,2n | tail -1) echo "Highest glibc symbol: $HIGHEST (max allowed: $MAX_GLIBC)" if [ "$(printf '%s\n%s' "$MAX_GLIBC" "$HIGHEST" | sort -t. -k1,1n -k2,2n | tail -1)" != "$MAX_GLIBC" ]; then echo "ERROR: Binary requires $HIGHEST but wheel claims $MAX_GLIBC" exit 1 fi cd dist ARCHIVE="agentsview_${VERSION}_linux_${{ matrix.goarch }}.tar.gz" tar czf "$ARCHIVE" agentsview rm agentsview - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: agentsview-linux-${{ matrix.goarch }} path: dist/*.tar.gz build: strategy: fail-fast: false matrix: include: - os: macos-15 goos: darwin goarch: amd64 - os: macos-15 goos: darwin goarch: arm64 - os: windows-latest goos: windows goarch: amd64 - os: windows-11-arm goos: windows goarch: arm64 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: go.mod - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" - name: Build frontend run: npm ci && npm run build working-directory: frontend - name: Embed frontend shell: bash run: | rm -rf internal/web/dist cp -r frontend/dist internal/web/dist - name: Restore pricing snapshot run: go run ./internal/pricing/cmd/litellm-snapshot -restore - name: Setup MinGW (Windows amd64) if: matrix.goos == 'windows' && matrix.goarch == 'amd64' uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 with: msystem: MINGW64 update: false install: mingw-w64-x86_64-gcc path-type: inherit - name: Setup llvm-mingw (Windows arm64) if: matrix.goos == 'windows' && matrix.goarch == 'arm64' shell: pwsh env: # Self-contained aarch64 mingw clang toolchain (clang + runtime + # lld). cgo (mattn/go-sqlite3) needs a C compiler; no Visual # Studio / Windows SDK required. Pinned to a version + SHA256 for # reproducibility and supply-chain integrity. LLVM_MINGW_VERSION: "20260602" LLVM_MINGW_NAME: llvm-mingw-20260602-ucrt-aarch64 LLVM_MINGW_SHA256: cb5c20fbe1808e31ada5cbe4efd9daa2fee19c91dac6ec5ca1ac46a9c7247e37 run: | $ErrorActionPreference = 'Stop' $zip = Join-Path $env:RUNNER_TEMP 'llvm-mingw.zip' $url = "https://github.com/mstorsjo/llvm-mingw/releases/download/$env:LLVM_MINGW_VERSION/$env:LLVM_MINGW_NAME.zip" Write-Host "Downloading $url" Invoke-WebRequest -Uri $url -OutFile $zip # Verify the archive against the pinned SHA256 before extracting or # executing any of it, so a swapped or compromised upstream asset # fails the build instead of producing a trojaned binary. $actual = (Get-FileHash -Path $zip -Algorithm SHA256).Hash if ($actual -ne $env:LLVM_MINGW_SHA256) { throw "Checksum mismatch for $url`n expected $env:LLVM_MINGW_SHA256`n actual $actual" } # 7-Zip is preinstalled on the runner image and extracts faster # than Expand-Archive for this many files. 7z x $zip -o"$env:RUNNER_TEMP" | Out-Null $bin = Join-Path $env:RUNNER_TEMP "$env:LLVM_MINGW_NAME\bin" $cc = Join-Path $bin 'aarch64-w64-mingw32-clang.exe' if (-not (Test-Path $cc)) { throw "CC not found at $cc" } Add-Content -Path $env:GITHUB_PATH -Value $bin Add-Content -Path $env:GITHUB_ENV -Value "CC=$cc" - name: Build shell: bash env: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} CGO_ENABLED: "1" MACOSX_DEPLOYMENT_TARGET: ${{ matrix.goos == 'darwin' && '11.0' || '' }} run: | VERSION=${GITHUB_REF#refs/tags/v} COMMIT=${GITHUB_SHA::8} BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") EXT="" if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi mkdir -p dist LDFLAGS="-s -w -X main.version=v${VERSION} -X main.commit=${COMMIT} -X main.buildDate=${BUILD_DATE}" go build -tags fts5 -ldflags="$LDFLAGS" -trimpath \ -o dist/agentsview${EXT} ./cmd/agentsview # Smoke test (skip cross-compiled darwin/amd64 on ARM runner) if [ "$GOARCH" = "$(go env GOHOSTARCH)" ]; then ./dist/agentsview${EXT} --version fi # Verify macOS deployment target if [ "$GOOS" = "darwin" ]; then MINOS=$(otool -l dist/agentsview | grep -A3 LC_BUILD_VERSION | grep minos | awk '{print $2}') echo "Minimum macOS version: $MINOS" if [ "$MINOS" != "11.0" ]; then echo "ERROR: Expected minos 11.0, got $MINOS" exit 1 fi fi cd dist if [ "$GOOS" = "windows" ]; then ARCHIVE="agentsview_${VERSION}_${{ matrix.goos }}_${{ matrix.goarch }}.zip" 7z a "$ARCHIVE" agentsview${EXT} else ARCHIVE="agentsview_${VERSION}_${{ matrix.goos }}_${{ matrix.goarch }}.tar.gz" tar czf "$ARCHIVE" agentsview${EXT} fi rm agentsview${EXT} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: agentsview-${{ matrix.goos }}-${{ matrix.goarch }} path: | dist/*.tar.gz dist/*.zip release: needs: [build-linux, build] runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: artifacts merge-multiple: true - name: Generate checksums run: | cd artifacts sha256sum *.tar.gz *.zip > SHA256SUMS cat SHA256SUMS - name: Get tag message id: tag_message run: | TAG_NAME="${GITHUB_REF#refs/tags/}" TAG_TYPE=$(git cat-file -t "$TAG_NAME") if [ "$TAG_TYPE" != "tag" ]; then echo "Warning: $TAG_NAME is a lightweight tag, using auto-generated notes" echo "has_body=false" >> $GITHUB_OUTPUT exit 0 fi TAG_MSG=$(git tag -l --format='%(contents:body)' "$TAG_NAME") if [ -n "$TAG_MSG" ]; then DELIM="TAGMSG_$(date +%s%N)" echo "body<<$DELIM" >> $GITHUB_OUTPUT echo "$TAG_MSG" >> $GITHUB_OUTPUT echo "$DELIM" >> $GITHUB_OUTPUT echo "has_body=true" >> $GITHUB_OUTPUT else echo "has_body=false" >> $GITHUB_OUTPUT fi - name: Create Release uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: files: | artifacts/*.tar.gz artifacts/*.zip artifacts/SHA256SUMS body: ${{ steps.tag_message.outputs.has_body == 'true' && steps.tag_message.outputs.body || '' }} generate_release_notes: ${{ steps.tag_message.outputs.has_body != 'true' }} pypi: needs: [build-linux, build, release] runs-on: ubuntu-latest environment: pypi permissions: contents: read id-token: write steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.14" - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: artifacts merge-multiple: true - name: Build wheels run: | VERSION=${GITHUB_REF#refs/tags/v} python scripts/build_wheels.py \ --version "$VERSION" \ --input-dir artifacts \ --output-dir wheels \ --readme README.md \ --require-all - name: Smoke test wheel run: | VERSION=${GITHUB_REF#refs/tags/v} pip install wheels/agentsview-${VERSION}-py3-none-manylinux_2_28_x86_64.whl agentsview --version - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: packages-dir: wheels/