name: CI on: push: branches: [main] pull_request: workflow_dispatch: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: GO_VERSION: "1.26" jobs: test: name: Test (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: server/go.sum - name: Verify formatting if: runner.os == 'Linux' working-directory: server run: | unformatted="$(gofmt -l .)" if [ -n "$unformatted" ]; then echo "These files need gofmt:" echo "$unformatted" exit 1 fi - name: Verify go.mod is tidy if: runner.os == 'Linux' working-directory: server run: | go mod tidy git diff --exit-code -- go.mod go.sum - name: Vet working-directory: server run: go vet ./... - name: Test working-directory: server run: go test -count=1 ./... - name: Test with the race detector if: runner.os == 'Linux' working-directory: server run: go test -race -count=1 ./... - name: Build working-directory: server run: go build ./... postgres: name: Storage tests on PostgreSQL runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_USER: warp POSTGRES_PASSWORD: warp POSTGRES_DB: warp_test ports: ["5432:5432"] options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: server/go.sum - name: Storage tests (sqlite + postgres) working-directory: server env: WARP_TEST_POSTGRES_DSN: postgres://warp:warp@localhost:5432/warp_test?sslmode=disable run: go test -count=1 ./internal/storage/... complement: name: Complement (federation off) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} # Complement is a separate Go module with its own dependencies; the # runner script clones it into .complement/. - name: Run the Complement subset for the phases that are done run: bash server/test/complement/run.sh lint: name: golangci-lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: server/go.sum - uses: golangci/golangci-lint-action@v8 with: version: v2.14.0 working-directory: server smoke: name: Smoke test the binary runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: server/go.sum - name: Build run: make build - name: Generate a signing key and boot the server run: | set -euo pipefail sed 's/^ server_name: .*/ server_name: ci.example/' warp.yaml.example > warp.yaml ./bin/warp version ./bin/warp generate-keys ./bin/warp migrate ./bin/warp serve & server_pid=$! trap 'kill "$server_pid" 2>/dev/null || true' EXIT for _ in $(seq 1 30); do if curl -fsS http://127.0.0.1:8008/healthz >/dev/null 2>&1; then break fi sleep 1 done curl -fsS http://127.0.0.1:8008/healthz | tee /dev/stderr | grep -q '"status":"ok"' curl -fsS http://127.0.0.1:8008/_matrix/client/versions | grep -q '"versions"' curl -fsS http://127.0.0.1:8008/.well-known/matrix/client | grep -q 'm.homeserver' curl -fsS http://127.0.0.1:8008/metrics | grep -q warp_build_info # Federation is off by default, so its surface must be absent. status="$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8008/.well-known/matrix/server)" test "$status" = "404" # Registration is off in a fresh install, so the first account is # made from the command line. Everything from here on is the # Phase 1 surface, through the real binary. ./bin/warp create-account --username ci-admin --password hunter2hunter2 --admin token="$(curl -fsS http://127.0.0.1:8008/_matrix/client/v3/login -d '{ "type": "m.login.password", "identifier": {"type": "m.id.user", "user": "ci-admin"}, "password": "hunter2hunter2" }' | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')" test -n "$token" curl -fsS http://127.0.0.1:8008/_matrix/client/v3/account/whoami \ -H "Authorization: Bearer $token" | grep -q '@ci-admin:ci.example' curl -fsS http://127.0.0.1:8008/_matrix/client/v3/capabilities \ -H "Authorization: Bearer $token" | grep -q 'm.room_versions' curl -fsS http://127.0.0.1:8008/_warp/admin/v1/stats \ -H "Authorization: Bearer $token" | grep -q '"admins":1' # An unauthenticated request must be refused, not served. status="$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8008/_warp/admin/v1/stats)" test "$status" = "401" # Registration is refused while the switch is off, and accepted # once the admin API flips it — with no restart. status="$(curl -s -o /dev/null -w '%{http_code}' -X POST \ http://127.0.0.1:8008/_matrix/client/v3/register -d '{}')" test "$status" = "403" curl -fsS -X PUT http://127.0.0.1:8008/_warp/admin/v1/registration \ -H "Authorization: Bearer $token" -d '{"enabled":true}' | grep -q '"enabled":true' status="$(curl -s -o /dev/null -w '%{http_code}' -X POST \ http://127.0.0.1:8008/_matrix/client/v3/register -d '{}')" test "$status" = "401" # Graceful shutdown: SIGTERM must stop the process cleanly. kill -TERM "$server_pid" wait "$server_pid" admin: name: Admin panel runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: pnpm - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: server/go.sum - name: Install run: pnpm install --frozen-lockfile - name: Typecheck and build the frontend run: | pnpm -r typecheck pnpm --filter @warp/admin build - name: Verify formatting working-directory: apps/admin run: | unformatted="$(gofmt -l .)" if [ -n "$unformatted" ]; then echo "These files need gofmt:" echo "$unformatted" exit 1 fi - name: Vet and test warp-admin working-directory: apps/admin run: | go vet ./... go test -count=1 ./... - name: Smoke test warp-admin against the homeserver run: | set -euo pipefail make build (cd apps/admin && go build -o ../../bin/warp-admin ./cmd/warp-admin) sed 's/^ server_name: .*/ server_name: ci.example/' warp.yaml.example > warp.yaml ./bin/warp generate-keys ./bin/warp migrate ./bin/warp create-account --username ci-admin --password hunter2hunter2 --admin ./bin/warp serve & ./bin/warp-admin -homeserver http://127.0.0.1:8008 -listen 127.0.0.1:8009 & trap 'kill $(jobs -p) 2>/dev/null || true' EXIT for _ in $(seq 1 30); do if curl -fsS http://127.0.0.1:8008/healthz >/dev/null 2>&1 && curl -fsS http://127.0.0.1:8009/healthz >/dev/null 2>&1; then break fi sleep 1 done # The panel is embedded, and its routes fall back to index.html. curl -fsS http://127.0.0.1:8009/ | grep -q '