tagcmp

package module
v0.0.0-...-eb3e775 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2023 License: GPL-2.0 Imports: 4 Imported by: 5

README

tagcmp

Build Status Go Report Card Go Reference

Comparison operations for image tags. Because registries aren't doing this for us 🙄

This library is helpful if you're aiming to use only "stable" and "semver-like" tags and want to be able to do things like compare them, find which tags are more recent, sort them and other types of comparisons. This is a best-effort implementation which follows the wisdom of Renovate.

Docker doesn't really have versioning, instead it supports "tags" and these are usually used by Docker image authors as a form of versioning ... It's pretty "wild west" for tagging and not always compliant with SemVer.

The Renovate implementation allows image tags to be automatically upgraded, is the only show in town, apparently. This library follows that implementation quite closely.

Example

package main

import (
	"fmt"
	"sort"

	"coopcloud.tech/tagcmp"
)

func main() {
	rawTags := []string{
		"1.7.1",
		"1.9.4-linux-arm64",
		"1.14.2-rootless",
		"linux-arm64-rootless",
		"1.14.1-rootless",
		"1.12.4-linux-amd64",
		"1.14.0-rootless",
	}

	tag, err := tagcmp.Parse("1.14.0-rootless")
	if err != nil {
		panic(err)
	}

	var compatible []tagcmp.Tag
	for _, rawTag := range rawTags {
		parsed, _ := tagcmp.Parse(rawTag) // skips unsupported tags
		if tag.IsCompatible(parsed) {
			compatible = append(compatible, parsed)
		}
	}

	sort.Sort(tagcmp.ByTagAsc(compatible))

	fmt.Println(compatible)
}

Output:

[1.14.0-rootless 1.14.1-rootless 1.14.2-rootless]

Types of versions supported

// semver
"5",
"2.6",
"4.3.5",

// semver with 'v'
"v1",
"v2.3",
"v1.0.2",

// semver with suffix
"6-alpine",
"6.2-alpine",
"6.2.1-alpine",

// semver with sufix and 'v'
"v6-alpine",
"v6.2-alpine",
"v6.2.1-alpine",
"v6.2.1-alpine",

// semver with multiple suffix values
"6.2.1-alpine-foo",

// semver with multiple suffix values and 'v'
"v6.2.1-alpine-foo",

Types of versions not supported

Please note, we could support some of these versions if people really need them to be supported. Some tags are using a unique format which we could support by implementing a very specific parser for (e.g. ParseMinioTag, ParseZncTag). For now, this library tries to provide a Parse function which handles more general cases. Please open an issue, change sets are welcome.

// empty
"",

// patametrized
"${MAILU_VERSION:-master}",
"${PHP_VERSION}-fpm-alpine3.13",

// commit hash like
"0a1b2c3d4e5f6a7b8c9d0a1b2c3d4e5f6a7b8c9d",

// numeric
"20191109",
"e02267d",

// not semver
"3.0.6.0",
"r1295",
"version-r1070",

// prerelease
"3.7.0b1",
"3.8.0b1-alpine",

// multiple versions
"5.36-backdrop-php7.4",
"v1.0.5_3.4.0",
"v1.0.5_3.4.0_openid-sso",

// tz based
"RELEASE.2021-04-22T15-44-28Z",

// only text
"alpine",
"latest",
"master",

// multiple - delimters
"apache-debian-1.8-prod",
"version-znc-1.8.2",

License

GPLv3+

Who's using it?

Documentation

Overview

Package tagcmp provides image tag comparison operations.

Index

Constants

This section is empty.

Variables

View Source
var CommitHashPattern = "^[a-f0-9]{7,40}$"

CommitHashPattern matches commit-like hash tags

View Source
var DotPattern = "([0-9]+)\\.([0-9]+)"

DotPattern matches tags which contain multiple versions

View Source
var EmptyPattern = "^$"

EmptyPattern matches when tags are missing

View Source
var ParametrizedPattern = "\\${.+}"

ParametrizedPattern matches when tags are parametrized

View Source
var StringPattern = "^[a-zA-Z]+$"

StringPattern matches when tags are only made up of alphabetic characters

Functions

func IsParsable

func IsParsable(tag string) bool

IsParsable determines if a tag is supported by this library

Types

type ByTagAsc

type ByTagAsc []Tag

ByTagAsc sorts tags in ascending order where the last element is the latest tag.

func (ByTagAsc) Len

func (t ByTagAsc) Len() int

func (ByTagAsc) Less

func (t ByTagAsc) Less(i, j int) bool

func (ByTagAsc) Swap

func (t ByTagAsc) Swap(i, j int)

type ByTagDesc

type ByTagDesc []Tag

ByTagDesc sorts tags in descending order where the first element is the latest tag.

func (ByTagDesc) Len

func (t ByTagDesc) Len() int

func (ByTagDesc) Less

func (t ByTagDesc) Less(i, j int) bool

func (ByTagDesc) Swap

func (t ByTagDesc) Swap(i, j int)

type Tag

type Tag struct {
	Major        string `json:",omitempty"` // major semver part
	Minor        string `json:",omitempty"` // minor semver part
	MissingMinor bool   // whether or not the minor semver part was left out
	Patch        string `json:",omitempty"` // patch semver part
	MissingPatch bool   // whether or not he patch semver part was left out
	Suffix       string // tag suffix (e.g. "-alpine") [would be release candidate in semver]
	UsesV        bool   // whether or not the tag uses the "v" prefix
	Metadata     string // metadata: what's after + and after the first "-"
}

func Parse

func Parse(tag string) (Tag, error)

Parse converts an image tag into a structured data format. It aims to to support the general case of tags which are "semver-like" and/or stable and parseable by heuristics. Image tags follow no formal specification and therefore this is a best-effort implementation. Examples of tags this function can parse are: "5", "5.2", "v4", "v5.3.6", "4-alpine", "v3.2.1-debian".

func (Tag) Equals

func (t Tag) Equals(tag Tag) bool

Equals tests Tag equality

func (Tag) IsCompatible

func (t Tag) IsCompatible(tag Tag) bool

IsCompatible determines if two tags can be compared together

func (Tag) IsGreaterThan

func (t Tag) IsGreaterThan(tag Tag) bool

IsGreaterThan tests if a tag is greater than another. There are some tag-isms to take into account here, shorter is bigger (i.e. 2.1 > 2.1.1 == true, 2 > 2.1 == true).

func (Tag) IsLessThan

func (t Tag) IsLessThan(tag Tag) bool

IsLessThan tests if a tag is less than another. There are some tag-isms to take into account here, shorter is bigger (i.e. 2.1 < 2.1.1 == false, 2 < 2.1 == false).

func (Tag) IsUpgradeCompatible

func (pin Tag) IsUpgradeCompatible(upTag Tag) bool

IsUpgradeCompatible chekcs if upTag is compatible with a pinned version tag. I.e. pinning to 22-fpm should return true if upTag is 22.2.0-fpm but not 22.2.0-alpine or 23.0.0-fpm

func (Tag) String

func (t Tag) String() string

String formats a Tag correctly in string representation

func (Tag) UpgradeDelta

func (curTag Tag) UpgradeDelta(newTag Tag) (TagDelta, error)

UpgradeDelta returns a TagDelta object which is the difference between an old and new tag It can contain negative numbers if comparing with an older tag.

type TagDelta

type TagDelta struct {
	Major int // major semver difference
	Minor int // minor semver difference
	Patch int // patch semver difference
}

func ParseDelta

func ParseDelta(delta string) (TagDelta, error)

ParseDelta converts a tag difference in the format of X, X.Y or X.Y.Z where X, Y, Z are positive or negative integers or 0

func (TagDelta) String

func (t TagDelta) String() string

func (TagDelta) UpgradeType

func (d TagDelta) UpgradeType() int

UpgradeType takes exit from UpgradeElemene and returns a numeric representation of upgrade or downgrade 1/-1: patch 2/-2: minor 4/-4: major 0: no change

Jump to

Keyboard shortcuts

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