libyear

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2024 License: GPL-3.0 Imports: 21 Imported by: 0

README

go-libyear

Calculate Go module's libyear!

Install

Use pre-built binaries from the latest release or install with Go:

go install github.com/nieomylnieja/go-libyear/cmd/go-libyear@latest

It can also be built directly from this repository:

git clone https://github.com/nieomylnieja/go-libyear.git
cd go-libyear
make build
./bin/go-libyear ./go.mod

Usage

go-libyear can be used both as a CLI and Go library. The CLI usage is also documented in usage.txt and accessed through go-libyear --help.

Basic usage:

$ go-libyear /path/to/go.mod
package                             version  date        latest   latest_date  libyear
github.com/nieomylnieja/go-libyear           2023-11-06                        2.41
github.com/pkg/errors               v0.8.1   2019-01-03  v0.9.1   2020-01-14   1.03
github.com/urfave/cli/v2            v2.20.0  2022-10-14  v2.25.7  2023-06-14   0.67
golang.org/x/mod                    v0.12.0  2023-06-21  v0.14.0  2023-10-25   0.35
golang.org/x/sync                   v0.3.0   2023-06-01  v0.5.0   2023-10-11   0.36

Calculated metrics

Libyear

What exactly is libyear? Quoting and paraphrasing libyear.com:

Libyear is a simple measure of software dependency freshness.
It is a single number telling you how up-to-date your dependencies are.
Example: pkg/errors v0.8.1 (June 2019) is 1 libyear behind v0.9.0 (June 2020).

Libyear is the default metric calculated by the program.

Example:

Current Current release Latest Latest release Libyear
v1.45.1 2022-10-11 v2.0.5 2023-10-11 1
v1.46.0 2022-12-04 v2.0.5 2023-10-11 0.85
v2.0.0 2023-10-01 v2.0.5 2022-10-11 0.03
Number of releases

Dependencies with short release cycles are penalized by this measurement, as the version sequence distance is relatively high compared to other dependencies.

Example:

Versions
v1.45.1
v1.45.2
v1.46.0
v2.0.0
v2.0.1
Current Latest Delta
v1.45.1 v2.0.5 5
Version number delta

Version delta is a tuple (x,y,z) where:

  • x is major version
  • y is minor version
  • z is patch version

Only highest-order version number is taken into consideration.

Example:

Current Latest Delta
v1.45.1 v2.0.5 (1,0,0)
v1.45.1 v1.47.5 (0,2,0)
v1.45.1 v.45.5 (0,0,4)
Results manipulation
Flag Explanation
--releases Count number of releases between current and latest.
--versions Calculate version number delta between current and latest.
--indirect Include indirect dependencies in the results.
--skip-fresh Skip up-to-date dependencies from the results.
--find-latest-major Use next, greater than or equal to v2 version as the latest.
Module sources
Source Flag Example
File path default ~/my-project/go.mod
URL --url https://raw.githubusercontent.com/nieomylnieja/go-libyear/main/go.mod
Module path --pkg github.com/nieomylnieja/go-libyear@latest
Output formats
Format Flag
Table default
JSON --json
CSV --csv
Historical data

In order to calculate the metrics in a given point in time, use --age-limit flag. Example:

go-libyear --age-limit 2022-10-01T12:00:00Z ./go.mod

The latest version for each package will be appointed as the latest version of the package before or at the provided timestamp.

The flag works any other flag. If using a script to extract a history of the calculated metrics, it is recommended to use --cache flag as well.

Caching

go-libyear ships with a built-in caching mechanism. It is disabled by default but can be enabled and adjusted with the following flags:

Flag Explanation
--cache Enable caching.
--cache-file-path Use the specified file for caching.
--vcs-cache-dir Use custom cache path for VCS modules.

Go versioning

By default go-libyear will fetch the latest version for the current major version adhering to the following rules:

  • If the current major version is equal to 0.x.x and there's version 1.x.x available, set the latest to 1.x.x.
  • If the current major is equal to or greater than 1.x.x, set the latest to 1.x.x.

If you wish to always set the next major version as the latest, you can use the --find-latest-major (short -M) flag. This flag enforces the following rules:

  • If the current major is equal to or greater than x.x.x, set the latest to the latest (by semver) available version.

  • If the latest major version is greater than the current major and the current version has been published after the first version of the latest major, the libyear is calculated as a difference between the first version of the latest major and latest major version.

    Example:

    Current version is 1.21.9 (2024-02-01), latest is 2.0.5 (2024-01-19);
    1.21.9 was a security fix, it still means we're some time behind v2;
    2.0.0 was released on 2024-01-02, this means we're 17 days (2024-01-19 - 2024-01-02) behind the latest v2, despite the fact that we've updated to the latest security patch for v1.

