api

package
v0.0.0-...-b090dc0 Latest Latest
Warning

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

Go to latest
Published: May 29, 2024 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package api defines the API that the command-line interface uses to communicate with UPM language backends.

Index

Constants

View Source
const (
	// By default, UPM assumes that each language backend
	// implements the add/remove, lock, and install operations as
	// totally separate steps: add/remove only modifies the
	// specfile, lock only updates the lockfile from the specfile,
	// and install only installs packages from the lockfile.
	QuirksNone Quirks = 0

	// This constant indicates that the package manager doesn't
	// have any concept of a lockfile, so the backend implements
	// its own. In this case, the functioning of add/remove is
	// unchanged. However, lock does nothing (and the backend must
	// not implement it), while install installs packages directly
	// from the specfile and then generates a lockfile from what
	// was installed.
	QuirksNotReproducible = 1 << iota

	// This constant indicates that add/remove also executes lock
	// subsequently, so it doesn't need to be run afterwards.
	QuirksAddRemoveAlsoLocks

	// This constant indicates that add/remove also executes lock
	// and install subsequently, so they don't need to be run
	// afterwards. If specified, then QuirksAddRemoveAlsoLocks
	// also must be specified.
	QuirksAddRemoveAlsoInstalls

	// This constant indicates that lock also executes install
	// subsequently, so it doesn't need to be run afterwards.
	QuirksLockAlsoInstalls

	// This constant indicates that remove cannot be performed
	// without a lockfile.
	QuirkRemoveNeedsLockfile
)

Constants of type Quirks, used to denote whether a language backend follows the expected abstractions of UPM or if it needs special treatment.

Variables

View Source
var HttpClient = &UpmHttpClient{}

Functions

This section is empty.

Types

type LanguageBackend

