{"id":36546220,"url":"https://github.com/contriboss/pubgrub-go","last_synced_at":"2026-01-16T07:44:50.693Z","repository":{"id":321034193,"uuid":"1084099054","full_name":"contriboss/pubgrub-go","owner":"contriboss","description":"A comprehensive Go implementation of the PubGrub version solving algorithm with CDCL, semantic versioning, and enhanced error reporting","archived":false,"fork":false,"pushed_at":"2025-10-27T12:08:28.000Z","size":86,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-27T13:10:57.786Z","etag":null,"topics":["cdcl","dependency-resolution","golang","package-manager","pubgrub","sat-solver","semver","version-solving"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/contriboss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-27T08:11:07.000Z","updated_at":"2025-10-27T12:08:27.000Z","dependencies_parsed_at":"2025-10-27T13:11:01.938Z","dependency_job_id":"02ff0588-6318-45b6-a281-91d41f461934","html_url":"https://github.com/contriboss/pubgrub-go","commit_stats":null,"previous_names":["contriboss/pubgrub-go"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/contriboss/pubgrub-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contriboss%2Fpubgrub-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contriboss%2Fpubgrub-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contriboss%2Fpubgrub-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contriboss%2Fpubgrub-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/contriboss","download_url":"https://codeload.github.com/contriboss/pubgrub-go/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contriboss%2Fpubgrub-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28336098,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T00:36:25.062Z","status":"online","status_checked_at":"2026-01-12T02:00:08.677Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cdcl","dependency-resolution","golang","package-manager","pubgrub","sat-solver","semver","version-solving"],"created_at":"2026-01-12T06:02:06.292Z","updated_at":"2026-01-12T06:03:50.762Z","avatar_url":"https://github.com/contriboss.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PubGrub-Go\n\nA comprehensive Go implementation of the PubGrub version solving algorithm with semantic versioning, version ranges, and enhanced error reporting.\n\n[![Go Version](https://img.shields.io/badge/Go-1.25%2B-blue.svg)](https://golang.org)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n[![Algorithm](https://img.shields.io/badge/Algorithm-CDCL-green.svg)](.)\n\n## Features\n\n- ✨ **Semantic Versioning** - Full semver support with major.minor.patch ordering\n- 📊 **Version Ranges** - Complex constraints like `\u003e=1.0.0, \u003c2.0.0 || \u003e=3.0.0`\n- 🔍 **Enhanced Error Messages** - Human-readable explanations of why resolution fails\n- 🚀 **CDCL Solver** - Conflict-driven clause learning with unit propagation\n- 🧪 **Well Tested** - Comprehensive test suite with strong coverage\n- ⚡ **Production Ready** - Handles complex dependency graphs efficiently\n- 🪵 **Structured Debug Logging** - Plug in `log/slog` via `WithLogger` for rich solver traces\n\n## Origin\n\nThis is a derivative work based on the [tinyrange/tinyrange](https://github.com/tinyrange/tinyrange) `experimental/pubgrub` package (v0.2.6). The original implementation was removed from the tinyrange repository. This package preserves the original work and significantly extends it with:\n\n- Semantic versioning support\n- Complex version range constraints\n- Enhanced error reporting with derivation trees\n- Comprehensive test suite\n- Production-ready CDCL solver\n\n**Original Copyright:** Copyright 2024 The University of Queensland\n**Enhancements:** Copyright 2025 Contriboss\n**License:** Apache 2.0\n\n## About PubGrub\n\nPubGrub is a version solving algorithm designed for dependency resolution. This implementation features a **production-ready CDCL (Conflict-Driven Clause Learning) solver** with unit propagation, learned clauses, and intelligent backtracking for efficient dependency resolution. The solver includes optional incompatibility tracking for generating detailed, human-readable error messages with derivation trees.\n\n## Installation\n\n**Requirements:** Go 1.25 or later\n\n```bash\ngo get github.com/contriboss/pubgrub-go\n```\n\n## Quick Start\n\n### Basic Usage\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/contriboss/pubgrub-go\"\n)\n\nfunc main() {\n    // Create a root source with your initial requirements\n    root := pubgrub.NewRootSource()\n    root.AddPackage(\"mypackage\", pubgrub.EqualsCondition{Version: pubgrub.SimpleVersion(\"1.0.0\")})\n\n    // Create an in-memory source for your package repository\n    source := \u0026pubgrub.InMemorySource{}\n    source.AddPackage(\"mypackage\", pubgrub.SimpleVersion(\"1.0.0\"), []pubgrub.Term{\n        pubgrub.NewTerm(\"dependency\", pubgrub.EqualsCondition{Version: pubgrub.SimpleVersion(\"2.0.0\")}),\n    })\n    source.AddPackage(\"dependency\", pubgrub.SimpleVersion(\"2.0.0\"), nil)\n\n    // Create a solver and solve\n    solver := pubgrub.NewSolver(root, source)\n    solution, err := solver.Solve(root.Term())\n    if err != nil {\n        panic(err)\n    }\n\n    fmt.Println(\"Solution:\", solution)\n}\n```\n\n### Using New Features: Version Ranges + Better Errors\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n    \"github.com/contriboss/pubgrub-go\"\n)\n\nfunc main() {\n    // Use semantic versioning\n    webVersion, _ := pubgrub.ParseSemanticVersion(\"1.0.0\")\n\n    // Create requirements with version ranges\n    httpRange, _ := pubgrub.ParseVersionRange(\"\u003e=2.0.0, \u003c3.0.0\")\n    jsonRange, _ := pubgrub.ParseVersionRange(\"\u003e=1.5.0\")\n\n    root := pubgrub.NewRootSource()\n    root.AddPackage(\"web\", pubgrub.EqualsCondition{Version: webVersion})\n\n    source := \u0026pubgrub.InMemorySource{}\n    source.AddPackage(\"web\", webVersion, []pubgrub.Term{\n        pubgrub.NewTerm(\"http\", pubgrub.NewVersionSetCondition(httpRange)),\n        pubgrub.NewTerm(\"json\", pubgrub.NewVersionSetCondition(jsonRange)),\n    })\n\n    http2, _ := pubgrub.ParseSemanticVersion(\"2.5.0\")\n    json15, _ := pubgrub.ParseSemanticVersion(\"1.5.0\")\n    source.AddPackage(\"http\", http2, nil)\n    source.AddPackage(\"json\", json15, nil)\n\n    // Enable enhanced error reporting\n    solver := pubgrub.NewSolver(root, source).EnableIncompatibilityTracking()\n    solution, err := solver.Solve(root.Term())\n\n    if err != nil {\n        if nsErr, ok := err.(*pubgrub.NoSolutionError); ok {\n            fmt.Fprintln(os.Stderr, nsErr.Error())\n        } else {\n            fmt.Fprintln(os.Stderr, err)\n        }\n        os.Exit(1)\n    }\n\n    for _, nv := range solution {\n        fmt.Printf(\"✓ %s: %s\\n\", nv.Name, nv.Version)\n    }\n}\n```\n\n### Debug Logging\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n    \"os\"\n\n    \"github.com/contriboss/pubgrub-go\"\n)\n\nfunc main() {\n    logger := slog.New(slog.NewTextHandler(os.Stdout, \u0026slog.HandlerOptions{\n        Level: slog.LevelDebug,\n    }))\n\n    root := pubgrub.NewRootSource()\n    source := \u0026pubgrub.InMemorySource{}\n\n    solver := pubgrub.NewSolverWithOptions(\n        []pubgrub.Source{root, source},\n        pubgrub.WithLogger(logger),\n    )\n\n    if _, err := solver.Solve(root.Term()); err != nil {\n        logger.Error(\"resolution failed\", \"err\", err)\n    }\n}\n```\n\n## Core Concepts\n\n### Versions\n\n```go\n// Simple string-based versions (original)\nv1 := pubgrub.SimpleVersion(\"1.0.0\")\n\n// Semantic versioning (new)\nv2, _ := pubgrub.ParseSemanticVersion(\"1.2.3\")\nv3, _ := pubgrub.ParseSemanticVersion(\"2.0.0-alpha.1\")\n```\n\n### Version Constraints\n\n```go\n// Exact match (original)\ncond := pubgrub.EqualsCondition{Version: v}\n\n// Version ranges (new)\nset, _ := pubgrub.ParseVersionRange(\"\u003e=1.0.0, \u003c2.0.0\")\ncond := pubgrub.NewVersionSetCondition(set)\n\n// Operators: \u003e=, \u003e, \u003c=, \u003c, ==, !=\n// Compound: \"\u003e=1.0.0, \u003c2.0.0\" (AND)\n// Union: \"\u003e=1.0.0 || \u003e=3.0.0\" (OR)\n// Wildcard: \"*\" (any version)\n```\n\n### Custom Conditions\n\nYou can create custom version constraints by implementing the `Condition` interface and optionally the `VersionSetConverter` interface for CDCL solver support:\n\n```go\n// Custom caret constraint (like npm's ^1.2.3)\ntype CaretCondition struct {\n    Base *SemanticVersion\n}\n\nfunc (cc CaretCondition) String() string {\n    return fmt.Sprintf(\"^%s\", cc.Base)\n}\n\nfunc (cc CaretCondition) Satisfies(ver Version) bool {\n    sv, ok := ver.(*SemanticVersion)\n    if !ok { return false }\n    return sv.Major == cc.Base.Major \u0026\u0026 sv.Sort(cc.Base) \u003e= 0\n}\n\n// Implement VersionSetConverter to enable CDCL solver support\nfunc (cc CaretCondition) ToVersionSet() VersionSet {\n    rangeStr := fmt.Sprintf(\"\u003e=%d.%d.%d, \u003c%d.0.0\",\n        cc.Base.Major, cc.Base.Minor, cc.Base.Patch,\n        cc.Base.Major+1)\n    set, _ := ParseVersionRange(rangeStr)\n    return set\n}\n\n// Use it with the solver\nbase, _ := ParseSemanticVersion(\"1.2.0\")\ncondition := CaretCondition{Base: base}\nroot.AddPackage(\"mylib\", condition)\n```\n\nThe `VersionSetConverter` interface enables your custom condition to participate in set operations (union, intersection, complement) required by the CDCL solver. Without it, custom conditions work for simple resolution but may fail in complex scenarios requiring conflict analysis.\n\n### Error Reporting\n\n```go\n// Simple errors (default)\nsolver := pubgrub.NewSolver(root, source)\n_, err := solver.Solve(root.Term())\n\n// Enhanced errors (opt-in)\nsolver.EnableIncompatibilityTracking()\nif nsErr, ok := err.(*pubgrub.NoSolutionError); ok {\n    fmt.Println(nsErr.Error()) // Human-readable explanation\n}\n```\n\n### Solver Configuration\n\nYou can tune the solver with functional options when constructing it, or update an existing instance:\n\n```go\nsolver := pubgrub.NewSolverWithOptions(\n    []pubgrub.Source{root, source},\n    pubgrub.WithIncompatibilityTracking(true),\n    pubgrub.WithMaxSteps(10_000), // 0 disables the watchdog\n)\n\n// Adjust configuration later if needed\nsolver.Configure(pubgrub.WithMaxSteps(0))\n```\n\n`WithIncompatibilityTracking` toggles derivation tree generation, while `WithMaxSteps` caps (or disables) the internal propagation watchdog used to detect runaway scenarios.\n\n### Performance Optimization with Caching\n\nFor sources with expensive I/O operations (network, disk, database), wrap them with `CachedSource`:\n\n```go\n// Wrap an expensive source (e.g., HTTP API, database)\nexpensiveSource := \u0026MyRegistrySource{} // implements Source\ncached := pubgrub.NewCachedSource(expensiveSource)\n\nsolver := pubgrub.NewSolver(root, cached)\nsolution, _ := solver.Solve(root.Term())\n\n// Check cache performance\nstats := cached.GetCacheStats()\nfmt.Printf(\"Cache hit rate: %.1f%%\\n\", stats.OverallHitRate * 100)\n```\n\n**When to use caching:**\n- ✅ Network sources (package registries, APIs)\n- ✅ Database or file system sources\n- ✅ Multiple dependency resolutions with the same source\n- ❌ InMemorySource (already fast, adds ~3-5% overhead)\n- ❌ Single-shot resolutions\n\n## API Reference\n\n### Core Types\n- **`Name`** - Package name identifier\n- **`Version`** - Interface for version representation\n- **`Condition`** - Interface for version constraints\n- **`VersionSetConverter`** - Optional interface for custom conditions to enable CDCL solver support\n- **`Term`** - Package name with constraint\n- **`Source`** - Package version/dependency queries\n- **`Solution`** - Resolved package versions\n- **`VersionSet`** - Set of versions with operations\n\n### Implementations\n- **`SimpleVersion`** - String-based version (original)\n- **`SemanticVersion`** - Full semver support (new)\n- **`EqualsCondition`** - Exact match (original)\n- **`VersionSetCondition`** - Version ranges (new)\n- **`InMemorySource`** - In-memory repository\n- **`CachedSource`** - Caching wrapper for expensive sources (new)\n- **`CombinedSource`** - Multiple sources\n- **`RootSource`** - Initial requirements\n\n### Solver\n- **`NewSolver(sources...)`** - Create solver with defaults\n- **`NewSolverWithOptions([]Source, ...SolverOption)`** - Create solver with configuration options\n- **`Solve(root)`** - Solve dependencies\n- **`EnableIncompatibilityTracking()`** - Enable detailed errors\n- **`Configure(...SolverOption)`** - Adjust options after construction\n- **`GetIncompatibilities()`** - Get tracked conflicts\n\n### Error Types\n- **`ErrNoSolutionFound`** - Simple error (original)\n- **`NoSolutionError`** - Enhanced error (new)\n- **`ErrIterationLimit`** - Solver exceeded configured step limit\n- **`DefaultReporter`** / **`CollapsedReporter`** - Error formatters (new)\n\n## Examples\n\nSee runnable examples in test files:\n\n```bash\ngo test -v -run Example\n```\n\n- `ExampleVersionSetCondition` - Using version ranges with solver\n- `ExampleParseVersionRange` - Parsing and testing version ranges\n- `ExampleSemanticVersion` - Semantic version parsing\n- `ExampleNoSolutionError_defaultReporter` - Enhanced error messages\n- `ExampleNoSolutionError_collapsedReporter` - Compact error format\n\n## Documentation\n\n- **[API Documentation](https://pkg.go.dev/github.com/contriboss/pubgrub-go)** - Full API reference (when published)\n- See example tests for usage patterns (`go test -v -run Example`)\n\n## Testing\n\n```bash\n# Run all tests\ngo test -v\n\n# Run with coverage\ngo test -cover  # 82.7% coverage\n\n# Run specific test suites\ngo test -v -run TestBackwardCompatibility\ngo test -v -run TestSemanticVersion\ngo test -v -run TestVersionRange\ngo test -v -run TestSolver\n\n# Run benchmarks\ngo test -bench=. -benchmem\n```\n\n## Benchmarks\n\nPerformance characteristics on Apple M1 Max:\n\n| Scenario | Time/op | Memory/op | Ops/sec |\n|----------|---------|-----------|---------|\n| Simple linear chain (4 packages) | 14.7μs | 17.3KB | 67,000 |\n| Diamond dependency | 19.4μs | 22.5KB | 51,000 |\n| Complex graph (10 packages) | 43.7μs | 48.7KB | 23,000 |\n| Deep chain (20 levels) | 92.0μs | 98.8KB | 10,800 |\n| Conflict detection | 14.9μs | 17.5KB | 67,000 |\n\n**Key Insights:**\n- ⚡ Most resolutions complete in **microseconds**\n- 🎯 Conflict detection is as fast as successful resolution\n- 📊 Incompatibility tracking adds **\u003c1% overhead**\n- 🚀 Scales linearly with dependency graph complexity\n\nRun `go test -bench=. -benchmem` to see performance on your system.\n\n## Performance \u0026 Status\n\n- **Test Coverage:** Strong coverage with comprehensive test suite\n- **Tests:** All passing\n- **Algorithm:** CDCL (Conflict-Driven Clause Learning) with unit propagation\n- **Memory:** Minimal; learned clauses and optional tracking add derivation trees\n- **Caching:** Optional CachedSource wrapper for expensive I/O operations\n- **Production Ready:** Yes, handles complex dependency graphs efficiently\n\n## Contributing\n\nContributions welcome! Please:\n1. Add tests for new features\n2. Update documentation\n3. Follow existing code style\n4. Ensure all tests pass\n\n## Attribution\n\nThis package is derived from the tinyrange project:\n- **Original Repository:** https://github.com/tinyrange/tinyrange\n- **Original Package:** experimental/pubgrub\n- **Version:** v0.3.3 (Nov 1, 2025)\n- **Original Copyright:** Copyright 2024 The University of Queensland\n- **Original License:** Apache 2.0\n\nEnhancements and extensions:\n- **Semantic Versioning:** Copyright 2025 Contriboss\n- **Version Ranges:** Copyright 2025 Contriboss\n- **Error Reporting:** Copyright 2025 Contriboss\n- **License:** Apache 2.0\n\n## License\n\nApache License 2.0 - See [LICENSE](LICENSE) file for details.\n\nThis project maintains the same Apache 2.0 license as the original tinyrange implementation and includes proper attribution as required by the license terms.\n\n## Credits\n\nThis implementation builds upon the following works:\n\n1. **Weizenbaum, N.** (2018). *PubGrub: Next-Generation Version Solving*. Medium. https://medium.com/@nex3/pubgrub-2fb6470504f\n\n2. **University of Queensland** (2024). *tinyrange/pubgrub* (v0.2.6). Original Go implementation. https://github.com/tinyrange/tinyrange\n\n3. **Dart Team** (2018-present). *PubGrub Specification*. https://github.com/dart-lang/pub/blob/master/doc/solver.md\n\n4. **pubgrub-rs Contributors** (2019-present). *pubgrub-rs*. Rust reference implementation providing CDCL patterns. https://github.com/pubgrub-rs/pubgrub\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontriboss%2Fpubgrub-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontriboss%2Fpubgrub-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontriboss%2Fpubgrub-go/lists"}