gps

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Jan 25, 2018 License: BSD-3-Clause Imports: 45 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrSourceManagerIsReleased = fmt.Errorf("this SourceManager has been released, its methods can no longer be called")

ErrSourceManagerIsReleased is the error returned by any SourceManager method called after the SourceManager has been released, rendering its methods no longer safe to call.

Functions

func HashingInputsAsString

func HashingInputsAsString(s Solver) string

HashingInputsAsString returns the raw input data used by Solver.HashInputs() as a string.

This is primarily intended for debugging purposes.

func IsAny

func IsAny(c Constraint) bool

IsAny indicates if the provided constraint is the wildcard "Any" constraint.

func LocksAreEq

func LocksAreEq(l1, l2 Lock, checkHash bool) bool

LocksAreEq checks if two locks are equivalent. This checks that all contained LockedProjects are equal, and optionally (if the third parameter is true) whether the locks' input hashes are equal.

func PruneProject

func PruneProject(baseDir string, lp LockedProject, options PruneOptions, logger *log.Logger) error

PruneProject remove excess files according to the options passed, from the lp directory in baseDir.

func SortForDowngrade

func SortForDowngrade(vl []Version)

SortForDowngrade sorts a slice of []Version in roughly ascending order, so that presumably older versions are visited first.

This is *not* the same as reversing SortForUpgrade (or you could simply sort.Reverse()). The type precedence is the same, including the semver vs. semver-with-prerelease relation. Lexicographical comparisons within non-semver tags, branches, and revisions remains the same as well; because we treat these domains as having no ordering relation, there can be no real concept of "upgrade" vs "downgrade", so there is no reason to reverse them.

Thus, the only binary relation that is reversed for downgrade is within-type comparisons for semver.

So, given a slice of the following versions:

  • Branch: master devel
  • Semver tags: v1.0.0, v1.1.0, v1.1.0-alpha1
  • Non-semver tags: footag
  • Revision: f6e74e8d

Sorting for downgrade will result in the following slice.

[v1.0.0 v1.1.0 v1.1.0-alpha1 footag devel master f6e74e8d]

func SortForUpgrade

func SortForUpgrade(vl []Version)

SortForUpgrade sorts a slice of []Version in roughly descending order, so that presumably newer versions are visited first. The rules are:

  • All semver versions come first, and sort mostly according to the semver 2.0 spec (as implemented by github.com/Masterminds/semver lib), with one exception:
  • Semver versions with a prerelease are after *all* non-prerelease semver. Within this subset they are sorted first by their numerical component, then lexicographically by their prerelease version.
  • The default branch(es) is next; the exact semantics of that are specific to the underlying source.
  • All other branches come next, sorted lexicographically.
  • All non-semver versions (tags) are next, sorted lexicographically.
  • Revisions, if any, are last, sorted lexicographically. Revisions do not typically appear in version lists, so the only invariant we maintain is determinism - deeper semantics, like chronology or topology, do not matter.

So, given a slice of the following versions:

  • Branch: master devel
  • Semver tags: v1.0.0, v1.1.0, v1.1.0-alpha1
  • Non-semver tags: footag
  • Revision: f6e74e8d

Sorting for upgrade will result in the following slice.

[v1.1.0 v1.0.0 v1.1.0-alpha1 footag devel master f6e74e8d]

func SortPairedForDowngrade

func SortPairedForDowngrade(vl []PairedVersion)

SortPairedForDowngrade has the same behavior as SortForDowngrade, but operates on []PairedVersion types.

func SortPairedForUpgrade

func SortPairedForUpgrade(vl []PairedVersion)

SortPairedForUpgrade has the same behavior as SortForUpgrade, but operates on []PairedVersion types.

func ValidateParams

func ValidateParams(params SolveParameters, sm SourceManager) error

ValidateParams validates the solver parameters to ensure solving can be completed.

func VersionComponentStrings

func VersionComponentStrings(v Version) (revision string, branch string, version string)

VersionComponentStrings decomposes a Version into the underlying number, branch and revision

func WriteDepTree

func WriteDepTree(basedir string, l Lock, sm SourceManager, co CascadingPruneOptions, logger *log.Logger) error

WriteDepTree takes a basedir, a Lock and a RootPruneOptions and exports all the projects listed in the lock to the appropriate target location within basedir.

If the goal is to populate a vendor directory, basedir should be the absolute path to that vendor directory, not its parent (a project root, typically).

It requires a SourceManager to do the work. Prune options are read from the passed manifest.

Types

type CascadingPruneOptions added in v0.4.1

type CascadingPruneOptions struct {
	DefaultOptions    PruneOptions
	PerProjectOptions map[ProjectRoot]PruneOptionSet
}

