From 970f1d62f256c9deab0a670ddf65f7effb0d991c Mon Sep 17 00:00:00 2001 From: Jose Alekhinne Date: Mon, 5 Oct 2026 19:03:28 -0700 Subject: [PATCH] build: keep node_modules out of the Go module npm dependencies may ship Go sources: flatted, pulled in by eslint, carries flatted/golang/pkg/flatted/flatted.go. After npm install in editors/vscode, go list ./... reported it as a package of this module, so go build/vet/test compiled it, make lint failed with two govet findings in it, and make test failed in TestGolangciLint. CI never ran npm install in the Go jobs, so only contributors hit it. Add `ignore node_modules` to go.mod. The bare form matches the directory at any depth (./node_modules matches only the root), and removes it from ./... for every go command, golangci-lint included. Also list node_modules in .golangci.yml exclusions.paths, beside vendor/dist/site, so the linter config states the same scope. TestGoModIgnoresNodeModules reads go.mod through go mod edit -json and fails if the directive goes missing. Spec: specs/go-ignore-node-modules.md Signed-off-by: Jose Alekhinne --- .golangci.yml | 1 + go.mod | 2 + internal/compliance/go_mod_ignore_test.go | 58 ++++++++++++++++++++ specs/go-ignore-node-modules.md | 65 +++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 internal/compliance/go_mod_ignore_test.go create mode 100644 specs/go-ignore-node-modules.md diff --git a/.golangci.yml b/.golangci.yml index d0cca7e1e..ac2668c57 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -22,6 +22,7 @@ linters: - vendor - dist - site + - node_modules rules: # Test files use literal strings for clarity - linters: [goconst] diff --git a/go.mod b/go.mod index e175f67d3..fb54c7a04 100644 --- a/go.mod +++ b/go.mod @@ -34,3 +34,5 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect google.golang.org/protobuf v1.36.12 // indirect ) + +ignore node_modules diff --git a/internal/compliance/go_mod_ignore_test.go b/internal/compliance/go_mod_ignore_test.go new file mode 100644 index 000000000..94a5249b0 --- /dev/null +++ b/internal/compliance/go_mod_ignore_test.go @@ -0,0 +1,58 @@ +// / ctx: https://ctx.ist +// ,'`./ do you remember? +// `.,'\ +// \ Copyright 2026-present Context contributors. +// SPDX-License-Identifier: Apache-2.0 + +package compliance + +import ( + "encoding/json" + "os/exec" + "testing" +) + +// TestGoModIgnoresNodeModules verifies that the root go.mod +// carries an `ignore node_modules` directive. +// +// npm packages may ship Go sources (flatted does, under +// flatted/golang/). Without the directive, `./...` matches +// them as packages of this module, so `go build`, `go vet`, +// `go test`, and golangci-lint all compile and lint +// third-party code the moment anyone runs `npm install` in +// editors/vscode, ctx-desktop, or tools/typecheck/*. The +// bare form (no `./` prefix) matches a node_modules +// directory at any depth. +// +// The directive is read through `go mod edit -json` rather +// than by string search, so an ignore block or a reformat +// cannot fool the check. +// +// See specs/go-ignore-node-modules.md. +func TestGoModIgnoresNodeModules(t *testing.T) { + root := projectRoot(t) + + cmd := exec.Command("go", "mod", "edit", "-json") + cmd.Dir = root + out, runErr := cmd.Output() + if runErr != nil { + t.Fatalf("go mod edit -json: %v", runErr) + } + + var mod struct { + Ignore []struct { + Path string + } + } + if parseErr := json.Unmarshal(out, &mod); parseErr != nil { + t.Fatalf("parse go mod edit -json output: %v", parseErr) + } + + for _, ig := range mod.Ignore { + if ig.Path == "node_modules" { + return + } + } + t.Errorf("go.mod is missing `ignore node_modules`; " + + "add it with: go mod edit -ignore=node_modules") +} diff --git a/specs/go-ignore-node-modules.md b/specs/go-ignore-node-modules.md new file mode 100644 index 000000000..da3c673b3 --- /dev/null +++ b/specs/go-ignore-node-modules.md @@ -0,0 +1,65 @@ +# Keep node_modules Out of the Go Module + +The repo holds four npm packages (`editors/vscode`, `ctx-desktop`, +`tools/typecheck/opencode`, `tools/typecheck/pi`) inside the root +Go module. npm dependencies may ship Go sources, and nothing kept +Go tooling from treating them as part of ctx. + +## Problem + +`flatted` (pulled in by eslint through `flat-cache`) ships +`flatted/golang/pkg/flatted/flatted.go`. After `npm install` in +`editors/vscode`: + +- `go list ./...` reports + `github.com/ActiveMemory/ctx/editors/vscode/node_modules/flatted/golang/pkg/flatted` + as a package of this module, so `go build`, `go vet`, and + `go test ./...` compile it. +- `make lint` fails with two `govet` findings inside that file. +- `make test` fails in `TestGolangciLint`, which shells out to + golangci-lint. + +CI never saw it, because the Go jobs don't run `npm install`. Any +contributor who works on the extension and then runs `make lint` +does. + +`./...` skips only directories starting with `.` or `_`, +`testdata`, and nested modules; `node_modules` is none of those. +golangci-lint's `exclusions.paths` only hides reported issues: the +package is still loaded and type-checked. + +## Solution + +1. **`go.mod`: `ignore node_modules`** (Go 1.25+). The bare form, + without a `./` prefix, matches a `node_modules` directory at + any depth (`./node_modules` would match only the root, which + was verified to leave the package in `go list ./...`). This + removes the directory from `./...` for every Go command, + golangci-lint included, since it loads packages through the + go command. +2. **`.golangci.yml`: `node_modules` in `exclusions.paths`**, + beside `vendor`, `dist`, and `site`. With (1) in place this is + belt-and-braces; it states in the linter's own config that + node code is not in scope, and holds if golangci-lint is ever + pointed at an explicit path. +3. **Regression guard:** + `internal/compliance/go_mod_ignore_test.go` reads go.mod via + `go mod edit -json` and fails if the directive is missing. + +The compliance tests' own file walkers (`allGoFiles`, +`allSourceFiles`) already skip `node_modules`; they need no +change. + +## Verification + +With `editors/vscode/node_modules` installed: + +- `go list ./... | grep node_modules` prints nothing. +- `make lint`: 0 issues. `make test`: passes. +- `TestGoModIgnoresNodeModules` passes, and fails after + `go mod edit -dropignore=node_modules`. + +## Non-Goals + +- `tools/ctxctl` is a separate module with no npm package inside + it; its go.mod is unchanged.