integrations

package
v1.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package integrations provides HTTP clients for package registry APIs.

Overview

This package contains low-level API clients for fetching package metadata from various registries. Each registry has its own subpackage:

  • [pypi]: Python Package Index
  • [npm]: Node Package Manager
  • [crates]: Rust crates.io
  • [rubygems]: Ruby gems
  • [packagist]: PHP Composer packages
  • [maven]: Java Maven Central
  • [goproxy]: Go Module Proxy
  • [github]: GitHub API for metadata enrichment
  • [gitlab]: GitLab API for metadata enrichment

Client Pattern

All registry clients follow a consistent pattern:

client, err := pypi.NewClient(24 * time.Hour)  // Cache TTL
pkg, err := client.FetchPackage(ctx, "fastapi", false)  // false = use cache

Clients handle:

  • HTTP requests with retry and rate limiting
  • Response caching (file-based, configurable TTL)
  • API-specific parsing and normalization

Shared Infrastructure

The Client type provides shared HTTP functionality used by all registry clients, including HTTP response caching via cache.Cache.

Adding a New Registry

To add support for a new package registry:

  1. Create a subpackage: pkg/integrations/<registry>/
  2. Define response structs matching the API schema
  3. Implement a Client with FetchPackage method
  4. Use NewClient for HTTP with caching
  5. Wire into [deps] as a new language

[pypi]: github.com/stacktower-io/stacktower/pkg/integrations/pypi [npm]: github.com/stacktower-io/stacktower/pkg/integrations/npm [crates]: github.com/stacktower-io/stacktower/pkg/integrations/crates [rubygems]: github.com/stacktower-io/stacktower/pkg/integrations/rubygems [packagist]: github.com/stacktower-io/stacktower/pkg/integrations/packagist [maven]: github.com/stacktower-io/stacktower/pkg/integrations/maven [goproxy]: github.com/stacktower-io/stacktower/pkg/integrations/goproxy [github]: github.com/stacktower-io/stacktower/pkg/integrations/github [gitlab]: github.com/stacktower-io/stacktower/pkg/integrations/gitlab cache.Cache: github.com/stacktower-io/stacktower/pkg/cache.Cache [deps]: github.com/stacktower-io/stacktower/pkg/core/deps

Example (Errors)
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/integrations"
)

func main() {
	// Standard errors for registry operations
	fmt.Println("ErrNotFound:", integrations.ErrNotFound)
	fmt.Println("ErrNetwork:", integrations.ErrNetwork)
}
Output:
ErrNotFound: not found
ErrNetwork: network error

Index

Examples

Constants

View Source
const DefaultTimeout = 10 * time.Second

DefaultTimeout is the default timeout for registry API requests when no registry-specific timeout is configured.

View Source
const MaxResponseSize = 25 * 1024 * 1024

MaxResponseSize is the maximum allowed HTTP response body size (25MB). Responses larger than this are rejected to prevent memory exhaustion. Set to 25MB to accommodate large PyPI project metadata (e.g., pydantic-core has >10MB of release file metadata across all platforms/versions).

View Source
const UserAgent = "stacktower/1.0"

UserAgent is the default User-Agent header for all HTTP requests.

Variables

View Source
var (
	// ErrNotFound is returned when a package or resource doesn't exist in the registry.
	// This corresponds to HTTP 404 responses.
	ErrNotFound = cache.ErrNotFound

	// ErrNetwork is returned for HTTP failures (timeouts, connection errors, 5xx responses).
	ErrNetwork = cache.ErrNetwork

	// ErrUnauthorized is returned when authentication fails (HTTP 401/403).
	// This typically means the API token is invalid, expired, or revoked.
	ErrUnauthorized = cache.ErrUnauthorized
)

Sentinel errors - re-exported from cache for API consistency.

View Source
var DefaultRateLimits = map[string]BurstLimit{
	"pypi":          {RequestsPerSecond: 50, Burst: 30},
	"npm":           {RequestsPerSecond: 50, Burst: 30},
	"crates":        {RequestsPerSecond: 25, Burst: 20},
	"rubygems":      {RequestsPerSecond: 30, Burst: 20},
	"packagist":     {RequestsPerSecond: 30, Burst: 20},
	"maven":         {RequestsPerSecond: 30, Burst: 20},
	"goproxy":       {RequestsPerSecond: 50, Burst: 30},
	"github":        {RequestsPerSecond: 10, Burst: 50},
	"github_unauth": {RequestsPerSecond: 0.015, Burst: 5},
	"osv":           {RequestsPerSecond: 20, Burst: 15},
}