CascadingPruneOptions is a set of rules for pruning a dependency tree.

The DefaultOptions are the global default pruning rules, expressed as a single PruneOptions bitfield. These global rules will cascade down to individual project rules, unless superseded.

func (CascadingPruneOptions) PruneOptionsFor added in v0.4.1

func (o CascadingPruneOptions) PruneOptionsFor(pr ProjectRoot) PruneOptions

PruneOptionsFor returns the PruneOptions bits for the given project, indicating which pruning rules should be applied to the project's code.

It computes the cascade from default to project-specific options (if any) on the fly.

type Constraint

type Constraint interface {
	fmt.Stringer

	// ImpliedCaretString converts the Constraint to a string in the same manner
	// as String(), but treats the empty operator as equivalent to ^, rather
	// than =.
	//
	// In the same way that String() is the inverse of NewConstraint(), this
	// method is the inverse of NewSemverConstraintIC().
	ImpliedCaretString() string

	// Matches indicates if the provided Version is allowed by the Constraint.
	Matches(Version) bool

	// MatchesAny indicates if the intersection of the Constraint with the
	// provided Constraint would yield a Constraint that could allow *any*
	// Version.
	MatchesAny(Constraint) bool

	// Intersect computes the intersection of the Constraint with the provided
	// Constraint.
	Intersect(Constraint) Constraint
	// contains filtered or unexported methods
}

A Constraint provides structured limitations on the versions that are admissible for a given project.

As with Version, it has a private method because the gps's internal implementation of the problem is complete, and the system relies on type magic to operate.

func Any

func Any() Constraint

Any returns a constraint that will match anything.

func NewSemverConstraint

func NewSemverConstraint(body string) (Constraint, error)

NewSemverConstraint attempts to construct a semver Constraint object from the input string.

If the input string cannot be made into a valid semver Constraint, an error is returned.

func NewSemverConstraintIC

func NewSemverConstraintIC(body string) (Constraint, error)

NewSemverConstraintIC attempts to construct a semver Constraint object from the input string, defaulting to a caret, ^, when no operator is specified. Put differently, ^ is the default operator for NewSemverConstraintIC, while = is the default operator for NewSemverConstraint.

If the input string cannot be made into a valid semver Constraint, an error is returned.

type CouldNotCreateLockError

type CouldNotCreateLockError struct {
	Path string
	Err  error
}

CouldNotCreateLockError describe failure modes in which creating a SourceMgr did not succeed because there was an error while attempting to create the on-disk lock file.

func (CouldNotCreateLockError) Error

func (e CouldNotCreateLockError) Error() string

type DeductionErrs

type DeductionErrs map[string]error

DeductionErrs maps package import path to errors occurring during deduction.

func (DeductionErrs) Error

func (e DeductionErrs) Error() string

type Lock

type Lock interface {
	// The hash digest of inputs to gps that resulted in this lock data.
	InputsDigest() []byte

	// Projects returns the list of LockedProjects contained in the lock data.
	Projects() []LockedProject
}

Lock represents data from a lock file (or however the implementing tool chooses to store it) at a particular version that is relevant to the satisfiability solving process.

In general, the information produced by gps on finding a successful solution is all that would be necessary to constitute a lock file, though tools can include whatever other information they want in their storage.

type LockDiff

type LockDiff struct {
	HashDiff *StringDiff
	Add      []LockedProjectDiff
	Remove   []LockedProjectDiff
	Modify   []LockedProjectDiff
}

LockDiff is the set of differences between an existing lock file and an updated lock file. Fields are only populated when there is a difference, otherwise they are empty.

func DiffLocks

func DiffLocks(l1 Lock, l2 Lock) *LockDiff

DiffLocks compares two locks and identifies the differences between them. Returns nil if there are no differences.

type LockedProject

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

LockedProject is a single project entry from a lock file. It expresses the project's name, one or both of version and underlying revision, the network URI for accessing it, the path at which it should be placed within a vendor directory, and the packages that are used in it.

func NewLockedProject

func NewLockedProject(id ProjectIdentifier, v Version, pkgs []string) LockedProject

NewLockedProject creates a new LockedProject struct with a given ProjectIdentifier (name and optional upstream source URL), version. and list of packages required from the project.

Note that passing a nil version will cause a panic. This is a correctness measure to ensure that the solver is never exposed to a version-less lock entry. Such a case would be meaningless - the solver would have no choice but to simply dismiss that project. By creating a hard failure case via panic instead, we are trying to avoid inflicting the resulting pain on the user by instead forcing a decision on the Analyzer implementation.

func (LockedProject) Eq

func (lp LockedProject) Eq(lp2 LockedProject) bool

Eq checks if two LockedProject instances are equal.

func (LockedProject) Ident

