Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ before:

builds:
- env:
- CGO_ENABLED=0
- CGO_ENABLED=1
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
goos:
- linux
- windows
- darwin
main: ./cmd/github-mcp-server
Comment on lines 8 to 16
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR enables CGO in release builds and removes Windows from GoReleaser targets, but the repo CI still runs script/test and go build on windows-latest. Since go-tree-sitter requires CGO (and Windows runners typically lack a GCC/Clang toolchain by default), the Windows CI job will likely fail. Consider either (1) adding build tags + a !cgo stub that falls back to unified diffs, so non-CGO builds still compile, or (2) updating CI to drop Windows or install a working CGO toolchain there.

Copilot uses AI. Check for mistakes.

Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ ARG VERSION="dev"
# Set the working directory
WORKDIR /build

# Install git
# Install git and C compiler for CGO (tree-sitter)
RUN --mount=type=cache,target=/var/cache/apk \
apk add git
apk add git gcc musl-dev

# Build the server
# go build automatically download required module dependencies to /go/pkg/mod
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=bind,target=. \
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION} -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
CGO_ENABLED=1 go build -ldflags="-s -w -linkmode external -extldflags '-static' -X main.version=${VERSION} -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-o /bin/github-mcp-server ./cmd/github-mcp-server

# Make a stage to run the app
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ require (
github.com/stretchr/testify v1.11.1
)

require github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82

Comment on lines 14 to +17
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go.mod is not in the usual go mod tidy canonical form: the new direct dependency is in its own single-line require block. Since CI runs go mod tidy -diff, this is likely to fail. Please re-run go mod tidy so the dependency is merged into the main direct-requires block and any indirects are updated accordingly.

Suggested change
)
require github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82
)

Copilot uses AI. Check for mistakes.
require (
Comment on lines 14 to 18
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new third-party dependency is introduced here. This repo’s CI includes script/licenses-check, which regenerates and diffs third-party-licenses.*.md and third-party/; without committing the regenerated outputs, the license-check workflow will fail. Please run ./script/licenses and commit any updated license artifacts alongside the dependency change.

Copilot uses AI. Check for mistakes.
github.com/aymerick/douceur v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkv
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 h1:6C8qej6f1bStuePVkLSFxoU22XBS165D3klxlzRg8F4=
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82/go.mod h1:xe4pgH49k4SsmkQq5OT8abwhWmnzkhpgnXeekbx2efw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
Expand Down
6 changes: 3 additions & 3 deletions pkg/github/compare_file_contents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func Test_CompareFileContents(t *testing.T) {
expectDiff: `host: "localhost" → "production.db"`,
},
{
name: "unsupported format falls back to unified diff",
name: "Go file uses structural diff",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
GetReposContentsByOwnerByRepoByPath: mockContentsForRef(map[string]string{
"main": "func main() {}\n",
Expand All @@ -128,8 +128,8 @@ func Test_CompareFileContents(t *testing.T) {
"base": "main",
"head": "feature",
},
expectFormat: "unified",
expectDiff: "--- a/main.go",
expectFormat: "structural",
expectDiff: "function_declaration main: modified",
},
{
name: "missing required parameter - owner",
Expand Down
7 changes: 7 additions & 0 deletions pkg/github/semantic_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func SemanticDiff(path string, base, head []byte) SemanticDiffResult {
case ".toml":
return semanticDiffTOML(path, base, head)
default:
// Try tree-sitter structural diff for code files
if languageForPath(path) != nil {
return structuralDiff(path, base, head)
}
return SemanticDiffResult{
Format: DiffFormatUnified,
Diff: unifiedDiff(path, base, head),
Expand Down Expand Up @@ -542,6 +546,9 @@ func DetectDiffFormat(path string) DiffFormat {
case ".toml":
return DiffFormatTOML
default:
if languageForPath(path) != nil {
return DiffFormatStructural
}
return DiffFormatUnified
}
}
43 changes: 12 additions & 31 deletions pkg/github/semantic_diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,36 +302,17 @@ func TestSemanticDiffTOML(t *testing.T) {
}

func TestSemanticDiffUnifiedFallback(t *testing.T) {
tests := []struct {
name string
path string
base string
head string
expectedDiff string
}{
{
name: "unsupported extension uses unified diff",
path: "main.go",
base: "func main() {\n}\n",
head: "func main() {\n\tfmt.Println(\"hello\")\n}\n",
expectedDiff: "--- a/main.go",
},
{
name: "no extension uses unified diff",
path: "Makefile",
base: "all:\n\techo hello\n",
head: "all:\n\techo world\n",
expectedDiff: "--- a/Makefile",
},
}
t.Run("Go file uses structural diff", func(t *testing.T) {
result := SemanticDiff("main.go", []byte("func main() {\n}\n"), []byte("func main() {\n\tfmt.Println(\"hello\")\n}\n"))
assert.Equal(t, DiffFormatStructural, result.Format)
assert.Contains(t, result.Diff, "function_declaration main: modified")
})

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := SemanticDiff(tc.path, []byte(tc.base), []byte(tc.head))
assert.Equal(t, DiffFormatUnified, result.Format)
assert.Contains(t, result.Diff, tc.expectedDiff)
})
}
t.Run("no extension uses unified diff", func(t *testing.T) {
result := SemanticDiff("Makefile", []byte("all:\n\techo hello\n"), []byte("all:\n\techo world\n"))
assert.Equal(t, DiffFormatUnified, result.Format)
assert.Contains(t, result.Diff, "--- a/Makefile")
})
}

func TestSemanticDiffFileSizeLimit(t *testing.T) {
Expand Down Expand Up @@ -373,7 +354,7 @@ func TestSemanticDiffNewAndDeletedFiles(t *testing.T) {

t.Run("deleted Go file", func(t *testing.T) {
result := SemanticDiff("main.go", []byte("package main\n"), nil)
assert.Equal(t, DiffFormatUnified, result.Format)
assert.Equal(t, DiffFormatStructural, result.Format)
assert.Equal(t, "file deleted", result.Diff)
})

Expand All @@ -394,7 +375,7 @@ func TestDetectDiffFormat(t *testing.T) {
{"config.yml", DiffFormatYAML},
{"data.csv", DiffFormatCSV},
{"config.toml", DiffFormatTOML},
{"main.go", DiffFormatUnified},
{"main.go", DiffFormatStructural},
{"README.md", DiffFormatUnified},
{"Makefile", DiffFormatUnified},
}
Expand Down
Loading