DefaultRateLimits provides per-registry burst limits.

These are intentionally generous - they exist to prevent stampedes, not to enforce registry rate limits. Actual rate limit compliance is handled by:

  • Retry with backoff (honors Retry-After header)
  • Circuit breaker (opens after consecutive 429s)

The Burst value is the key constraint: it limits how many requests can be in-flight before we start getting 429 responses back.

Registry-specific notes:

  • crates.io: Has strict 1 req/s policy, so we use lower burst
  • GitHub: Has hard hourly limits, so we use lower values
  • Others: CDN-backed or generous, higher bursts are safe
View Source
var DefaultTimeouts = map[string]time.Duration{
	"pypi":      10 * time.Second,
	"npm":       10 * time.Second,
	"crates":    10 * time.Second,
	"rubygems":  10 * time.Second,
	"packagist": 10 * time.Second,
	"maven":     30 * time.Second,
	"goproxy":   15 * time.Second,
	"github":    20 * time.Second,
	"osv":       30 * time.Second,
}

DefaultTimeouts provides per-registry HTTP request timeouts. Registries not listed here use DefaultTimeout (10s).

Timeout values are tuned based on observed API performance:

  • Most registries are fast (10s is generous)
  • Maven Central can be slow with large POMs (30s)
  • GitHub GraphQL batches can take time (20s)
  • OSV batch queries scale with size (30s)
View Source
var ErrCircuitOpen = errors.New("circuit breaker is open")

ErrCircuitOpen is returned when the circuit breaker is open and rejecting requests.

Functions

func ExtractRepoURL

func ExtractRepoURL(re *regexp.Regexp, urls map[string]string, homepage string) (owner, repo string, ok bool)

ExtractRepoURL finds GitHub/GitLab owner and repo from package URLs. It searches through urls using standard keys (Source, Repository, Code, Homepage) and falls back to homepage if no match is found.

The re parameter should match URLs and capture:

  • Group 1: owner/organization name
  • Group 2: repository name

Examples:

re := regexp.MustCompile(`https?://github\.com/([^/]+)/([^/]+)`)
owner, repo, ok := ExtractRepoURL(re, pkg.ProjectURLs, pkg.HomePage)

URLs containing "/sponsors/" are automatically skipped to avoid false positives. The .git suffix is trimmed from the repository name if present.

Parameters:

  • re: Regular expression with exactly 2 capture groups (must not be nil)
  • urls: Map of URL keys to URL values (may be nil or empty)
  • homepage: Fallback homepage URL (may be empty)

Returns:

  • owner: The repository owner/organization (empty if not found)
  • repo: The repository name without .git suffix (empty if not found)
  • ok: true if a valid match was found, false otherwise

This function is safe for concurrent use if re is not mutated. Panics if re is nil.

func IsRateLimitedError

func IsRateLimitedError(err error) bool

IsRateLimitedError checks if an error is or wraps a RateLimitedError.

func LatestVersion

func LatestVersion(versions []string) string

LatestVersion returns the highest semantic version from a slice. Returns empty string if the slice is empty.

func NewHTTPClient

func NewHTTPClient() *http.Client

NewHTTPClient creates an HTTP client with DefaultTimeout for registry requests.

The client is safe for concurrent use by multiple goroutines. Returns a new client on every call; clients are not pooled.

func NewHTTPClientWithTimeout

func NewHTTPClientWithTimeout(timeout time.Duration) *http.Client

NewHTTPClientWithTimeout creates an HTTP client with a custom timeout. If timeout is <= 0, DefaultTimeout is used.

The client is safe for concurrent use by multiple goroutines. Returns a new client on every call; clients are not pooled.

func NormalizePkgName

func NormalizePkgName(name string) string

NormalizePkgName converts a package name to its canonical form. Applies lowercase and replaces underscores with hyphens, following PEP 503 normalization rules used by PyPI and other registries.

Normalization steps:

  1. Trim leading and trailing whitespace
  2. Convert to lowercase
  3. Replace all underscores with hyphens

Examples:

NormalizePkgName("FastAPI")      → "fastapi"
NormalizePkgName("my_package")   → "my-package"
NormalizePkgName("  Spaces  ")   → "spaces"

An empty string input returns an empty string. This function is safe for concurrent use.

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/integrations"
)