func (lp LockedProject) Ident() ProjectIdentifier

Ident returns the identifier describing the project. This includes both the local name (the root name by which the project is referenced in import paths) and the network name, where the upstream source lives.

func (LockedProject) Packages

func (lp LockedProject) Packages() []string

Packages returns the list of packages from within the LockedProject that are actually used in the import graph. Some caveats:

  • The names given are relative to the root import path for the project. If the root package itself is imported, it's represented as ".".
  • Just because a package path isn't included in this list doesn't mean it's safe to remove - it could contain C files, or other assets, that can't be safely removed.
  • The slice is not a copy. If you need to modify it, copy it first.

func (LockedProject) String

func (lp LockedProject) String() string

func (LockedProject) Version

func (lp LockedProject) Version() Version

Version assembles together whatever version and/or revision data is available into a single Version.

type LockedProjectDiff

type LockedProjectDiff struct {
	Name     ProjectRoot
	Source   *StringDiff
	Version  *StringDiff
	Branch   *StringDiff
	Revision *StringDiff
	Packages []StringDiff
}

LockedProjectDiff contains the before and after snapshot of a project reference. Fields are only populated when there is a difference, otherwise they are empty.

func DiffProjects

func DiffProjects(lp1 LockedProject, lp2 LockedProject) *LockedProjectDiff

DiffProjects compares two projects and identifies the differences between them. Returns nil if there are no differences

type Manifest

type Manifest interface {
	// Returns a list of project-level constraints.
	DependencyConstraints() ProjectConstraints
}

Manifest represents manifest-type data for a project at a particular version. The constraints expressed in a manifest determine the set of versions that are acceptable to try for a given project.

Expressing a constraint in a manifest does not guarantee that a particular dependency will be present. It only guarantees that if packages in the project specified by the dependency are discovered through static analysis of the (transitive) import graph, then they will conform to the constraint.

This does entail that manifests can express constraints on projects they do not themselves import. This is by design, but its implications are complex. See the gps docs for more information: https://github.com/sdboyer/gps/wiki

type PairedVersion

type PairedVersion interface {
	Version

	// Revision returns the immutable Revision that identifies this Version.
	Revision() Revision

	// Unpair returns the surface-level UnpairedVersion that half of the pair.
	//
	// It does NOT modify the original PairedVersion
	Unpair() UnpairedVersion
	// contains filtered or unexported methods
}

PairedVersion represents a normal Version, but paired with its corresponding, underlying Revision.

type ProjectAnalyzer

type ProjectAnalyzer interface {
	// Perform analysis of the filesystem tree rooted at path, with the
	// root import path importRoot, to determine the project's constraints, as
	// indicated by a Manifest and Lock.
	//
	// Note that an error will typically cause the solver to treat the analyzed
	// version as unusable. As such, an error should generally only be returned
	// if the code tree is somehow malformed, but not if the implementor's
	// expected files containing Manifest and Lock data are merely absent.
	DeriveManifestAndLock(path string, importRoot ProjectRoot) (Manifest, Lock, error)

	// Info reports this project analyzer's info.
	Info() ProjectAnalyzerInfo
}

A ProjectAnalyzer is responsible for analyzing a given path for Manifest and Lock information. Tools relying on gps must implement one.

type ProjectAnalyzerInfo

type ProjectAnalyzerInfo struct {
	Name    string
	Version int
}

ProjectAnalyzerInfo indicates a ProjectAnalyzer's name and version.

func (ProjectAnalyzerInfo) String

func (p ProjectAnalyzerInfo) String() string

String returns a string like: "<name>.<decimal version>"

type ProjectConstraint

type ProjectConstraint struct {
	Ident      ProjectIdentifier
	Constraint Constraint
}

A ProjectConstraint combines a ProjectIdentifier with a Constraint. It indicates that, if packages contained in the ProjectIdentifier enter the depgraph, they must do so at a version that is allowed by the Constraint.

type ProjectConstraints

type ProjectConstraints map[ProjectRoot]ProjectProperties

ProjectConstraints is a map of projects, as identified by their import path roots (ProjectRoots) to the corresponding ProjectProperties.

They are the standard form in which Manifests declare their required dependency properties - constraints and network locations - as well as the form in which RootManifests declare their overrides.

type ProjectIdentifier

type ProjectIdentifier struct {
	ProjectRoot ProjectRoot
	Source      string
}

A ProjectIdentifier provides the name and source location of a dependency. It is related to, but differs in two key ways from, a plain import path.

First, ProjectIdentifiers do not identify a single package. Rather, they encompass the whole tree of packages, including tree's root - the ProjectRoot. In gps' current design, this ProjectRoot almost always corresponds to the root of a repository.

Second, ProjectIdentifiers can optionally carry a Source, which identifies where the underlying source code can be located on the network. These can be either a full URL, including protocol, or plain import paths. So, these are all valid data for Source:

github.com/sdboyer/gps
github.com/fork/gps
git@github.com:sdboyer/gps
https://github.com/sdboyer/gps

With plain import paths, network addresses are derived purely through an algorithm. By having an explicit network name, it becomes possible to, for example, transparently substitute a fork for the original upstream source repository.

Note that gps makes no guarantees about the actual import paths contained in a repository aligning with ImportRoot. If tools, or their users, specify an alternate Source that contains a repository with incompatible internal import paths, gps' solving operations will error. (gps does no import rewriting.)

Also note that if different projects' manifests report a different Source for a given ImportRoot, it is a solve failure. Everyone has to agree on where a given import path should be sourced from.

If Source is not explicitly set, gps will derive the network address from the ImportRoot using a similar algorithm to that utilized by `go get`.

func (ProjectIdentifier) Less

Less compares by ProjectRoot then normalized Source.

func (ProjectIdentifier) String

func (i ProjectIdentifier) String() string

type ProjectProperties

type ProjectProperties struct {
	Source     string
	Constraint Constraint
}

ProjectProperties comprise the properties that can be attached to a ProjectRoot.

In general, these are declared in the context of a map of ProjectRoot to its ProjectProperties; they make little sense without their corresponding ProjectRoot.

type ProjectRoot

type ProjectRoot string

ProjectRoot is the topmost import path in a tree of other import paths - the root of the tree. In gps' current design, ProjectRoots have to correspond to a repository root (mostly), but their real purpose is to identify the root import path of a "project", logically encompassing all child packages.

Projects are a crucial unit of operation in gps. Constraints are declared by a project's manifest, and apply to all packages in a ProjectRoot's tree. Solving itself mostly proceeds on a project-by-project basis.

Aliasing string types is usually a bit of an anti-pattern. gps does it here as a means of clarifying API intent. This is important because Go's package management domain has lots of different path-ish strings floating around:

 actual directories:
	/home/sdboyer/go/src/github.com/sdboyer/gps/example
 URLs:
	https://github.com/sdboyer/gps
 import paths:
	github.com/sdboyer/gps/example
 portions of import paths that refer to a package:
	example
 portions that could not possibly refer to anything sane:
	github.com/sdboyer
 portions that correspond to a repository root:
	github.com/sdboyer/gps

While not a panacea, having ProjectRoot allows gps to clearly indicate via the type system when a path-ish string must have particular semantics.

type PruneOptionSet added in v0.4.1

type PruneOptionSet struct {
	NestedVendor   uint8
	UnusedPackages uint8
	NonGoFiles     uint8
	GoTests        uint8
}

PruneOptionSet represents trinary distinctions for each of the types of prune rules (as expressed via PruneOptions): nested vendor directories, unused packages, non-go files, and go test files.

The three-way distinction is between "none", "true", and "false", represented by uint8 values of 0, 1, and 2, respectively.

This trinary distinction is necessary in order to record, with full fidelity, a cascading tree of pruning values, as expressed in CascadingPruneOptions; a simple boolean cannot delineate between "false" and "none".

type PruneOptions

type PruneOptions uint8

PruneOptions represents the pruning options used to write the dependecy tree.

const (
	// PruneNestedVendorDirs indicates if nested vendor directories should be pruned.
	PruneNestedVendorDirs PruneOptions = 1 << iota
	// PruneUnusedPackages indicates if unused Go packages should be pruned.
	PruneUnusedPackages
	// PruneNonGoFiles indicates if non-Go files should be pruned.
	// Files matching licenseFilePrefixes and legalFileSubstrings are kept in
	// an attempt to comply with legal requirements.
	PruneNonGoFiles
	// PruneGoTestFiles indicates if Go test files should be pruned.
	PruneGoTestFiles
)

type Revision

type Revision string

A Revision represents an immutable versioning identifier.

func (Revision) ImpliedCaretString

func (r Revision) ImpliedCaretString() string

ImpliedCaretString follows the same rules as String(), but in accordance with the Constraint interface will always print a leading "=", as all Versions, when acting as a Constraint, act as exact matches.

func (Revision) Intersect

func (r Revision) Intersect(c Constraint) Constraint

Intersect computes the intersection of the Constraint with the provided Constraint. For Revisions, this can only be another, exactly equal Revision, or a PairedVersion whose underlying Revision is exactly equal.

func (Revision) Matches

func (r Revision) Matches(v Version) bool

Matches is the Revision acting as a constraint; it checks to see if the provided version is the same Revision as itself.

func (Revision) MatchesAny

func (r Revision) MatchesAny(c Constraint) bool

MatchesAny is the Revision acting as a constraint; it checks to see if the provided version is the same Revision as itself.

func (Revision) String

func (r Revision) String() string

String converts the Revision back into a string.

func (Revision) Type

func (r Revision) Type() VersionType

Type indicates the type of version - for revisions, "revision".

type RootManifest

type RootManifest interface {
	Manifest

	// Overrides returns a list of ProjectConstraints that will unconditionally
	// supersede any ProjectConstraint declarations made in either the root
	// manifest, or in any dependency's manifest.
	//
	// Overrides are a special control afforded only to root manifests. Tool
	// users should be encouraged to use them only as a last resort; they do not
	// "play well with others" (that is their express goal), and overreliance on
	// them can harm the ecosystem as a whole.
	Overrides() ProjectConstraints

	// IngoredPackages returns a pkgtree.IgnoredRuleset, which comprises a set
	// of import paths, or import path patterns, that are to be ignored during
	// solving. These ignored import paths can be within the root project, or
	// part of other projects. Ignoring a package means that both it and its
	// (unique) imports will be disregarded by all relevant solver operations.
	//
	// It is an error to include a package in both the ignored and required
	// sets.
	IgnoredPackages() *pkgtree.IgnoredRuleset

	// RequiredPackages returns a set of import paths to require. These packages
	// are required to be present in any solution. The list can include main
	// packages.
	//
	// It is meaningless to specify packages that are within the
	// PackageTree of the ProjectRoot (though not an error, because the
	// RootManifest itself does not report a ProjectRoot).
	//
	// It is an error to include a package in both the ignored and required
	// sets.
	RequiredPackages() map[string]bool
}

RootManifest extends Manifest to add special controls over solving that are only afforded to the root project.

type SimpleLock

type SimpleLock []LockedProject

SimpleLock is a helper for tools to easily describe lock data when they know that no hash, or other complex information, is available.

func (SimpleLock) InputsDigest

func (SimpleLock) InputsDigest() []byte

InputsDigest always returns an empty string for SimpleLock. This makes it useless as a stable lock to be written to disk, but still useful for some ephemeral purposes.

func (SimpleLock) Projects

func (l SimpleLock) Projects() []LockedProject

Projects returns the entire contents of the SimpleLock.

type SimpleManifest

type SimpleManifest struct {
	Deps ProjectConstraints
}

SimpleManifest is a helper for tools to enumerate manifest data. It's generally intended for ephemeral manifests, such as those Analyzers create on the fly for projects with no manifest metadata, or metadata through a foreign tool's idioms.

func (SimpleManifest) DependencyConstraints

func (m SimpleManifest) DependencyConstraints() ProjectConstraints

DependencyConstraints returns the project's dependencies.

type Solution

type Solution interface {
	Lock
	// The name of the ProjectAnalyzer used in generating this solution.
	AnalyzerName() string
	// The version of the ProjectAnalyzer used in generating this solution.
	AnalyzerVersion() int
	// The name of the Solver used in generating this solution.
	SolverName() string
	// The version of the Solver used in generating this solution.
	SolverVersion() int
	Attempts() int
}

A Solution is returned by a solver run. It is mostly just a Lock, with some additional methods that report information about the solve run.

type SolveParameters

type SolveParameters struct {
	// The path to the root of the project on which the solver should operate.
	// This should point to the directory that should contain the vendor/
	// directory.
	//
	// In general, it is wise for this to be under an active GOPATH, though it
	// is not (currently) required.
	//
	// A real path to a readable directory is required.
	RootDir string

	// The ProjectAnalyzer is responsible for extracting Manifest and
	// (optionally) Lock information from dependencies. The solver passes it
	// along to its SourceManager's GetManifestAndLock() method as needed.
	//
	// An analyzer is required.
	ProjectAnalyzer ProjectAnalyzer

	// The tree of packages that comprise the root project, as well as the
	// import path that should identify the root of that tree.
	//
	// In most situations, tools should simply pass the result of ListPackages()
	// directly through here.
	//
	// The ImportRoot property must be a non-empty string, and at least one
	// element must be present in the Packages map.
	RootPackageTree pkgtree.PackageTree

	// The root manifest. This contains all the dependency constraints
	// associated with normal Manifests, as well as the particular controls
	// afforded only to the root project.
	//
	// May be nil, but for most cases, that would be unwise.
	Manifest RootManifest

	// The root lock. Optional. Generally, this lock is the output of a previous
	// solve run.
	//
	// If provided, the solver will attempt to preserve the versions specified
	// in the lock, unless ToChange or ChangeAll settings indicate otherwise.
	Lock Lock

	// ToChange is a list of project names that should be changed - that is, any
	// versions specified for those projects in the root lock file should be
	// ignored.
	//
	// Passing ChangeAll has subtly different behavior from enumerating all
	// projects into ToChange. In general, ToChange should *only* be used if the
	// user expressly requested an upgrade for a specific project.
	ToChange []ProjectRoot

	// ChangeAll indicates that all projects should be changed - that is, any
	// versions specified in the root lock file should be ignored.
	ChangeAll bool

	// Downgrade indicates whether the solver will attempt to upgrade (false) or
	// downgrade (true) projects that are not locked, or are marked for change.
	//
	// Upgrading is, by far, the most typical case. The field is named
	// 'Downgrade' so that the bool's zero value corresponds to that most
	// typical case.
	Downgrade bool

	// TraceLogger is the logger to use for generating trace output. If set, the
	// solver will generate informative trace output as it moves through the
	// solving process.
	TraceLogger *log.Logger
	// contains filtered or unexported fields
}