type LanguageBackend struct {
	// The name of the language backend. This is matched against
	// the value of the --lang argument on the command line. The
	// format is a sequence of one or more tokens separated by
	// hyphens, as in ruby-bundler or python-python3-poetry. Each
	// token gets matched separately against the --lang argument,
	// so that python-python3-poetry will match against python or
	// python3 or poetry or python-poetry. The name must be unique
	// among all language backends.
	//
	// This field is mandatory.
	Name string

	// An alias for the backend, useful for backwards compatibility
	// when renaming backends.
	Alias string

	// The filename of the specfile, e.g. "pyproject.toml" for
	// Poetry.
	//
	// This field is mandatory.
	Specfile string

	// An optional function to analyze the specfile to determine compatibility
	IsSpecfileCompatible func(fullPath string) (bool, error)

	// The filename of the lockfile, e.g. "poetry.lock" for
	// Poetry.
	Lockfile string

	// Check to see if we think we can run at all
	IsAvailable func() bool

	// List of filename globs that match against files written in
	// this programming language, e.g. "*.py" for Python. These
	// should not include any slashes, because they may be matched
	// in any subdirectory.
	//
	// FilenamePatterns is used for two things: language backend
	// autodetection (if matching files exist in the project
	// directory, the project is autodetected for this backend,
	// subject to being overridden by another heuristic), and
	// regexp searches for dependency guessing.
	//
	// This field is mandatory.
	FilenamePatterns []string

	// QuirksNone if the language backend conforms to the core
	// abstractions of UPM, and some bitwise disjunction of the
	// Quirks constant values otherwise.
	//
	// This field is optional, and defaults to QuirksNone.
	Quirks Quirks

	// Function that normalizes packages as they come in as CLI args
	//
	// This function is optional, defaulting to split(" ", 2)
	NormalizePackageArgs func(args []string) map[PkgName]PkgCoordinates

	// Function that normalizes a package name. This is used to
	// prevent duplicate packages getting added to the specfile.
	// For example, in Python the package names "flask" and
	// "Flask" are the same, so all package names are lowercased
	// before comparison.
	//
	// This field is optional.
	NormalizePackageName func(name PkgName) PkgName

	// Return the path (relative to the project directory) in
	// which packages are installed. The path need not exist.
	GetPackageDir func() string

	// Apply a sensible heuristic for sorting search results
	// if we know we want to surface some packages over others.
	SortPackages func(query string, packages []PkgInfo) []PkgInfo

	// Search for packages using an online index. The query may
	// contain any characters, including whitespace. Return a list
	// of search results, which can be of any length. (It will be
	// truncated by the command-line interface.) If the search
	// fails, terminate the process. If it successfully returns no
	// results, return an empty slice.
	//
	// This field is mandatory.
	Search func(query string) []PkgInfo

	// Retrieve information about a package from an online index.
	// If the package doesn't exist, return a zero struct.
	//
	// This field is mandatory.
	Info func(PkgName) PkgInfo

	// Add packages to the specfile. The map is guaranteed to have
	// at least one package, and all of the packages are
	// guaranteed to not already be in the specfile (according to
	// ListSpecfile). The specfile is *not* guaranteed to exist
	// already. The specs may be empty, in which case default
	// specs should be generated (for example, specifying the
	// latest version or newer). This method must create the
	// specfile if it does not exist already. Additional
	// information needed to create the specfile can be passed
	// as well. If a significant amount of additional info is
	// required for initalizing specfiles, we can break that out
	// to a seperate step.
	//
	// If QuirksAddRemoveAlsoInstalls, then also lock and install.
	// In this case this method must also create the lockfile if
	// it does not exist already.
	//
	// This field is mandatory.
	Add func(context.Context, map[PkgName]PkgSpec, string)

	// Remove packages from the specfile. The map is guaranteed to
	// have at least one package, and all of the packages are
	// guaranteed to already be in the specfile (according to
	// ListSpecfile). The specfile is guaranteed to exist already.
	// This method may not delete the specfile or lockfile.
	//
	// If QuirksAddRemoveAlsoInstalls, then also lock and install.
	// In this case this method must also create the lockfile if
	// it does not exist already.
	//
	// This field is mandatory.
	Remove func(context.Context, map[PkgName]bool)

	// Generate the lockfile from the specfile. The specfile is
	// guaranteed to already exist. This method must create the
	// lockfile if it does not exist already.
	//
	// If QuirksLockAlsoInstalls, then also install.
	//
	// This field is mandatory, unless QuirksNotReproducible in
	// which case this field *may* not be specified.
	Lock func(context.Context)

	// Install packages from the lockfile. The specfile and
	// lockfile are guaranteed to already exist, unless
	// QuirksNotReproducible in which case only the specfile is
	// guaranteed to exist.
	//
	// This field is mandatory.
	Install func(context.Context)

	// List the packages in the specfile. Names and specs should
	// be returned in a format suitable for the Add method. The
	// specfile is guaranteed to exist already.
	//
	// This field is mandatory.
	ListSpecfile func(mergeAllGroups bool) map[PkgName]PkgSpec

	// List the packages in the lockfile. Names should be returned
	// in a format suitable for the Add method. The lockfile is
	// guaranteed to exist already.
	ListLockfile func() map[PkgName]PkgVersion

	// Regexps used to determine if the Guess method really needs
	// to be invoked, or if its previous return value can be
	// re-used.
	//
	// These regexps should match imports, requires, or whatever
	// is analogous for the language. They are run against every
	// file in the project directory and its subdirectories,
	// subject to FilenamePatterns and util.IgnoredPaths. If a
	// regexp has no capture groups, the entire match is used;
	// otherwise, the match of each of its capture groups is used.
	// The list of all matches from all regexps against all files
	// is aggregated and hashed, and this is used to determine if
	// Guess needs to be re-run. So, these regexps should be
	// written so that what they match will change whenever the
	// return value of Guess might change.
	//
	// This field is optional; if it is omitted, then Guess will
	// always be run without recourse to caching.
	GuessRegexps []*regexp.Regexp

	// Return a list of packages that are probably needed as
	// dependencies of the project. It is better to be safe than
	// sorry: only packages which are *definitely* project
	// dependencies should be returned. Names should be returned
	// in a format suitable for the Add method. There is no need
	// to eliminate packages already installed.
	//
	// The second value indicates whether the bare imports search
	// was fully successful. One reason why it might not be
	// successful is if there is a syntax error in the code. It is
	// important to get this right because a syntax error can
	// cause an entire file to be skipped. Then if the error is
	// fixed later, the GuessRegexps may return the same results,
	// causing UPM to re-use the existing Guess return value
	// (which is now wrong).
	//
	// This field is mandatory.
	Guess func(ctx context.Context) (map[string][]PkgName, bool)

	// Installs system dependencies into replit.nix for supported
	// languages.
	InstallReplitNixSystemDependencies func(context.Context, []PkgName)
}