func main() {
	// Package names are normalized to lowercase with hyphens
	fmt.Println(integrations.NormalizePkgName("FastAPI"))
	fmt.Println(integrations.NormalizePkgName("my_package"))
	fmt.Println(integrations.NormalizePkgName("  Spaces  "))
}
Output:
fastapi
my-package
spaces

func NormalizeRepoURL

func NormalizeRepoURL(raw string) string

NormalizeRepoURL converts various repository URL formats to canonical HTTPS form. Handles git@, git://, and git+ prefixes, and removes .git suffixes.

Transformations applied:

Returns an empty string if the input is empty or contains only whitespace. Non-git URLs are returned unchanged after whitespace trimming and .git suffix removal. This function is safe for concurrent use.

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/integrations"
)

func main() {
	// Various repository URL formats are normalized to HTTPS
	fmt.Println(integrations.NormalizeRepoURL("git@github.com:user/repo.git"))
	fmt.Println(integrations.NormalizeRepoURL("git://github.com/user/repo"))
	fmt.Println(integrations.NormalizeRepoURL("git+https://github.com/user/repo.git"))
	fmt.Println(integrations.NormalizeRepoURL("https://github.com/user/repo"))
}
Output:
https://github.com/user/repo
https://github.com/user/repo
https://github.com/user/repo
https://github.com/user/repo

func SortVersions

func SortVersions(versions []string)

SortVersions sorts a slice of version strings in ascending semantic version order. Non-semver versions are sorted to the end alphabetically.

func SortVersionsDescending

func SortVersionsDescending(versions []string)

SortVersionsDescending sorts a slice of version strings in descending semantic version order. This puts the latest (highest) version first. Non-semver versions are sorted to the end alphabetically, mirroring SortVersions behaviour.

func TimeoutForRegistry

func TimeoutForRegistry(registry string) time.Duration

TimeoutForRegistry returns the configured timeout for a registry. Falls back to DefaultTimeout for registries not in DefaultTimeouts.

func URLEncode

func URLEncode(s string) string

URLEncode percent-encodes a string for use in URLs. This is a convenience wrapper around url.QueryEscape.

Spaces are encoded as "+", and special characters as "%XX" hex sequences. An empty string returns an empty string. This function is safe for concurrent use.

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/integrations"
)

func main() {
	// URL-encode special characters for API queries
	fmt.Println(integrations.URLEncode("@scope/package"))
	fmt.Println(integrations.URLEncode("package name"))
}
Output:
%40scope%2Fpackage
package+name

Types

type BurstLimit

type BurstLimit struct {
	// RequestsPerSecond is the sustained token refill rate.
	// Set high enough to not slow down normal operation.
	RequestsPerSecond float64
	// Burst is the maximum concurrent requests allowed in a burst.
	// This is the key constraint - limits how many requests can fire at once.
	Burst int
}

BurstLimit defines burst limiting parameters for a registry client. These limits prevent request stampedes when the resolver fires many concurrent requests. They are NOT intended to stay under registry rate limits - that's handled by the retry/backoff mechanism and circuit breaker responding to 429s.

The goal is to prevent flooding a registry with 100+ simultaneous requests before we even receive the first 429 response.

type CircuitBreaker

type CircuitBreaker struct {
	// contains filtered or unexported fields
}

CircuitBreaker implements the circuit breaker pattern for rate limit handling. It tracks consecutive rate limit failures and temporarily stops requests when a threshold is exceeded, preventing resource exhaustion during rate limiting.

States:

  • Closed: Normal operation, requests are allowed
  • Open: Requests are rejected to allow recovery
  • Half-Open: One probe request is allowed to test if the service has recovered

CircuitBreaker is safe for concurrent use.

func NewCircuitBreaker

func NewCircuitBreaker(registry string, config CircuitBreakerConfig) *CircuitBreaker

NewCircuitBreaker creates a circuit breaker for the given registry.

func (*CircuitBreaker) Allow

func (cb *CircuitBreaker) Allow(ctx context.Context) bool

Allow checks if a request should be allowed through the circuit breaker. Returns true if the request can proceed, false if it should be rejected.

In the half-open state, only one request is allowed through to probe whether the service has recovered.

func (*CircuitBreaker) Failures

func (cb *CircuitBreaker) Failures() int

Failures returns the current consecutive failure count.

func (*CircuitBreaker) RecordFailure

func (cb *CircuitBreaker) RecordFailure(ctx context.Context, retryAfter int)

