ci: one cross-build runner per target, one target per OS on PRs (#951) #647
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Windows | |
| # All Windows CI for the repo. test.yaml covers Linux/macOS Go tests and the | |
| # `e2e` job in session-e2e.yaml covers Linux/macOS session e2e; Windows is here. | |
| # | |
| # pkg/apm/webrtc's C++ uses MSVC SEH (__try/__except) that the runner's mingw GCC | |
| # can't compile, and on native Windows the cgo link of ~560 webrtc/portaudio | |
| # objects overflows the ~32KB command-line limit. So -- exactly like | |
| # .goreleaser.yaml -- everything is cross-compiled on Linux with zig (clang) in a | |
| # single `cross-build` job, then run natively on windows-latest. Both consumers | |
| # (the unit-test run and the session e2e run) depend on that one build, so the | |
| # expensive cgo compile happens once. | |
| on: | |
| push: | |
| branches: ['**'] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: windows-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Cross-compile lk.exe and a test binary per package on Linux with zig. | |
| # manifest.tsv maps each test binary to its package directory so the unit-test | |
| # job can run it from there (tests read cwd-relative fixtures, e.g. | |
| # cmd/lk/testdata and pkg/agentfs/examples). | |
| cross-build: | |
| runs-on: ubuntu-latest | |
| name: Cross-build Windows artifacts (zig) | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout livekit-cli | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| # pkg/portaudio/pa_src is a submodule with the vendored PortAudio C | |
| # source linked into cmd/lk via cgo. | |
| submodules: true | |
| - name: Set up Zig | |
| uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2 | |
| with: | |
| version: 0.14.1 | |
| - name: Set up Go | |
| uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 | |
| with: | |
| go-version-file: go.mod | |
| # setup-go's default key carries no workflow or job component, so this | |
| # job and test.yaml generate the same one. Test finishes first and | |
| # claims it; the save here then fails with "Unable to reserve cache", | |
| # leaving this job to restore native-gcc objects no zig target can use. | |
| cache: false | |
| - name: Cache Go modules and build cache | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: go-${{ runner.os }}-windows-cross-${{ hashFiles('go.sum') }} | |
| # A go.sum bump leaves the webrtc/portaudio C++ objects — the | |
| # expensive part — valid, so fall back to any earlier build. | |
| restore-keys: | | |
| go-${{ runner.os }}-windows-cross- | |
| # Generate the MinGW import libs lld needs for Go's /DEFAULTLIB references | |
| # (dbghelp, bcrypt, ...), exactly as goreleaser's before-hook does. | |
| - name: Generate MinGW import libs | |
| run: scripts/setup-cross.sh windows/amd64 | |
| - name: Cross-compile lk.exe and per-package test binaries | |
| env: | |
| GOOS: windows | |
| GOARCH: amd64 | |
| CGO_ENABLED: "1" | |
| # zig cc/c++ as the cgo toolchain, matching .goreleaser.yaml's Windows | |
| # build. -fms-extensions (SEH) isn't on cgo's allowlist; -fno-sanitize | |
| # is a zig UBSan-under-lld workaround -- both copied from goreleaser. | |
| CC: zig cc -target x86_64-windows-gnu | |
| CXX: zig c++ -target x86_64-windows-gnu | |
| CGO_CXXFLAGS_ALLOW: -fms-extensions | |
| CGO_CXXFLAGS: -fno-sanitize=all | |
| run: | | |
| set -euo pipefail | |
| ldflags="-extldflags=-L$PWD/.cross/windows_amd64/mingw_lib" | |
| module=$(go list -m) | |
| mkdir -p dist | |
| go build -ldflags="$ldflags" -o dist/lk.exe ./cmd/lk | |
| : > dist/manifest.tsv | |
| for pkg in $(go list -f '{{if or .TestGoFiles .XTestGoFiles}}{{.ImportPath}}{{end}}' ./...); do | |
| rel=${pkg#"$module"/} | |
| [ "$rel" = "$module" ] && rel="." | |
| safe=$(echo "$rel" | tr '/' '_') | |
| go test -c -ldflags="$ldflags" -o "dist/${safe}.test.exe" "$pkg" | |
| printf '%s\t%s\n' "${safe}.test.exe" "$rel" >> dist/manifest.tsv | |
| done | |
| echo "Built lk.exe and:"; cat dist/manifest.tsv | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: windows-artifacts | |
| path: dist | |
| retention-days: 1 | |
| if-no-files-found: error | |
| # Run every package's test binary natively, from its package directory so | |
| # cwd-relative fixtures resolve. No secrets: TestSessionE2E (in the cmd/lk | |
| # binary) skips without LIVEKIT_API_KEY and is exercised by windows-session-e2e | |
| # below. node ships on the runner; uv is installed for the Python SDK tests. | |
| windows-tests: | |
| needs: cross-build | |
| runs-on: windows-latest | |
| name: Go test on windows-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout livekit-cli | |
| # Test fixtures (cmd/lk/testdata, pkg/agentfs/examples) are read at | |
| # runtime; submodules are C source already compiled into the binaries. | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Download Windows artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: windows-artifacts | |
| path: dist | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 | |
| with: | |
| python-version: "3.12" | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| with: | |
| enable-cache: true | |
| - name: Run test binaries | |
| shell: bash | |
| run: | | |
| set -uo pipefail | |
| root=$(pwd) | |
| fail=0 | |
| while IFS=$'\t' read -r exe rel; do | |
| echo "::group::$rel" | |
| ( cd "$rel" && "$root/dist/$exe" -test.v -test.count=1 ) || fail=1 | |
| echo "::endgroup::" | |
| done < "$root/dist/manifest.tsv" | |
| exit $fail | |
| # Drive the real session lifecycle on Windows against the prebuilt lk.exe. | |
| # Needs live LiveKit credentials, so skip on pull_request (forks can't read | |
| # secrets); the cmd/lk test binary already exists from cross-build. | |
| windows-session-e2e: | |
| needs: cross-build | |
| if: github.event_name != 'pull_request' | |
| runs-on: windows-latest | |
| name: lk agent with ${{ matrix.lang }} agent on windows-latest | |
| # Same `lang` matrix as session-e2e.yaml's e2e job: Python and Node echo | |
| # agents. The cross-built binaries are language-agnostic, so one build | |
| # feeds both arms. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| lang: [python, node] | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout livekit-cli | |
| # Needed for the echo-agent fixtures; submodules are compiled into lk.exe. | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Download Windows artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: windows-artifacts | |
| path: dist | |
| - name: Set up Python | |
| if: matrix.lang == 'python' | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 | |
| with: | |
| python-version: "3.12" | |
| - name: Set up uv | |
| if: matrix.lang == 'python' | |
| uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| with: | |
| enable-cache: true | |
| - name: Sync echo agent deps | |
| if: matrix.lang == 'python' | |
| working-directory: cmd/lk/testdata/echo-agent | |
| run: uv sync | |
| - name: Set up Node | |
| if: matrix.lang == 'node' | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| # --experimental-strip-types (used to run the .ts entrypoint) needs >= 22.6. | |
| node-version: "24" | |
| - name: Install Node echo agent deps | |
| if: matrix.lang == 'node' | |
| working-directory: cmd/lk/testdata/echo-agent-node | |
| run: npm install | |
| - name: Run session e2e | |
| # Run from cmd/lk so the test's relative testdata/ entrypoint paths | |
| # resolve; point it at the cross-built lk.exe so nothing rebuilds. | |
| shell: bash | |
| working-directory: cmd/lk | |
| env: | |
| LK_SESSION_E2E_BIN: ../../dist/lk.exe | |
| LIVEKIT_API_KEY: ${{ secrets.LIVEKIT_API_KEY }} | |
| LIVEKIT_API_SECRET: ${{ secrets.LIVEKIT_API_SECRET }} | |
| LIVEKIT_URL: ${{ secrets.LIVEKIT_URL }} | |
| # Default (unset) targets the Python fixture; point at the Node one for that arm. | |
| LK_SESSION_E2E_AGENT: ${{ matrix.lang == 'node' && 'testdata/echo-agent-node/agent.ts' || '' }} | |
| run: | | |
| ../../dist/cmd_lk.test.exe \ | |
| -test.run TestSessionE2E -test.count=1 -test.v -test.timeout 600s |