LanguageBackend is the core abstraction of UPM. It represents an implementation of all the core package management functionality of UPM, for a specific programming language and package manager. For example, python-python3-poetry and python-python2-poetry would be different backends, as would python-python3-poetry and python-python3-pipenv.

Most of the fields of this struct are mandatory, and the Check method will panic at UPM startup if they are not provided. Not all language backends necessarily need to implement all operations; in this case, the relevant functions should call util.NotImplemented, which will cause UPM to exit with an appropriate error message. (The limitation should be noted in the backend feature matrix in the README.)

Make sure to update the Check method when adding/removing fields from this struct.

func (*LanguageBackend) QuirkRemoveNeedsLockfile

func (b *LanguageBackend) QuirkRemoveNeedsLockfile() bool

QuirkRemoveNeedsLockfile returns true if the language backend cannot run lock without a valid lockfile

func (*LanguageBackend) QuirksDoesAddRemoveAlsoInstall

func (b *LanguageBackend) QuirksDoesAddRemoveAlsoInstall() bool

QuirksDoesAddRemoveAlsoInstall returns true if the language backend specifies QuirksAddRemoveAlsoInstalls, i.e. it also runs lock and install when running add or remove.

func (*LanguageBackend) QuirksDoesAddRemoveAlsoLock

func (b *LanguageBackend) QuirksDoesAddRemoveAlsoLock() bool

QuirksDoesAddRemoveAlsoLock returns true if the language backend specifies QuirksAddRemoveAlsoLocks, i.e. it also runs lock (but not install) when running add or remove.

func (*LanguageBackend) QuirksDoesAddRemoveNotAlsoInstall

func (b *LanguageBackend) QuirksDoesAddRemoveNotAlsoInstall() bool

QuirksDoesAddRemoveNotAlsoInstall returns true if the language backend does *not* specify QuirksAddRemoveAlsoInstalls, i.e. running add or remove does *not* also run install (though it might run lock; see QuirksDoesAddRemoveAlsoLock).

func (*LanguageBackend) QuirksDoesAddRemoveNotAlsoLock

func (b *LanguageBackend) QuirksDoesAddRemoveNotAlsoLock() bool

QuirksDoesAddRemoveNotAlsoLock returns true if the language backend does *not* specify QuirksAddRemoveAlsoLocks, i.e. running add or remove does *not* also run lock or install.

func (*LanguageBackend) QuirksDoesLockAlsoInstall

func (b *LanguageBackend) QuirksDoesLockAlsoInstall() bool

QuirksDoesLockAlsoInstall returns true if the language backend specifies QuirksLockAlsoInstalls, i.e. running lock also runs install.

func (*LanguageBackend) QuirksDoesLockNotAlsoInstall

func (b *LanguageBackend) QuirksDoesLockNotAlsoInstall() bool

QuirksDoesLockNotAlsoInstall returns true if the language backend does *not* specify QuirksLockAlsoInstalls, i.e. running lock does *not* also run install.

func (*LanguageBackend) QuirksIsNotReproducible

func (b *LanguageBackend) QuirksIsNotReproducible() bool

QuirksIsNotReproducible returns true if the language backend specifies QuirksNotReproducible, i.e. the package manager doesn't support a lockfile and one must be generated after install.