RecordFailure records a rate limit failure. If the failure threshold is exceeded, the circuit opens. The retryAfter parameter is used to set the cooldown duration if provided.

func (*CircuitBreaker) RecordSuccess

func (cb *CircuitBreaker) RecordSuccess(ctx context.Context)

RecordSuccess records a successful request. In the half-open state, this closes the circuit. In the closed state, this resets the failure counter.

func (*CircuitBreaker) Reset

func (cb *CircuitBreaker) Reset(ctx context.Context)

Reset resets the circuit breaker to its initial closed state. This is primarily useful for testing.

func (*CircuitBreaker) State

State returns the current state of the circuit breaker.

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	// Threshold is the number of consecutive rate limit failures before opening.
	// Default: 5
	Threshold int
	// Cooldown is how long the circuit stays open before transitioning to half-open.
	// Default: 30s
	Cooldown time.Duration
}

CircuitBreakerConfig holds configuration for a circuit breaker.

func DefaultCircuitBreakerConfig

func DefaultCircuitBreakerConfig() CircuitBreakerConfig

DefaultCircuitBreakerConfig returns sensible defaults for circuit breaker configuration.

type CircuitBreakerRegistry

type CircuitBreakerRegistry struct {
	// contains filtered or unexported fields
}

CircuitBreakerRegistry manages circuit breakers for multiple registries. It provides a thread-safe way to get or create circuit breakers per registry.

func NewCircuitBreakerRegistry

func NewCircuitBreakerRegistry(config CircuitBreakerConfig) *CircuitBreakerRegistry

NewCircuitBreakerRegistry creates a registry with the given default configuration.

func (*CircuitBreakerRegistry) Get

func (r *CircuitBreakerRegistry) Get(registry string) *CircuitBreaker

Get returns the circuit breaker for the given registry, creating one if needed.

func (*CircuitBreakerRegistry) Reset

func (r *CircuitBreakerRegistry) Reset(ctx context.Context)

Reset resets all circuit breakers to their initial state. This is primarily useful for testing.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client provides shared HTTP functionality for all registry API clients. It handles caching, retry logic, request deduplication, proactive rate limiting, circuit breaking, and common request headers.

Client is safe for concurrent use by multiple goroutines. The underlying HTTP client, cache, and headers are all goroutine-safe.

Zero values: Do not use an uninitialized Client; always create via NewClient.

func NewClient

func NewClient(c cache.Cache, namespace string, ttl time.Duration, headers map[string]string) *Client

NewClient creates a Client with the given cache and default headers. Headers are applied to all requests made through this client.

The HTTP timeout is automatically configured based on the registry namespace using DefaultTimeouts. For example, "pypi:" uses 10s, "maven:" uses 30s.

Parameters:

  • c: Cache for caching HTTP responses. If nil, a NullCache is used (no caching).
  • namespace: Cache key prefix for this client (e.g., "pypi:", "npm:").
  • ttl: How long to cache responses.
  • headers: Default HTTP headers for all requests. Pass nil if no default headers are needed. Common examples: "Authorization", "User-Agent", "Accept".

The returned Client is safe for concurrent use by multiple goroutines.

func NewClientWithRateLimit

func NewClientWithRateLimit(c cache.Cache, namespace string, ttl time.Duration, headers map[string]string, rps float64, burst int) *Client

NewClientWithRateLimit creates a Client with proactive rate limiting. The limiter throttles outbound requests to stay within the registry's rate limits, preventing 429 errors proactively rather than only reacting to them.

Parameters are the same as NewClient, plus:

  • rps: Maximum sustained requests per second. If <= 0, no rate limiting is applied.
  • burst: Maximum burst size (concurrent requests allowed at once). If <= 0, defaults to 1.

func (*Client) Cached

func (c *Client) Cached(ctx context.Context, key string, refresh bool, v any, fetch func() error) error

Cached retrieves a value from cache or executes fetch and caches the result. If refresh is true, the cache is bypassed and fetch is always called.

Concurrent requests for the same key are deduplicated via singleflight: only one fetch executes and all callers receive the same result.

Parameters:

  • ctx: Context for cancellation. If cancelled, fetch is not executed and returns ctx.Err().
  • key: Cache key (usually package name or coordinate). Must not be empty.
  • refresh: If true, bypass cache and always call fetch. If false, try cache first.
  • v: Pointer to store the result. Must be a non-nil pointer to a JSON-serializable type.
  • fetch: Function to fetch data and populate v. Called with retry on transient failures.