SolveParameters hold all arguments to a solver run.

Only RootDir and RootPackageTree are absolutely required. A nil Manifest is allowed, though it usually makes little sense.

Of these properties, only the Manifest and RootPackageTree are (directly) incorporated in memoization hashing.

type Solver

type Solver interface {
	// HashInputs hashes the unique inputs to this solver, returning the hash
	// digest. It is guaranteed that, if the resulting digest is equal to the
	// digest returned from a previous Solution.InputHash(), that that Solution
	// is valid for this Solver's inputs.
	//
	// In such a case, it may not be necessary to run Solve() at all.
	HashInputs() []byte

	// Solve initiates a solving run. It will either abort due to a canceled
	// Context, complete successfully with a Solution, or fail with an
	// informative error.
	//
	// It is generally not allowed that this method be called twice for any
	// given solver.
	Solve(context.Context) (Solution, error)

	// Name returns a string identifying the particular solver backend.
	//
	// Different solvers likely have different invariants, and likely will not
	// have the same result sets for any particular inputs.
	Name() string

	// Version returns an int indicating the version of the solver of the given
	// Name(). Implementations should change their reported version ONLY when
	// the logic is changed in such a way that substantially changes the result
	// set that is possible for a substantial subset of likely inputs.
	//
	// "Substantial" is an imprecise term, and it is used intentionally. There
	// are no easy, general ways of subdividing constraint solving problems such
	// that one can know, a priori, the full impact that subtle algorithmic
	// changes will have on possible result sets. Consequently, we have to fall
	// back on coarser, intuition-based reasoning as to whether a change is
	// large enough that it is likely to be broadly user-visible.
	//
	// This is acceptable, because this value is not used programmatically by
	// the solver in any way. Rather, it is intend for implementing tools to
	// use as a coarse signal to users about compatibility between their tool's
	// version and the current data, typically via persistence to a Lock.
	// Changes to the version number reported should be weighed between
	// confusing teams by having two members' tools continuously rolling back
	// each others' chosen Solutions for no apparent reason, and annoying teams
	// by changing the number for changes so remote that warnings about solver
	// version mismatches become meaningless.
	//
	// Err on the side of caution.
	//
	// Chronology is the only implication of the ordering - that lower version
	// numbers were published before higher numbers.
	Version() int
}

A Solver is the main workhorse of gps: given a set of project inputs, it performs a constraint solving analysis to develop a complete Solution, or else fail with an informative error.

If a Solution is found, an implementing tool may persist it - typically into a "lock file" - and/or use it to write out a directory tree of dependencies, suitable to be a vendor directory, via CreateVendorTree.

func Prepare

func Prepare(params SolveParameters, sm SourceManager) (Solver, error)

Prepare readies a Solver for use.

This function reads and validates the provided SolveParameters. If a problem with the inputs is detected, an error is returned. Otherwise, a Solver is returned, ready to hash and check inputs or perform a solving run.

type SourceManager

