Skip to content

chore(sdk-py): prepare v0.15.0 release (#984) #925

chore(sdk-py): prepare v0.15.0 release (#984)

chore(sdk-py): prepare v0.15.0 release (#984) #925

Workflow file for this run

name: "CI: daemon"
on:
push:
branches: [main]
paths:
- "daemon/**"
- "sdk/go/**"
- "sdk/ts/**"
- "sdk/py/**"
pull_request:
branches: [main]
paths:
- "daemon/**"
- "sdk/go/**"
- "sdk/ts/**"
- "sdk/py/**"
permissions:
contents: read
jobs:
test:
# Matrix exercises both Linux (SO_PEERCRED, /proc/<pid>/exe) and macOS
# (LOCAL_PEERCRED, LOCAL_PEEREPID, SYS_PROC_INFO(PROC_PIDPATHINFO))
# peer-credential paths in CI. fail-fast is off so a darwin-only flake
# doesn't mask a Linux regression and vice versa.
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
# Pin to the patch recorded in daemon/go.mod (ADR-0031) so CI stops
# floating to the latest 1.26.x — a moving toolchain would defeat the
# reproducible-build attestation Gate B asserts.
go-version-file: daemon/go.mod
# Node + pnpm: integration tests spawn the TS emitter helper
# (daemon/helpers/emit_ts.mjs), which imports
# sdk/ts/dist/emitter.js. The dist/ directory is generated by
# `pnpm build`, so we need a Node toolchain and a TS build before
# the Go integration test job runs. Corepack + pnpm version pin
# mirrors .github/workflows/sdk-ts.yml.
# Note: corepack must be enabled before setup-node's cache restoration,
# so we call it in a separate step first.
- run: corepack enable && corepack prepare pnpm@10.33.0 --activate
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: "24"
cache: "pnpm"
cache-dependency-path: sdk/ts/pnpm-lock.yaml
- name: Build TypeScript SDK
working-directory: sdk/ts
run: |
pnpm install --frozen-lockfile
pnpm run build
# uv: integration tests invoke the Python emitter helper via
# `uv run` in sdk/py, so uv must be on PATH. Pin matches
# .github/workflows/sdk-py.yml.
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
- name: Vet
working-directory: daemon
run: go vet ./...
- name: Build
working-directory: daemon
# -trimpath mirrors the release build (daemon/.goreleaser.yaml): CI must
# exercise the same flags the attestation depends on (ADR-0031).
run: go build -trimpath ./cmd/...
- name: Test
working-directory: daemon
# Single tagged run covers unit + integration tests; the
# `integration` build tag only adds files (it does not exclude
# untagged ones), so this matches the coverage of running
# `go test ./...` and `go test -tags=integration ./...`
# separately, without re-running the untagged tests.
run: go test -race -tags=integration ./... -v
# Gate A (ADR-0031): the daemon keeps a lean import graph that never reaches
# the operator CLI surface. A dependency edge from cmd/obsigna-daemon into the
# receipt/keys/doctor/disclose *cli trees would drag operator-only code into
# the long-running signing process and break the blast-radius claim the
# topology exists to make. The rule lives in a Go test next to the code
# (cmd/obsigna-daemon/import_guard_test.go) so it is enforced locally too and
# auto-catches any new internal/*cli package; this job just runs it.
import-graph:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: daemon/go.mod
- name: Assert obsigna-daemon excludes the operator CLI surface
working-directory: daemon
run: go test -run '^TestImportGraphExcludesOperatorSurface$' ./cmd/obsigna-daemon -v
# Gate B (ADR-0031): reproducible build. Build obsigna-daemon twice from two
# working-directory paths of different lengths and assert byte-identical
# sha256. This is what proves -trimpath actually took: without it the absolute
# build path is baked into the binary and the two hashes diverge. The release
# workflow extends this to an independent rebuild that must match the published
# artifact (release-obsigna.yml), which is the auditor-facing attestation.
reproducible-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: daemon/go.mod
- name: Build from two different paths, assert identical sha256
run: |
set -euo pipefail
# Export the tracked tree (no .git, no untracked worktrees) into two
# parent dirs whose paths differ in length, then build the daemon in
# each via the shared reproducible-build script (same determinism flags
# the release attestation uses). The workspace (go.work) is exported
# too, so both builds resolve the in-tree sdk/go identically; the only
# difference between them is the absolute build path.
a=/tmp/p1
b=/tmp/path-two-longer-than-one
mkdir -p "$a/repo" "$b/repo"
git archive --format=tar HEAD | tar -x -C "$a/repo"
git archive --format=tar HEAD | tar -x -C "$b/repo"
"$a/repo/scripts/reproducible-build.sh" "$a/repo/daemon" /tmp/daemon-a ./cmd/obsigna-daemon
"$b/repo/scripts/reproducible-build.sh" "$b/repo/daemon" /tmp/daemon-b ./cmd/obsigna-daemon
h1="$(sha256sum /tmp/daemon-a | awk '{print $1}')"
h2="$(sha256sum /tmp/daemon-b | awk '{print $1}')"
echo "path-a sha256=$h1"
echo "path-b sha256=$h2"
if [ "$h1" != "$h2" ]; then
echo "::error::obsigna-daemon is not reproducible across build paths ($h1 vs $h2); -trimpath is not taking effect."
exit 1
fi
echo "ok: byte-identical obsigna-daemon from two different build paths ($h1)"