Skip to content

willibrandon/gonuget

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gonuget

NuGet client library and CLI for Go with protocol parity to the official .NET NuGet.Client.

Status

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.

Features

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

Installation

Library

go get github.com/willibrandon/gonuget

CLI

git clone https://github.com/willibrandon/gonuget
cd gonuget
make build
./gonuget --version

See cmd/gonuget/README.md for CLI documentation.

CLI Quick Start

# 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/gonuget

Command 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 .slnf files
  • 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

Library Usage

Basic Package Search

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)
        }
    }
}

Version Parsing and Comparison

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")
}

Dependency Resolution

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)
}

Package Reading

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)
}

Package Creation

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)
}

Framework Compatibility

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"

Caching

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,
})

Testing

Library Tests

# 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/resolver

The project includes C# interop tests that validate exact behavioral parity with NuGet.Client by running identical operations in both implementations and comparing results.

CLI Tests

# Run CLI tests
go test ./cmd/gonuget/... -v

# Run CLI benchmarks
go test -tags=benchmark -bench=. ./cmd/gonuget

Performance

Library

  • 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

CLI

  • 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.

Requirements

  • Go 1.25.2 or later
  • For interop tests: .NET 9.0 SDK

Development

# Build
make build

# Format code
make fmt

# Run linter
make lint

# Clean build artifacts
make clean

License

MIT License - See LICENSE file

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages