Skip to content

Add .NET bindings - #475

Open
grinidx wants to merge 5 commits into
scip-code:mainfrom
dbhq-uk:dotnet-bindings
Open

Add .NET bindings#475
grinidx wants to merge 5 commits into
scip-code:mainfrom
dbhq-uk:dotnet-bindings

Conversation

@grinidx

@grinidx grinidx commented Aug 17, 2026

Copy link
Copy Markdown

Adds .NET bindings, as discussed in #468.

bindings/ had go, haskell, java, kotlin, rust and typescript, so anyone writing a .NET SCIP producer or consumer vendors scip.proto by hand today. Sourcegraph's own scip-dotnet does, and so does the tool this came out of.

The three things asked for

The bindings. Package id Scip, targeting netstandard2.0. Generated by a new protoc_builtin: csharp entry in buf.gen.yaml, alongside the existing java and kotlin ones. The generated file is committed and marked linguist-generated, matching what the other bindings do; CI regenerates and runs git diff --exit-code, so it cannot drift.

netstandard2.0 because the bindings are pure generated data types with no framework-specific surface, so it costs nothing and reaches .NET Framework 4.6.1 and above, Mono and Unity, and every modern .NET. Google.Protobuf ships a netstandard2.0 assembly.

Nix. A dotnet-bindings check in checks.nix in the same shape as go-bindings, plus what the devShell needs.

$ nix flake check
all checks passed!

$ nix build .#checks.x86_64-linux.dotnet-bindings
share/nuget/source/scip/0.9.0/scip.0.9.0.nupkg

The formatting check still passes with these files in place.

Release automation. A publish-dotnet-bindings job in release.yaml in the shape of the existing per-binding publish jobs, plus a dotnet-bindings.yaml PR check modelled on jvm-bindings.yaml, including its validation that the version matches cmd/scip/version.txt.

One wrinkle worth knowing about

protoc's C# backend emits a Descriptor class that does not compile: CS0542, because every generated protobuf type already carries a static Descriptor property. So proto-generate renames the class to SymbolDescriptor.

The message itself is untouched, and SymbolDescriptor.Descriptor.FullName still returns scip.Descriptor, so nothing about the wire format or the schema changes. It is also what sourcegraph/scip-dotnet already ships, so anyone moving between the two sees the same name.

Verified against a real index

Rather than only checking it compiles, a console app consuming the packed Scip.0.9.0.nupkg read a real scip-typescript 0.4.0 index of 60,867 bytes:

tool         scip-typescript 0.4.0
projectRoot  file:///.../ScentVerdict.Mobile
documents    4
occurrences  702
symbols      161

Re-encoding produces a byte-identical 60,867 bytes, and the reparsed index .Equals(original).

Needs something at your end

A NUGET_API_KEY Actions secret, scoped to "Push new packages and package versions". The Scip id is unregistered, so the first release creates it - worth claiming from an org account rather than a personal one if you have a preference.

Nothing else: no nix, runner or branch-protection changes.

One maintenance note for whoever does the next release: bindings/dotnet/deps.json must be regenerated whenever Google.Protobuf moves, in lock-step with flake.nix's protoc. The command is documented in checks.nix, and renovate is pinned off that dependency so it cannot bump it silently.

Adds a `protoc_builtin: csharp` entry to buf.gen.yaml alongside java and
kotlin, the generated `bindings/dotnet/src/Scip.cs`, and a project that
packs to the `Scip` NuGet package.

protoc's C# backend emits an uncompilable `Descriptor` class (CS0542), so
proto-generate renames the generated class to `SymbolDescriptor`. The
Protobuf message itself is untouched.
Builds and packs bindings/dotnet with `buildDotnetModule`, pinned to the
.NET 10 SDK, and asserts the project version matches cmd/scip/version.txt
in the same style as the Rust, Haskell and TypeScript checks.
The PR check builds bindings/dotnet with a stock SDK and validates the
package version against cmd/scip/version.txt, as the JVM bindings check
does. The release workflow packs and pushes the Scip package to NuGet
through `nix develop`, like the crate and Hackage jobs.
Adds bindings/dotnet/Scip.csproj to the list of files a version bump
touches, and a NuGet publishing section alongside the JVM one.
`Scip.Index` is ambiguous with `System.Index` under implicit usings, so the
README example aliases it. Google.Protobuf joins protobuf-java in the
renovate rule that keeps the runtime in lock-step with flake.nix's protoc.
@grinidx

grinidx commented Aug 17, 2026

Copy link
Copy Markdown
Author

One practical note on the NUGET_API_KEY, which I should have thought about when I opened this.

Scip is currently unregistered. NuGet ids are claimed on first publish and there is no way to reserve one without pushing to it, so the name stays open until whoever owns the key runs a release. Prefix reservation is a separate process and applies to prefixes rather than bare names.

The risk is low - SCIP has existed four years and nobody has taken it - but it is not zero, and this could sit for a while.

Happy to do whichever suits you:

  • You claim it. The sourcegraph account already publishes scip-dotnet and scip-excel, so that is probably a two minute job whenever you want it, and it is the obvious home.
  • We publish 0.9.0 from ours and transfer ownership to you. Only if that is genuinely more convenient, and I would rather ask than assume, because publishing first would quietly settle a decision that is yours - including whether the id should be Scip at all rather than something prefixed.

Default assumption is the first. Nothing here blocks review of the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant