diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec479a9c1..02745ad2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,8 +10,9 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] go-version: ['1.26'] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -33,96 +34,6 @@ jobs: files: coverage.out continue-on-error: true - build-windows: - # CGO build smoke-test on native Windows. tree-sitter needs a C/C++ - # compiler — the GitHub windows runner ships mingw-w64 on PATH, so no - # extra toolchain setup is required. `go build ./...` compiles every - # production package; the focused agents and sidecar tests also exercise - # Windows-only paths without opting into the unsupported full test suite. - runs-on: windows-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: go.mod - - - name: Build CLI - run: go build -o gortex.exe ./cmd/gortex/ - - - name: Build all packages - run: go build ./... - - - name: Test Windows agent integrations - run: go test -timeout=5m -count=1 ./internal/agents - - # Symlink confinement keeps out-of-repo files out of the index, and it - # reads differently on Windows: a symlink reparse point maps to - # ModeSymlink but a directory junction maps to ModeIrregular, and - # evalSymlinks normalises path case where Unix does not. Verifying it - # only on the linux/macos matrix would leave the platform whose - # semantics differ most as the untested one. - - name: Test symlink confinement - run: go test -timeout=5m -count=1 ./internal/pathguard - - # Windows refuses to unlink an open file, so a leaked sidecar handle - # pins its directory and fails the enclosing t.TempDir() cleanup. That - # asymmetry is invisible on the linux/macos matrix, which is why this - # one test runs here rather than only there. - - name: Test sidecar handle release - run: go test -timeout=5m -count=1 -run TestCloseSidecar ./internal/persistence - - # Same asymmetry, different holder: the plan-lock fixture opens an - # on-disk Store, so a missing Close pins plan_lock.sqlite and fails - # its t.TempDir() cleanup here while passing everywhere else. Every - # assertion in these tests is platform-neutral — only the cleanup - # distinguishes the runners — so nothing but this step can protect - # the fixture's Close from being dropped again. - - name: Test plan-lock fixture handle release - run: > - go test -timeout=10m -count=1 - -run 'PlanLock|PlansLocked|PlansNeverScan' - ./internal/graph/store_sqlite - - # os.UserHomeDir reads %USERPROFILE% here and $HOME everywhere else, so - # a test that isolates itself with HOME alone keeps resolving the real - # profile on Windows — and writes to it. That is invisible to the - # linux/macos matrix by construction, so the isolation helper and the - # path resolver it protects have to be verified on this runner or not - # at all. - - name: Test user-state isolation - run: go test -timeout=5m -count=1 ./internal/testenv ./internal/platform - - # Indexed paths are repoPrefix + '/' + the rest in native separators, so - # on Windows a stored path reads `repo/dir\file.cs`. Every test for that - # shape is a no-op on the linux/macos matrix — POSIX treats '\' as an - # ordinary filename byte, so filepath.Clean and filepath.ToSlash both - # leave the fixture untouched and the assertions pass with or without - # the production fix. This runner is the only one where they can fail. - - name: Test store path separator handling - run: go test -timeout=5m -count=1 ./internal/graphpath - - # The same asymmetry reaches the other direction too: a test that spells - # an expected path with '/' passes on POSIX whatever the code does, and - # only this runner can tell whether the value under test is a native - # filesystem path (filepath.Join / filepath.Clean) or a '/'-keyed store - # path. ParseDiffGitPaths, ParseDiffLinesNewSide and FeedbackDir all - # assert on native paths, so they belong here rather than nowhere. - # The path-domain contracts belong here for the same reason. A graph - # key is "/" plus a native-separator remainder, so GraphKey, - # JoinFileNodes, the prefix-shadow end-to-end pair and RankFileRisk's - # per-domain normalization only diverge once '/' and the native - # separator differ — every one of them passes on the linux/macos - # matrix whatever the code does. internal/review joins the list - # because the file-risk normalizer lives there. - - name: Test native-separator store path comparisons - run: > - go test -timeout=10m -count=1 - -run 'NativeSeparator|MixedSeparator|ImportAdjacency|ParseDiffGitPaths|ParseDiffLinesNewSide|FeedbackDir|ReviewRulepack|PrefixedGraphReportsRulepack|GraphKey|JoinFileNodes|ChangedSymbolsForFiles|PrefixShadow|IdsOnPrefixedGraph|RankFileRisk|PrefixedDeleteAndRename' - ./internal/analysis ./internal/graph/store_sqlite - ./internal/mcp ./internal/resolver ./internal/persistence - ./internal/review - build-linux-static: # The linux release ships a statically linked binary so it runs on any # distro (see .goreleaser.yml). That only holds while the code stays