type SourceManager interface {
	// SourceExists checks if a repository exists, either upstream or in the
	// SourceManager's central repository cache.
	SourceExists(ProjectIdentifier) (bool, error)

	// SyncSourceFor will attempt to bring all local information about a source
	// fully up to date.
	SyncSourceFor(ProjectIdentifier) error

	// ListVersions retrieves a list of the available versions for a given
	// repository name.
	ListVersions(ProjectIdentifier) ([]PairedVersion, error)

	// RevisionPresentIn indicates whether the provided Version is present in
	// the given repository.
	RevisionPresentIn(ProjectIdentifier, Revision) (bool, error)

	// ListPackages parses the tree of the Go packages at or below root of the
	// provided ProjectIdentifier, at the provided version.
	ListPackages(ProjectIdentifier, Version) (pkgtree.PackageTree, error)

	// GetManifestAndLock returns manifest and lock information for the provided
	// root import path.
	//
	// gps currently requires that projects be rooted at their repository root,
	// necessitating that the ProjectIdentifier's ProjectRoot must also be a
	// repository root.
	GetManifestAndLock(ProjectIdentifier, Version, ProjectAnalyzer) (Manifest, Lock, error)

	// ExportProject writes out the tree of the provided import path, at the
	// provided version, to the provided directory.
	ExportProject(context.Context, ProjectIdentifier, Version, string) error

	// DeduceProjectRoot takes an import path and deduces the corresponding
	// project/source root.
	DeduceProjectRoot(ip string) (ProjectRoot, error)

	// SourceURLsForPath takes an import path and deduces the set of source URLs
	// that may refer to a canonical upstream source.
	// In general, these URLs differ only by protocol (e.g. https vs. ssh), not path
	SourceURLsForPath(ip string) ([]*url.URL, error)

	// Release lets go of any locks held by the SourceManager. Once called, it is
	// no longer safe to call methods against it; all method calls will
	// immediately result in errors.
	Release()

	// InferConstraint tries to puzzle out what kind of version is given in a string -
	// semver, a revision, or as a fallback, a plain tag
	InferConstraint(s string, pi ProjectIdentifier) (Constraint, error)
}

A SourceManager is responsible for retrieving, managing, and interrogating source repositories. Its primary purpose is to serve the needs of a Solver, but it is handy for other purposes, as well.

gps's built-in SourceManager, SourceMgr, is intended to be generic and sufficient for any purpose. It provides some additional semantics around the methods defined here.

type SourceManagerConfig

type SourceManagerConfig struct {
	Cachedir       string      // Where to store local instances of upstream sources.
	Logger         *log.Logger // Optional info/warn logger. Discards if nil.
	DisableLocking bool        // True if the SourceManager should NOT use a lock file to protect the Cachedir from multiple processes.
}

SourceManagerConfig holds configuration information for creating SourceMgrs.

type SourceMgr

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

SourceMgr is the default SourceManager for gps.

There's no (planned) reason why it would need to be reimplemented by other tools; control via dependency injection is intended to be sufficient.

func NewSourceManager

func NewSourceManager(c SourceManagerConfig) (*SourceMgr, error)

NewSourceManager produces an instance of gps's built-in SourceManager.

The returned SourceManager aggressively caches information wherever possible. If tools need to do preliminary work involving upstream repository analysis prior to invoking a solve run, it is recommended that they create this SourceManager as early as possible and use it to their ends. That way, the solver can benefit from any caches that may have already been warmed.

gps's SourceManager is intended to be threadsafe (if it's not, please file a bug!). It should be safe to reuse across concurrent solving runs, even on unrelated projects.

func (*SourceMgr) Cachedir

func (sm *SourceMgr) Cachedir() string

Cachedir returns the location of the cache directory.

func (*SourceMgr) DeduceProjectRoot

func (sm *SourceMgr) DeduceProjectRoot(ip string) (ProjectRoot, error)

DeduceProjectRoot takes an import path and deduces the corresponding project/source root.

Note that some import paths may require network activity to correctly determine the root of the path, such as, but not limited to, vanity import paths. (A special exception is written for gopkg.in to minimize network activity, as its behavior is well-structured)

func (*SourceMgr) ExportProject

func (sm *SourceMgr) ExportProject(ctx context.Context, id ProjectIdentifier, v Version, to string) error

ExportProject writes out the tree of the provided ProjectIdentifier's ProjectRoot, at the provided version, to the provided directory.

func (*SourceMgr) GetManifestAndLock

func (sm *SourceMgr) GetManifestAndLock(id ProjectIdentifier, v Version, an ProjectAnalyzer) (Manifest, Lock, error)

GetManifestAndLock returns manifest and lock information for the provided ProjectIdentifier, at the provided Version. The work of producing the manifest and lock is delegated to the provided ProjectAnalyzer's DeriveManifestAndLock() method.

func (*SourceMgr) HandleSignals

func (sm *SourceMgr) HandleSignals(sigch chan os.Signal)

HandleSignals sets up logic to handle incoming signals with the goal of shutting down the SourceMgr safely.

Calling code must provide the signal channel, and is responsible for calling signal.Notify() on that channel.

Successive calls to HandleSignals() will deregister the previous handler and set up a new one. It is not recommended that the same channel be passed multiple times to this method.

SetUpSigHandling() will set up a handler that is appropriate for most use cases.

func (*SourceMgr) InferConstraint

func (sm *SourceMgr) InferConstraint(s string, pi ProjectIdentifier) (Constraint, error)

