NuGet client library and CLI for Go with protocol parity to the official .NET NuGet.Client.
Library: 77% complete (68/88 milestones)
CLI: Command restructure complete (noun-first hierarchy with package, source, config, restore, and version namespaces)
Interop tests passing against NuGet.Client for feature parity validation.
Core Operations
- Package search, metadata retrieval, version resolution
- Package download with content verification
- Full dependency graph resolution with conflict detection
- Transitive dependency resolution with parallel processing
- Solution file support (.sln, .slnx, .slnf) for multi-project operations
Version System
- SemVer 2.0 parsing and comparison (<20ns/op)
- Legacy 4-part version support (Major.Minor.Build.Revision)
- Version range evaluation with floating version support
Framework Support
- Target Framework Moniker (TFM) parsing and compatibility checking
- Framework-specific dependency selection
- Portable Class Library (PCL) profile mapping
Protocol Implementation
- NuGet V3 (service index, registration, search, download, autocomplete)
- NuGet V2 (OData feeds with XML/Atom parsing)
- Automatic protocol detection
- Multi-source repository management
Package Operations
- Package reading from .nupkg files (ZIP + nuspec parsing)
- Package creation with OPC (Open Packaging Conventions) compliance
- PKCS#7 signature creation, verification, and RFC 3161 timestamping
- Asset selection with Runtime Identifier (RID) resolution
Infrastructure
- Multi-tier caching (LRU memory + disk persistence with ETag validation)
- HTTP/2 and HTTP/3 support with automatic fallback
- Retry logic with exponential backoff and Retry-After header support
- Circuit breaker pattern for fault tolerance
- Per-source rate limiting with token bucket algorithm
Observability
- OpenTelemetry tracing with distributed context propagation
- Prometheus metrics for operation monitoring
- Structured logging via mtlog integration
Authentication
- API key authentication (X-NuGet-ApiKey header)
- Bearer token authentication
- HTTP basic authentication
go get github.com/willibrandon/gonugetgit clone https://github.com/willibrandon/gonuget
cd gonuget
make build
./gonuget --versionSee cmd/gonuget/README.md for CLI documentation.
# Add a package source
gonuget source add https://api.nuget.org/v3/index.json --name "NuGet.org"
# List configured sources
gonuget source list
# Add a package to a project
gonuget package add Newtonsoft.Json --project MyProject.csproj --version 13.0.3
# List packages from a solution file
gonuget package list --project MySolution.sln
# List packages (auto-detects project or solution in current directory)
gonuget package list
# Search for packages
gonuget package search Newtonsoft --format json
# Get configuration value
gonuget config get repositoryPath
# Enable shell completion (bash, zsh, powershell)
gonuget completion bash > /etc/bash_completion.d/gonugetCommand Structure: Noun-first hierarchy matching dotnet CLI (e.g., gonuget package add, gonuget source list)
Solution File Support:
- Lists packages from all projects in
.sln,.slnx, and.slnffiles - Auto-detects solution files in the current directory
- Cross-platform path handling (Windows/Unix compatibility)
- UTF-8 with/without BOM support
- Shows warnings for missing project files
Performance: 15-17x faster than dotnet nuget for CLI operations
package main
import (
"context"
"fmt"
"log"
"github.com/willibrandon/gonuget/core"
"github.com/willibrandon/gonuget/http"
)
func main() {
// Create HTTP client
httpClient := http.NewClient(nil)
// Create repository manager
repoManager := core.NewRepositoryManager()
// Add NuGet.org as source
repo := core.NewSourceRepository(core.RepositoryConfig{
Name: "nuget.org",
SourceURL: "https://api.nuget.org/v3/index.json",
HTTPClient: httpClient,
})
repoManager.AddRepository(repo)
// Create client
client := core.NewClient(core.ClientConfig{
RepositoryManager: repoManager,
})
// Search for packages
ctx := context.Background()
results, err := client.SearchPackages(ctx, "newtonsoft", core.SearchOptions{
Take: 10,
IncludePrerelease: false,
})
if err != nil {
log.Fatal(err)
}
for repoName, pkgs := range results {
fmt.Printf("Repository: %s\n", repoName)
for _, pkg := range pkgs {
fmt.Printf(" %s %s\n", pkg.ID, pkg.Version)
}
}
}import "github.com/willibrandon/gonuget/version"
v1, _ := version.Parse("1.2.3-beta.1")
v2, _ := version.Parse("1.2.3")
if v1.Compare(v2) < 0 {
fmt.Println("v1 is less than v2")
}
// Version range evaluation
vr, _ := version.ParseVersionRange("[1.0.0,2.0.0)")
if vr.IsSatisfiedBy(v2) {
fmt.Println("v2 satisfies range")
}import (
"github.com/willibrandon/gonuget/core/resolver"
"github.com/willibrandon/gonuget/protocol/v3"
)
// Create metadata client
httpClient := http.NewClient(nil)
serviceIndexClient := v3.NewServiceIndexClient(httpClient)
metadataClient := v3.NewMetadataClient(httpClient, serviceIndexClient)
// Create resolver
res := resolver.NewResolver(
metadataClient,
[]string{"https://api.nuget.org/v3/index.json"},
"net8.0",
)
// Resolve dependencies
result, err := res.Resolve(ctx, "Newtonsoft.Json", "[13.0.1]")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Resolved %d packages\n", len(result.Packages))
for _, pkg := range result.Packages {
fmt.Printf(" %s %s\n", pkg.ID, pkg.Version)
}import "github.com/willibrandon/gonuget/packaging"
reader, err := packaging.OpenPackageReader("package.nupkg")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
nuspec := reader.GetNuspec()
fmt.Printf("Package: %s %s\n", nuspec.Metadata.ID, nuspec.Metadata.Version)
// List files
files := reader.GetFiles()
for _, f := range files {
fmt.Printf(" %s (%d bytes)\n", f, reader.GetEntry(f).UncompressedSize64)
}import (
"github.com/willibrandon/gonuget/packaging"
"github.com/willibrandon/gonuget/version"
)
builder := packaging.NewPackageBuilder()
ver, _ := version.Parse("1.0.0")
builder.
SetID("MyPackage").
SetVersion(ver).
SetDescription("My package description").
SetAuthors("Author Name").
AddFile("lib/net8.0/MyLibrary.dll", "MyLibrary.dll")
if err := builder.Save("MyPackage.1.0.0.nupkg"); err != nil {
log.Fatal(err)
}import "github.com/willibrandon/gonuget/frameworks"
net80, _ := frameworks.Parse("net8.0")
net70, _ := frameworks.Parse("net7.0")
netstandard20, _ := frameworks.Parse("netstandard2.0")
// Check compatibility
if net80.IsCompatibleWith(netstandard20) {
fmt.Println("net8.0 apps can use netstandard2.0 packages")
}
// Get portable framework name
fmt.Println(net80.GetShortFolderName()) // "net8.0"import (
"github.com/willibrandon/gonuget/cache"
"time"
)
// Create memory cache
memCache := cache.NewMemoryCache(100 * 1024 * 1024) // 100MB
// Create disk cache
diskCache, _ := cache.NewDiskCache("/tmp/nuget-cache", 1*1024*1024*1024) // 1GB
// Create multi-tier cache
mtCache := cache.NewMultiTierCache(memCache, diskCache)
// Use with client
repo := core.NewSourceRepository(core.RepositoryConfig{
Name: "nuget.org",
SourceURL: "https://api.nuget.org/v3/index.json",
HTTPClient: httpClient,
Cache: mtCache,
})# All tests
make test
# Go unit tests only (skip integration)
make test-go-unit
# Interop tests (validate parity with NuGet.Client)
make test-interop
# Specific package
go test ./version
go test ./core/resolverThe project includes C# interop tests that validate exact behavioral parity with NuGet.Client by running identical operations in both implementations and comparing results.
# Run CLI tests
go test ./cmd/gonuget/... -v
# Run CLI benchmarks
go test -tags=benchmark -bench=. ./cmd/gonuget- Version comparison: <20ns/op (zero allocations)
- Framework compatibility checks: optimized for hot path operations
- HTTP/2 connection pooling and multiplexing
- HTTP/3 support for reduced latency
- Multi-tier caching with efficient TTL and ETag validation
- 15-17x faster than dotnet nuget for common operations
- 30-35% less memory per command invocation
- Startup time: ~6-7ms (vs ~100-120ms for dotnet nuget)
- Zero runtime overhead (native binary)
See cmd/gonuget/benchmarks/README.md for detailed benchmarks.
- Go 1.25.2 or later
- For interop tests: .NET 9.0 SDK
# Build
make build
# Format code
make fmt
# Run linter
make lint
# Clean build artifacts
make cleanMIT License - See LICENSE file