Skip to content

Build and Test

Build and Test #146

Workflow file for this run

name: Build and Test
on:
push:
branches:
- dev
- stage
- main
- release**
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
schedule:
- cron: "0 1 * * *" # 02:00 UTC+1
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-{1}', github.workflow, github.event.pull_request.number) || format('{0}-push-{1}-{2}', github.workflow, github.ref, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
# ---------------------------------------------------------------------------
# Build, lint and test the standalone UI bundle. Independent of the Go
# jobs — the Go binary is UI-blind, so this exists to catch UI build
# failures on PRs and to feed the standalone-UI Docker image build.
# ---------------------------------------------------------------------------
job_ui:
name: UI bundle
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"
cache-dependency-path: |
sdk/pnpm-lock.yaml
ui/pnpm-lock.yaml
- name: Install and build SDK
working-directory: sdk
run: pnpm install --frozen-lockfile && pnpm build
- name: Install UI dependencies
working-directory: ui
run: pnpm install --frozen-lockfile
- name: Type-check & lint
working-directory: ui
run: pnpm run lint
- name: Run UI tests
working-directory: ui
run: pnpm run test
- name: Build UI
working-directory: ui
run: pnpm run build
env:
VITE_BUILD_VERSION: ${{ github.sha }}
# ---------------------------------------------------------------------------
# Static analysis and formatting checks
# ---------------------------------------------------------------------------
job_go_checks:
name: Go Checks
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Run go mod tidy
run: |
go mod tidy
if [[ $(git status --porcelain) ]]; then
git diff
echo
echo "go mod tidy made changes — run 'go mod tidy' and commit the result"
exit 1
fi
- name: Run go vet
run: go vet ./...
- name: Run gofumpt
run: |
go install mvdan.cc/gofumpt@latest
diff=$(gofumpt -l .)
if [[ -n "$diff" ]]; then
echo "::error::gofumpt found unformatted files:"
echo "$diff"
exit 1
fi
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v8
with:
version: v2.5
# ---------------------------------------------------------------------------
# Solidity contract tests
# ---------------------------------------------------------------------------
job_forge_test:
name: Forge Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
- name: Run forge build
working-directory: solidity
run: forge build
- name: Run forge test
working-directory: solidity
run: forge test -vvv
# ---------------------------------------------------------------------------
# Go unit tests (no chain, no proofs — fast path). Runs on self-hosted
# to match davinci-node's pattern: Go tests in this project exercise
# Groth16 trusted-setup code paths whose performance on GitHub-hosted
# runners is 5–10× slower than on our dedicated boxes.
# ---------------------------------------------------------------------------
job_go_unit_test:
name: Go Unit Tests
runs-on: [self-hosted]
env:
LOG_LEVEL: debug
RUN_INTEGRATION_TESTS: "false"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Build all binaries
run: go build ./cmd/...
- name: Run unit tests
run: |
go test -v \
$(go list ./... | grep -v github.com/vocdoni/davinci-dkg/tests) \
-timeout=30m \
-failfast
# ---------------------------------------------------------------------------
# Go integration tests (Anvil + Docker Compose deployer). Self-hosted:
# the pre-step regenerates all six Groth16 circuits via a trusted setup
# (MaxN=16 contribution circuit alone ≈ 90 s), so a cold GitHub-hosted
# runner would easily triple the job duration.
# ---------------------------------------------------------------------------
job_go_integration_test:
name: Go Integration Tests
runs-on: [self-hosted]
# Serialize across the whole repo on the shared self-hosted runner.
# Prevents two concurrent jobs (from different PRs / branches) from
# racing through `foundry-rs/foundry-toolchain@v1`: foundryup aborts
# with "'anvil' is currently running" when another job's in-flight
# anvil process (host- or container-visible) is still alive.
# cancel-in-progress must stay false so a queued job waits instead of
# cancelling the one that got there first.
concurrency:
group: self-hosted-integration
cancel-in-progress: false
env:
LOG_LEVEL: debug
RUN_INTEGRATION_TESTS: "true"
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Install Foundry (for deployer container build)
uses: foundry-rs/foundry-toolchain@v1
# Groth16 trusted setup is randomized: every invocation of
# `circuit-compile` produces a fresh proving key and verifying key
# with a new pair of hashes. The committed config/circuit_artifacts.go
# and solidity/src/verifiers/*_vkey.sol files are snapshots of one
# past setup run whose artifacts no CI runner has on disk. If we just
# run the test with the committed hashes, the Go prover falls back to
# a local setup whose vk does not match the deployed Solidity
# verifier, and every submitContribution reverts with
# ProofInvalid() (selector 0x7fcdd1f4).
#
# The fix is to regenerate *both* halves together in the same job:
# circuits-compile writes pk/vk to ~/.davinci/artifacts and rewrites
# solidity/src/verifiers/*_vkey.sol; circuits-update-hashes patches
# config/circuit_artifacts.go and the PROVING_KEY_HASH constants in
# the verifier wrappers; solidity-build recompiles the contracts with
# the fresh vkey. The integration test then sees a self-consistent
# triple (Go pk ↔ pinned hashes ↔ Solidity verifier vk) all coming
# from the same trusted setup run.
#
# Cost: ~3–5 minutes per run (dominated by contribution + finalize
# trusted setup at MaxN=16). Caching is not worthwhile — Setup is
# non-deterministic so each run's output is fresh regardless.
- name: Regenerate circuit artifacts and Solidity verifiers
run: make circuits-compile circuits-update-hashes solidity-build
- name: Run integration tests
if: github.event.pull_request.draft != true
run: |
go test -v ./tests/... \
-timeout=2h \
-failfast \
-count=1
# ── TypeScript SDK integration tests ─────────────────────────────────
# Runs after Go tests so that:
# • Circuit artifacts are already compiled in ~/.davinci/artifacts
# (the sdk-test-fixture binary uses them to generate ZK proofs).
# • The Go binary in cmd/sdk-test-fixture is buildable without a
# separate circuit-compile step.
# The SDK tests start their own isolated Docker Compose stack (fresh
# Anvil + deployer) so they do not interfere with the Go test stack.
- name: Set up pnpm
if: github.event.pull_request.draft != true
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Node.js
if: github.event.pull_request.draft != true
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"
cache-dependency-path: sdk/pnpm-lock.yaml
- name: Install SDK dependencies
if: github.event.pull_request.draft != true
working-directory: sdk
run: pnpm install --frozen-lockfile
- name: Run SDK integration tests
if: github.event.pull_request.draft != true
working-directory: sdk
run: pnpm test:integration
env:
RUN_INTEGRATION_TESTS: "true"
# ---------------------------------------------------------------------------
# Docker image build (calls reusable workflow). Only runs after all tests
# are green, and only pushes on branch pushes (not PRs).
# ---------------------------------------------------------------------------
call-docker-build:
name: Docker
needs:
- job_go_checks
- job_forge_test
- job_go_unit_test
- job_go_integration_test
permissions:
contents: read
packages: write
if: github.event.pull_request.draft != true
uses: ./.github/workflows/docker-build.yml
secrets: inherit
with:
image-tag: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}
push: ${{ github.event_name == 'push' }}
# ---------------------------------------------------------------------------
# Standalone UI image build. Independent of the node image — gated only on
# the UI bundle building cleanly, since the runtime image bundles its own
# nginx and doesn't share Go test infrastructure.
# ---------------------------------------------------------------------------
call-ui-docker-build:
name: UI Docker
needs:
- job_ui
permissions:
contents: read
packages: write
if: github.event.pull_request.draft != true
uses: ./.github/workflows/ui-docker-build.yml
secrets: inherit
with:
image-tag: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}
push: ${{ github.event_name == 'push' }}