InferConstraint tries to puzzle out what kind of version is given in a string. Preference is given first for branches, then semver constraints, then plain tags, and then revisions.

func (*SourceMgr) ListPackages

func (sm *SourceMgr) ListPackages(id ProjectIdentifier, v Version) (pkgtree.PackageTree, error)

ListPackages parses the tree of the Go packages at and below the ProjectRoot of the given ProjectIdentifier, at the given version.

func (*SourceMgr) ListVersions

func (sm *SourceMgr) ListVersions(id ProjectIdentifier) ([]PairedVersion, error)

ListVersions retrieves a list of the available versions for a given repository name.

The list is not sorted; while it may be returned in the order that the underlying VCS reports version information, no guarantee is made. It is expected that the caller either not care about order, or sort the result themselves.

This list is always retrieved from upstream on the first call. Subsequent calls will return a cached version of the first call's results. if upstream is not accessible (network outage, access issues, or the resource actually went away), an error will be returned.

func (*SourceMgr) Release

func (sm *SourceMgr) Release()

Release lets go of any locks held by the SourceManager. Once called, it is no longer safe to call methods against it; all method calls will immediately result in errors.

func (*SourceMgr) RevisionPresentIn

func (sm *SourceMgr) RevisionPresentIn(id ProjectIdentifier, r Revision) (bool, error)

RevisionPresentIn indicates whether the provided Revision is present in the given repository.

func (*SourceMgr) SourceExists

func (sm *SourceMgr) SourceExists(id ProjectIdentifier) (bool, error)

SourceExists checks if a repository exists, either upstream or in the cache, for the provided ProjectIdentifier.

func (*SourceMgr) SourceURLsForPath

func (sm *SourceMgr) SourceURLsForPath(ip string) ([]*url.URL, error)

SourceURLsForPath takes an import path and deduces the set of source URLs that may refer to a canonical upstream source. In general, these URLs differ only by protocol (e.g. https vs. ssh), not path

func (*SourceMgr) StopSignalHandling

func (sm *SourceMgr) StopSignalHandling()

StopSignalHandling deregisters any signal handler running on this SourceMgr.

It's normally not necessary to call this directly; it will be called as needed by Release().

func (*SourceMgr) SyncSourceFor

func (sm *SourceMgr) SyncSourceFor(id ProjectIdentifier) error

SyncSourceFor will ensure that all local caches and information about a source are up to date with any network-acccesible information.

The primary use case for this is prefetching.

func (*SourceMgr) UseDefaultSignalHandling

func (sm *SourceMgr) UseDefaultSignalHandling()

UseDefaultSignalHandling sets up typical os.Interrupt signal handling for a SourceMgr.

type StringDiff

type StringDiff struct {
	Previous string
	Current  string
}

StringDiff represents a modified string value. * Added: Previous = nil, Current != nil * Deleted: Previous != nil, Current = nil * Modified: Previous != nil, Current != nil * No Change: Previous = Current, or a nil pointer

func (*StringDiff) String

func (diff *StringDiff) String() string

type UnpairedVersion

type UnpairedVersion interface {
	Version
	// Pair takes the underlying Revision that this UnpairedVersion corresponds
	// to and unites them into a PairedVersion.
	Pair(Revision) PairedVersion
	// contains filtered or unexported methods
}

UnpairedVersion represents a normal Version, with a method for creating a VersionPair by indicating the version's corresponding, underlying Revision.

func NewBranch

func NewBranch(body string) UnpairedVersion

NewBranch creates a new Version to represent a floating version (in general, a branch).

func NewVersion

func NewVersion(body string) UnpairedVersion

NewVersion creates a Semver-typed Version if the provided version string is valid semver, and a plain/non-semver version if not.

type Version

type Version interface {
	Constraint

	// Indicates the type of version - Revision, Branch, Version, or Semver
	Type() VersionType
}

Version represents one of the different types of versions used by gps.

Version composes Constraint, because all versions can be used as a constraint (where they allow one, and only one, version - themselves), but constraints are not necessarily discrete versions.

Version is an interface, but it contains private methods, which restricts it to gps's own internal implementations. We do this for the confluence of two reasons: the implementation of Versions is complete (there is no case in which we'd need other types), and the implementation relies on type magic under the hood, which would be unsafe to do if other dynamic types could be hiding behind the interface.

func VCSVersion

func VCSVersion(path string) (Version, error)

VCSVersion returns the current project version for an absolute path.

type VersionType

type VersionType uint8

VersionType indicates a type for a Version that conveys some additional semantics beyond that which is literally embedded on the Go type.

const (
	IsRevision VersionType = iota
	IsVersion
	IsSemver
	IsBranch
)

VersionTypes for the four major classes of version we deal with

Jump to

Keyboard shortcuts

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