Behavior:

  1. If refresh=false and cache hit: returns nil immediately with v populated
  2. If cache miss or refresh=true: calls fetch with automatic retry on [RetryableError]
  3. Concurrent fetches for the same key are deduplicated (only one HTTP call)
  4. On successful fetch: stores result in cache (ignoring cache write errors)

The fetch function should populate v and return nil on success, or return an error. Network errors should be wrapped with [Retryable] to enable retry.

Observability hooks are emitted for cache hits, misses, and writes via observability.Cache.

Returns:

  • nil on success (v is populated)
  • error from fetch if it fails (v may be partially populated)
  • ctx.Err() if context is cancelled

This method is safe for concurrent use on the same Client.

func (*Client) Get

func (c *Client) Get(ctx context.Context, url string, v any) error

Get performs an HTTP GET request and JSON-decodes the response into v. It uses the client's default headers and handles retries automatically.

Parameters:

  • ctx: Context for cancellation and timeout
  • url: Full URL to request (must be absolute URL with scheme)
  • v: Pointer to store decoded JSON response (must be non-nil)

Returns:

  • ErrNotFound for HTTP 404 responses
  • ErrNetwork wrapped with [RetryableError] for HTTP 5xx responses
  • ErrNetwork for connection failures and timeouts
  • json decoding errors if response is not valid JSON

This method is safe for concurrent use on the same Client.

func (*Client) GetText

func (c *Client) GetText(ctx context.Context, url string) (string, error)

GetText performs an HTTP GET request and returns the response body as a string. Useful for non-JSON endpoints like go.mod files or plain text responses.

Parameters:

  • ctx: Context for cancellation and timeout
  • url: Full URL to request (must be absolute URL with scheme)

Response size is limited to MaxResponseSize bytes to prevent memory exhaustion.

Returns:

  • The response body as a string
  • ErrNotFound for HTTP 404 responses
  • ErrNetwork for connection failures, timeouts, and HTTP 5xx responses
  • Error if response exceeds MaxResponseSize
  • io errors if reading the response body fails

This method is safe for concurrent use on the same Client.

func (*Client) GetWithHeaders

func (c *Client) GetWithHeaders(ctx context.Context, url string, headers map[string]string, v any) error

GetWithHeaders performs an HTTP GET with additional headers merged with defaults. Request-specific headers override client defaults for the same key.

Parameters:

  • ctx: Context for cancellation and timeout
  • url: Full URL to request (must be absolute URL with scheme)
  • headers: Additional headers for this request only (may be nil). Headers with the same key as client defaults will override the default value for this request.
  • v: Pointer to store decoded JSON response (must be non-nil)

Example:

err := client.GetWithHeaders(ctx, url, map[string]string{"X-Custom": "value"}, &resp)

Returns the same errors as [Get]. This method is safe for concurrent use on the same Client.

func (*Client) PostJSON

func (c *Client) PostJSON(ctx context.Context, url string, body any, v any) error

PostJSON sends a POST request with a JSON body and decodes the response. Uses the client's default headers and rate limiter. Response size is limited to MaxResponseSize bytes.

type Contributor

type Contributor struct {
	Login         string `json:"login"`         // GitHub/GitLab username. Never empty in valid contributors.
	Contributions int    `json:"contributions"` // Number of commits. Always positive in valid contributors.
}

Contributor represents a repository contributor with their contribution count. Used for bus factor analysis and maintainer identification.

Zero values: Login is empty, Contributions is 0. A Contributor with 0 contributions is invalid. This struct is safe for concurrent reads.

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/integrations"
)

func main() {
	// Contributors track commit counts for bus factor analysis
	contributors := []integrations.Contributor{
		{Login: "maintainer1", Contributions: 500},
		{Login: "maintainer2", Contributions: 200},
		{Login: "contributor3", Contributions: 50},
	}

	fmt.Println("Top contributor:", contributors[0].Login)
	fmt.Println("Contributions:", contributors[0].Contributions)
}
Output:
Top contributor: maintainer1
Contributions: 500

type RateLimit

type RateLimit = BurstLimit

RateLimit is an alias for BurstLimit for backward compatibility.

type RateLimitedError

type RateLimitedError struct {
	RetryAfter int // Seconds to wait before retrying (0 if unknown)
}

RateLimitedError indicates the API rate limit has been exceeded.

func (*RateLimitedError) Error

func (e *RateLimitedError) Error() string

Error implements the error interface.

func (*RateLimitedError) RetryAfterSeconds