func (*LanguageBackend) QuirksIsReproducible

func (b *LanguageBackend) QuirksIsReproducible() bool

QuirksIsNotReproducible returns true if the language backend does *not* specify QuirksNotReproducible, i.e. the package manager supports a lockfile, as expected.

func (*LanguageBackend) Setup

func (b *LanguageBackend) Setup()

Setup panics if the given language backend does not specify all of the mandatory fields. It also assigns some defaults.

Honestly, this is a bit of a hack. We should really not expose the struct fields through the API directly, or at least we should have a builder function which can perform this normalization and validation.

type PkgCoordinates

type PkgCoordinates struct {
	Name string
	Spec PkgSpec
}

type PkgInfo

type PkgInfo struct {

	// The name of the package, e.g. "flask". Package names cannot
	// contain spaces.
	Name string `json:"name,omitempty" pretty:"Name"`

	// Brief description of the package, e.g. "A simple framework
	// for building complex web applications.".
	Description string `json:"description,omitempty" pretty:"Description"`

	// Version number of the package, e.g. "1.1.1". No particular
	// format is enforced.
	Version string `json:"version,omitempty" pretty:"Version"`

	// URL for the package's home page, e.g.
	// "https://palletsprojects.com/p/flask/".
	HomepageURL string `json:"homepageURL,omitempty" pretty:"Homepage"`

	// URL for the package's documentation, e.g.
	// "https://flask.palletsprojects.com/".
	DocumentationURL string `json:"documentationURL,omitempty" pretty:"Documentation"`

	// URL for the package's source code, e.g.
	// "https://github.com/pallets/flask".
	SourceCodeURL string `json:"sourceCodeURL,omitempty" pretty:"Source code"`

	// URL for the package's bug tracker, e.g.
	// "https://github.com/pallets/flask/issues".
	BugTrackerURL string `json:"bugTrackerURL,omitempty" pretty:"Bug tracker"`

	// Author of the package. Only one author is supported; if
	// there are multiple, we either pick one or simply
	// concatenate them into a single Author.
	Author string `json:"author,omitempty" pretty:"Author"`

	// License of the package. No particular format is enforced.
	// If the package has multiple licenses, we just concatenate
	// them into one string.
	License string `json:"license,omitempty" pretty:"License"`

	// Names of packages which are dependencies of this package.
	// There is no way to distinguish between a package that has
	// no dependencies and a package whose language backend did
	// not provide dependency information.
	Dependencies []string `json:"dependencies,omitempty" pretty:"Dependencies"`
}

PkgInfo is a general-purpose struct for representing package metadata. Any of the fields may be zeroed except for Name. Which fields are nonzero depends on the context and language backend.

Note: the PkgInfo struct is parsed with reflection in several places. It must have "json" and "pretty" tags, and the only allowed types are string and []string.

type PkgName

type PkgName string

PkgName represents the name of a package, e.g. "flask" for Python.

func (PkgName) Contains

func (n PkgName) Contains(other PkgName) bool

func (PkgName) HasPrefix

func (n PkgName) HasPrefix(other PkgName) bool

type PkgSpec

type PkgSpec string

PkgSpec represents a package version constraint, e.g. "^1.1" or ">= 1.1, <2.0" for Python.

type PkgVersion

type PkgVersion string

PkgVersion represents an exact package version, e.g. "1.1" or "1.0b2.post345.dev456" for Python.

type Quirks

type Quirks uint8

Quirks is a bitmask enum used to indicate how specific language backends behave differently from the core abstractions of UPM, and therefore require some different treatment by the command-line interface layer. See the constants of this type for more information.

type UpmHttpClient

type UpmHttpClient struct {
	http.Client
}

func (*UpmHttpClient) Do

func (c *UpmHttpClient) Do(req *http.Request) (*http.Response, error)

func (*UpmHttpClient) Get

func (c *UpmHttpClient) Get(url string) (*http.Response, error)

Jump to

Keyboard shortcuts

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