If you wish to not compensate for such cases and leave libyear as is (it won't be ever negative, we round it to 0 if negative), use the --no-libyear-compensation flag.

The modules reference states that:

If the module is released at major version 2 or higher, the module path must end with a major version suffix like /v2.

This is however not always the case, some older projects, usually pre-module, might not adhere to that. The aforementioned flag also works with such scenarios.

Caveats

Accessing private repositories

Currently the default mode of execution only supports git VCS and GitHub source. To access all private modules use --go-list flag. It will instruct the program to utilize go list command instead of GOPROXY API.

Using --go-list flag

If --go-list flag is provided, go-libyear will used go list command to fetch information about modules. Specifically it runs go list -m -mod=readonly. If the program is executed in a project containing a go.mod which go.sum file is out of sync, it will drop the following error:

updates to go.sum needed, disabled by -mod=readonly

Due to that it is advised to use stick with default modules information provider.

Development

CLI application is tested with bats framework. The tests are defined in test folder. Only core calculations are covered by unit tests, main paths are tested through CLI tests.

Acknowledgements

Inspired directly by SE Radio episode 587. Further reading through https://libyear.com/ and mimicking https://github.com/jaredbeck/libyear-bundler capabilities.

All the concepts and theory is based on or directly quoted from Measuring Dependency Freshness in Software Systems.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CSVOutput

type CSVOutput struct{}

func (CSVOutput) Send

func (p CSVOutput) Send(summary Summary) error

type Command

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

func (Command) Run

func (c Command) Run(ctx context.Context) error

type CommandBuilder

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

func NewCommandBuilder

func NewCommandBuilder(source Source, output Output) CommandBuilder

func (CommandBuilder) Build

func (b CommandBuilder) Build() (*Command, error)

func (CommandBuilder) WithAgeLimit added in v0.4.0

func (b CommandBuilder) WithAgeLimit(limit time.Time) CommandBuilder

func (CommandBuilder) WithCache

func (b CommandBuilder) WithCache(cacheFilePath string) CommandBuilder

func (CommandBuilder) WithFallbackVersionsGetter

func (b CommandBuilder) WithFallbackVersionsGetter(getter VersionsGetter) CommandBuilder

func (CommandBuilder) WithModulesRepo

func (b CommandBuilder) WithModulesRepo(repo ModulesRepo) CommandBuilder

func (CommandBuilder) WithOptions

func (b CommandBuilder) WithOptions(opts ...Option) CommandBuilder

func (CommandBuilder) WithVCSRegistry added in v0.3.0

func (b CommandBuilder) WithVCSRegistry(registry *VCSRegistry) CommandBuilder

type FileSource

type FileSource struct {
	Path string
}

func (FileSource) Read

func (s FileSource) Read() ([]byte, error)

type JSONOutput

type JSONOutput struct{}

func (JSONOutput) Send

func (j JSONOutput) Send(summary Summary) error

type ModulesRepo

type ModulesRepo interface {
	VersionsGetter
	GetModFile(path string, version *semver.Version) ([]byte, error)
	GetInfo(path string, version *semver.Version) (*internal.Module, error)
	GetLatestInfo(path string) (*internal.Module, error)
}

type Option

type Option int
const (
	OptionShowReleases          Option = 1 << iota // 1
	OptionShowVersions                             // 2
	OptionSkipFresh                                // 4
	OptionIncludeIndirect                          // 8
	OptionUseGoList                                // 16
	OptionFindLatestMajor                          // 32
	OptionNoLibyearCompensation                    // 32
)

type Output

type Output interface {
	Send(summary Summary) error
}

type PkgSource

type PkgSource struct {
	Pkg string
	// contains filtered or unexported fields
}

func (*PkgSource) Read

func (p *PkgSource) Read() ([]byte, error)

func (*PkgSource) SetModulesRepo

func (p *PkgSource) SetModulesRepo(repo ModulesRepo)

func (*PkgSource) SetVCSRegistry added in v0.3.0

func (p *PkgSource) SetVCSRegistry(registry *VCSRegistry)

type Source

type Source interface {
	Read() ([]byte, error)
}

type StdinSource

type StdinSource struct{}

func (StdinSource) Read

func (s StdinSource) Read() ([]byte, error)

type Summary

type Summary struct {
	Modules []*internal.Module
	Main    *internal.Module
	// contains filtered or unexported fields
}

type TableOutput

type TableOutput struct{}

func (TableOutput) Send

func (p TableOutput) Send(summary Summary) error

type URLSource

type URLSource struct {
	HTTP   http.Client
	RawURL string
}

func (URLSource) Read

func (s URLSource) Read() ([]byte, error)

type VCSHandler added in v0.3.0

type VCSHandler interface {
	ModulesRepo
	// CanHandle reports whether the vcs can handle the given path.
	CanHandle(path string) (bool, error)
	// Name reports the name of the VCS system.
	Name() string
}

VCSHandler is an interface that can be implemented by specifc VCS handler.

type VCSRegistry added in v0.3.0

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

VCSRegistry implements [command.ModulesRepo] and delegates handling of an invoked method to the registered VCS handler which supports the given path.

func NewVCSRegistry added in v0.3.0

func NewVCSRegistry(cacheDir string) *VCSRegistry

func (*VCSRegistry) GetHandler added in v0.3.0

func (v *VCSRegistry) GetHandler(path string) (ModulesRepo, error)

GetHandler returns the VCS handler which supports the given path. nolint: ireturn

func (*VCSRegistry) IsPrivate added in v0.3.0

func (v *VCSRegistry) IsPrivate(path string) bool

type VersionsGetter

type VersionsGetter interface {
	GetVersions(path string) ([]*semver.Version, error)
}

Directories

Path Synopsis
cmd
mocks
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.

Jump to

Keyboard shortcuts

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