func (e *RateLimitedError) RetryAfterSeconds() int

RetryAfterSeconds returns the requested wait time in seconds.

type RepoMetrics

type RepoMetrics struct {
	RepoURL       string        `json:"repo_url"`                   // Canonical repository URL (https://...). Never empty in valid metrics.
	Owner         string        `json:"owner"`                      // Repository owner username. Never empty in valid metrics.
	Description   string        `json:"description,omitempty"`      // Repository description from GitHub/GitLab. Empty if not set.
	Stars         int           `json:"stars"`                      // GitHub/GitLab star count. 0 is a valid value for new repositories.
	SizeKB        int           `json:"size_kb,omitempty"`          // Repository size in kilobytes. 0 means not available or very small.
	LastCommitAt  *time.Time    `json:"last_commit_at,omitempty"`   // Date of most recent commit. Nil if not available.
	LastReleaseAt *time.Time    `json:"last_release_at,omitempty"`  // Date of most recent release. Nil if no releases or not available.
	License       string        `json:"license,omitempty"`          // SPDX license identifier (e.g., "MIT", "Apache-2.0"). Empty if not detected.
	Contributors  []Contributor `json:"top_contributors,omitempty"` // Top contributors by commit count (typically top 5). Nil or empty if not available.
	Language      string        `json:"language,omitempty"`         // Primary repository language (e.g., "Go", "Python"). Empty if not detected.
	Topics        []string      `json:"topics,omitempty"`           // Repository topic tags. Nil or empty if none.
	Archived      bool          `json:"archived"`                   // Whether the repository is archived. False means active or unknown.
}

RepoMetrics holds repository-level data fetched from GitHub or GitLab. Used to enrich package metadata with maintenance and popularity indicators.

Zero values: All string fields are empty, integers are 0, time pointers are nil. Nil Contributors slice is valid and indicates no contributor data was fetched.

This struct is safe for concurrent reads after construction but not for concurrent writes.

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/integrations"
)

func main() {
	// RepoMetrics holds repository data from GitHub/GitLab
	metrics := integrations.RepoMetrics{
		RepoURL:  "https://github.com/psf/requests",
		Owner:    "psf",
		Stars:    51000,
		Language: "Python",
		Archived: false,
	}

	fmt.Println("Repository:", metrics.RepoURL)
	fmt.Println("Stars:", metrics.Stars)
	fmt.Println("Archived:", metrics.Archived)
}
Output:
Repository: https://github.com/psf/requests
Stars: 51000
Archived: false

type SemanticVersion

type SemanticVersion struct {
	Original   string
	Major      int
	Minor      int
	Patch      int
	Prerelease string // e.g., "alpha", "beta.1", "rc.1"
	Build      string // e.g., build metadata after '+'
	Valid      bool
}

SemanticVersion represents a parsed semantic version.

func ParseSemver

func ParseSemver(version string) SemanticVersion

ParseSemver parses a version string into a SemanticVersion. Supports formats: "1", "1.2", "1.2.3", "v1.2.3", "1.2.3-beta", "1.2.3+build"

func (SemanticVersion) Compare

func (a SemanticVersion) Compare(b SemanticVersion) int

Compare compares two SemanticVersions. Returns -1 if a < b, 0 if a == b, 1 if a > b. Invalid versions are sorted to the end.

Directories

Path Synopsis
Package crates provides an HTTP client for the crates.io API.
Package crates provides an HTTP client for the crates.io API.
Package github provides an HTTP client for the GitHub API.
Package github provides an HTTP client for the GitHub API.
Package gitlab provides an HTTP client for the GitLab API.
Package gitlab provides an HTTP client for the GitLab API.
Package goproxy provides an HTTP client for the Go Module Proxy.
Package goproxy provides an HTTP client for the Go Module Proxy.
Package maven provides an HTTP client for Maven Central.
Package maven provides an HTTP client for Maven Central.
Package npm provides an HTTP client for the npm registry API.
Package npm provides an HTTP client for the npm registry API.
Package osv provides a client for the OSV.dev vulnerability database API.
Package osv provides a client for the OSV.dev vulnerability database API.
Package packagist provides an HTTP client for the Packagist API.
Package packagist provides an HTTP client for the Packagist API.
Package pypi provides an HTTP client for the Python Package Index API.
Package pypi provides an HTTP client for the Python Package Index API.
Package rubygems provides an HTTP client for the RubyGems.org API.
Package rubygems provides an HTTP client for the RubyGems.org API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL