A Go (golang) library for parsing and verifying versions and version constraints.

Versioning Library for Go

Build Status GoDoc

go-version is a library for parsing versions and version constraints, and verifying versions against a set of constraints. go-version can sort a collection of versions properly, handles prerelease/beta versions, can increment versions, etc.

Versions used with go-version must follow SemVer.

Installation and Usage

Package documentation can be found on GoDoc.

Installation can be done with a normal go get:

$ go get github.com/hashicorp/go-version

Version Parsing and Comparison

v1, err := version.NewVersion("1.2")
v2, err := version.NewVersion("1.5+metadata")

// Comparison example. There is also GreaterThan, Equal, and just
// a simple Compare that returns an int allowing easy >=, <=, etc.
if v1.LessThan(v2) {
    fmt.Printf("%s is less than %s", v1, v2)
}

Version Constraints

v1, err := version.NewVersion("1.2")

// Constraints example.
constraints, err := version.NewConstraint(">= 1.0, < 1.4")
if constraints.Check(v1) {
	fmt.Printf("%s satisfies constraints %s", v1, constraints)
}

Version Sorting

versionsRaw := []string{"1.1", "0.7.1", "1.4-beta", "1.4", "2"}
versions := make([]*version.Version, len(versionsRaw))
for i, raw := range versionsRaw {
    v, _ := version.NewVersion(raw)
    versions[i] = v
}

// After this, the versions are properly sorted
sort.Sort(version.Collection(versions))

Issues and Contributing

If you find an issue with this library, please report an issue. If you'd like, we welcome any contributions. Fork this library and submit a pull request.

Owner
HashiCorp
Consistent workflows to provision, secure, connect, and run any infrastructure for any application.
HashiCorp
Comments
  • Feature/4ormoredots

    Feature/4ormoredots

    We ran into an issue where we had version strings with more specificity than 3 dots - We had to hack around it for the time being, but we'd ultimately appreciate if the library could natively support this.

    I took a crack at changing the Regex - It now supports an unlimited number of dots (because no matter what number I pick, someone else will need it bumped up by 1 some day), and altered a few things to support that (removing the cap on the segments slice), and added some comments around why some code still refers to 3 places (such as where you handle the segments being less than 3 in number, to support the default case of MAJOR.MINOR.PATCH).

    I also had to update the way the pessimistic comparator worked, to cover the additional edge cases present in the ability to support a greater specificity, as well as problems stemming from a jagged or uneven specificity between constraints and versions that I wasn't sure was explicitly being covered properly.

    I also added test cases to cover this.

    And I fixed an annoying nag from the go tools about replacing +=1 with ++, along with one about a syntax change to a range iterator.

  • Enable Sourcegraph

    Enable Sourcegraph

    I want to use Sourcegraph for go-version code search, browsing, and usage examples. Can an admin enable Sourcegraph for this repository? Just go to https://sourcegraph.com/github.com/hashicorp/go-version. (It should only take 30 seconds.)

    Thank you!

  • Adding support for an optional v prefix.

    Adding support for an optional v prefix.

    While not part of the SemVer 2.0.0 spec, the leading v is common. The release tags on SemVer itself have the v prefix. While not part of the spec they are optional in practice.

    In practice the SemVer libraries in other languages support the optional v prefix. For example, node-semver (used by npm and common for node.js projects doing versioning) allows the v prefix.

    Note, my hope is to use this to add support for SemVer to Glide

  • Prerelease constraint handling

    Prerelease constraint handling

    The logic in fbe76cf doesn't seem quite right. In particular, consider the use-case here: https://github.com/gesellix/couchdb-prometheus-exporter/blob/master/lib/couchdb-client.go#L52-L69 where we are checking if the server version is >= 2.0 and the reported version is e.g. 2.2.0-302bd8b. Clearly, in this scenario, the constraint check should return true (but it returns false). At the very least, the constraint needs to be able to opt-out of this mode.

    https://semver.org/#spec-item-11 implies that 2.2.0-pre >= 2.0 should be true: "1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." I can certainly see the case for not wanting to match pre-releases at all in certain circumstances.

  • Incorrect hanling of prerelease in constraints

    Incorrect hanling of prerelease in constraints

      v, _ := version.NewVersion("1.1.8-alpha1")
      if fast_check, err := version.NewConstraint(">= 1.1.7"); err == nil {
        if fast_check.Check(v) {
          fmt.Printf("OK: %v >= 1.1.7\n", v)
        } else {
          fmt.Printf("BUG: %v < 1.1.7\n", v)
        }
    

    Constraint check incorrectly returns false, which is obviously a bug.

  • Fix function comments based on best practices from Effective Go

    Fix function comments based on best practices from Effective Go

    Every exported function in a program should have a doc comment. The first sentence should be a summary that starts with the name being declared. From effective go.

    I generated this with CodeLingo and I'm keen to get some feedback, but this is automated so feel free to close it and just say "opt out" to opt out of future CodeLingo outreach PRs.

  • Adhere to semvar pre-release separator

    Adhere to semvar pre-release separator

    The semvar specifications X.Y.Z states Z must be non-negative integers. Without a hyphen - between the version and a pre-release identifier, Z is no longer an integer and is alphanumeric.

    A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: 1.9.0 -> 1.10.0 -> 1.11.0

    this is introducing a backwards incompatible change

  • Fixes version parsing for versions including ~

    Fixes version parsing for versions including ~

    • Closes https://github.com/terraform-providers/terraform-provider-mysql/issues/6
    • ~ comes in a lot of packages as part of the Debian package versioning: http://aalvarez.me/blog/posts/debian-package-versioning.html
  • github: Pin external GitHub Actions to hashes

    github: Pin external GitHub Actions to hashes

    The intention here is to reduce the security risk posed by the supply chain - i.e. externally maintained GitHub Actions.

    The expectation is that dependabot will continue to update these hashes as and when new versions become available.

  • Add tests about pre releases

    Add tests about pre releases

    I tried add tests and source code about prerelease following the semver precedence: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1

    But probably this patch needs revision.

    :cloud:

  • Bump actions/cache from 3.0.11 to 3.2.1

    Bump actions/cache from 3.0.11 to 3.2.1

    Bumps actions/cache from 3.0.11 to 3.2.1.

    Release notes

    Sourced from actions/cache's releases.

    v3.2.1

    What's Changed

    Full Changelog: https://github.com/actions/cache/compare/v3.2.0...v3.2.1

    v3.2.0

    What's Changed

    New Contributors

    Full Changelog: https://github.com/actions/cache/compare/v3...v3.2.0

    v3.2.0-beta.1

    What's Changed

    v3.1.0-beta.3

    What's Changed

    • Bug fixes for bsdtar fallback, if gnutar not available, and gzip fallback, if cache saved using old cache action, on windows.

    Full Changelog: https://github.com/actions/cache/compare/v3.1.0-beta.2...v3.1.0-beta.3

    ... (truncated)

    Changelog

    Sourced from actions/cache's changelog.

    Releases

    3.0.0

    • Updated minimum runner version support from node 12 -> node 16

    3.0.1

    • Added support for caching from GHES 3.5.
    • Fixed download issue for files > 2GB during restore.

    3.0.2

    • Added support for dynamic cache size cap on GHES.

    3.0.3

    • Fixed avoiding empty cache save when no files are available for caching. (issue)

    3.0.4

    • Fixed tar creation error while trying to create tar with path as ~/ home folder on ubuntu-latest. (issue)

    3.0.5

    • Removed error handling by consuming actions/cache 3.0 toolkit, Now cache server error handling will be done by toolkit. (PR)

    3.0.6

    • Fixed #809 - zstd -d: no such file or directory error
    • Fixed #833 - cache doesn't work with github workspace directory

    3.0.7

    • Fixed #810 - download stuck issue. A new timeout is introduced in the download process to abort the download if it gets stuck and doesn't finish within an hour.

    3.0.8

    • Fix zstd not working for windows on gnu tar in issues #888 and #891.
    • Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable SEGMENT_DOWNLOAD_TIMEOUT_MINS. Default is 60 minutes.

    3.0.9

    • Enhanced the warning message for cache unavailablity in case of GHES.

    3.0.10

    • Fix a bug with sorting inputs.
    • Update definition for restore-keys in README.md

    3.0.11

    • Update toolkit version to 3.0.5 to include @actions/core@^1.10.0
    • Update @actions/cache to use updated saveState and setOutput functions from @actions/core@^1.10.0

    3.1.0-beta.1

    • Update @actions/cache on windows to use gnu tar and zstd by default and fallback to bsdtar and zstd if gnu tar is not available. (issue)

    3.1.0-beta.2

    • Added support for fallback to gzip to restore old caches on windows.

    3.1.0-beta.3

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Bump actions/upload-artifact from 3.1.1 to 3.1.2

    Bump actions/upload-artifact from 3.1.1 to 3.1.2

    Bumps actions/upload-artifact from 3.1.1 to 3.1.2.

    Release notes

    Sourced from actions/upload-artifact's releases.

    v3.1.2

    • Update all @actions/* NPM packages to their latest versions- #374
    • Update all dev dependencies to their most recent versions - #375
    Commits
    • 0b7f8ab ci(github): update action/download-artifact from v1 to v3 (#312)
    • 013d2b8 Create devcontainer for codespaces + update all dev dependencies (#375)
    • 055b8b3 Bump Actions NPM dependencies (#374)
    • 7a5d483 ci(github): update action/checkout from v2 to v3 (#315)
    • e0057a5 README: Bump actions/checkout to v3 (#352)
    • 7fe6c13 Update to latest actions/publish-action (#363)
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Bump actions/checkout from 3.2.0 to 3.3.0

    Bump actions/checkout from 3.2.0 to 3.3.0

    Bumps actions/checkout from 3.2.0 to 3.3.0.

    Release notes

    Sourced from actions/checkout's releases.

    v3.3.0

    What's Changed

    New Contributors

    Full Changelog: https://github.com/actions/checkout/compare/v3.2.0...v3.3.0

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Bump actions/cache from 3.0.11 to 3.2.2

    Bump actions/cache from 3.0.11 to 3.2.2

    Bumps actions/cache from 3.0.11 to 3.2.2.

    Release notes

    Sourced from actions/cache's releases.

    v3.2.2

    What's Changed

    New Contributors

    Full Changelog: https://github.com/actions/cache/compare/v3.2.1...v3.2.2

    v3.2.1

    What's Changed

    Full Changelog: https://github.com/actions/cache/compare/v3.2.0...v3.2.1

    v3.2.0

    What's Changed

    New Contributors

    ... (truncated)

    Changelog

    Sourced from actions/cache's changelog.

    Releases

    3.0.0

    • Updated minimum runner version support from node 12 -> node 16

    3.0.1

    • Added support for caching from GHES 3.5.
    • Fixed download issue for files > 2GB during restore.

    3.0.2

    • Added support for dynamic cache size cap on GHES.

    3.0.3

    • Fixed avoiding empty cache save when no files are available for caching. (issue)

    3.0.4

    • Fixed tar creation error while trying to create tar with path as ~/ home folder on ubuntu-latest. (issue)

    3.0.5

    • Removed error handling by consuming actions/cache 3.0 toolkit, Now cache server error handling will be done by toolkit. (PR)

    3.0.6

    • Fixed #809 - zstd -d: no such file or directory error
    • Fixed #833 - cache doesn't work with github workspace directory

    3.0.7

    • Fixed #810 - download stuck issue. A new timeout is introduced in the download process to abort the download if it gets stuck and doesn't finish within an hour.

    3.0.8

    • Fix zstd not working for windows on gnu tar in issues #888 and #891.
    • Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable SEGMENT_DOWNLOAD_TIMEOUT_MINS. Default is 60 minutes.

    3.0.9

    • Enhanced the warning message for cache unavailablity in case of GHES.

    3.0.10

    • Fix a bug with sorting inputs.
    • Update definition for restore-keys in README.md

    3.0.11

    • Update toolkit version to 3.0.5 to include @actions/core@^1.10.0
    • Update @actions/cache to use updated saveState and setOutput functions from @actions/core@^1.10.0

    3.1.0-beta.1

    • Update @actions/cache on windows to use gnu tar and zstd by default and fallback to bsdtar and zstd if gnu tar is not available. (issue)

    3.1.0-beta.2

    • Added support for fallback to gzip to restore old caches on windows.

    3.1.0-beta.3

    ... (truncated)

    Commits
    • 4723a57 Revert compression changes related to windows but keep version logging (#1049)
    • d1507cc Merge pull request #1042 from me-and/correct-readme-re-windows
    • 3337563 Merge branch 'main' into correct-readme-re-windows
    • 60c7666 save/README.md: Fix typo in example (#1040)
    • b053f2b Fix formatting error in restore/README.md (#1044)
    • 501277c README.md: remove outdated Windows cache tip link
    • c1a5de8 Upgrade codeql to v2 (#1023)
    • 9b0be58 Release compression related changes for windows (#1039)
    • c17f4bf GA for granular cache (#1035)
    • ac25611 docs: fix an invalid link in workarounds.md (#929)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Invalid semver versions are treated as

    Invalid semver versions are treated as "valid"

    As discovered by @rclark

    The semver spec is clear that build or prerelease identifiers MUST come after a -.

    Versions, such as the following

    1.2.-3
    1.2.03
    1.2
    1.2.3.4.5.6
    1.2.3.alpha
    1.2.3-preview.01
    1.2.3-preview..1
    1.2.3-preview!
    1.2.3+one+two
    1.2.3+one.2!
    

    are treated as "valid". I intentionally put valid in quotes because it's clear that the internal representation of such a version is wrong anyway. All the consumer can reliably do with that parsed version is to call String() on it to reconstruct the same (invalid) version. https://go.dev/play/p/hyp8ZZJQO8-

    The Readme of go-version explicitly says

    Versions used with go-version must follow SemVer.

    Therefore I would treat this as a bug.

  • Prerelease String shouldn't always be treated as lower version

    Prerelease String shouldn't always be treated as lower version

    Hey, I've found some irregularity where I have the following two versions for tcpdump 4.9.3-4ubuntu0.1 4.9.3

    Using the example code from the readme we get that 4.9.3 is greater than 4.9.3-4ubuntu0.1 even though that's not the case Link to playground to see my tests.

    I'm pretty sure that 4ubuntu0.1 doesn't mean it's a prerelease version and it's a widely used suffix. In other compiled binaries for ubuntu.

    I'm not sure what's the correct approach to solve this, but I was wondering whether we should narrow down the behavior when evaluating the prerelease string. WDYT?

  • Skipping some versions

    Skipping some versions

    Hello, I want to make a smooth migration for AWS provider from version 3 to 4.9.0 or newer https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade In Node.js world it is possible to define allowed version of a package like this: <1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0 So for my scenario Node.js-like version constraint would look like this: >= 3.0 < 4.0 || >= 4.9 < 5.0

    But when I try to do something similar for AWS provider in terraform this is the only working possibility >= 3.0, != 4.0, != 4.1, != 4.2, != 4.3, != 4.4, != 4.5, != 4.6, != 4.7, != 4.8, < 5.0 or am I missing something?

Related tags
Molecule is a Go library for parsing protobufs in an efficient and zero-allocation manner

Molecule Molecule is a Go library for parsing protobufs in an efficient and zero-allocation manner. The API is loosely based on this excellent Go JSON

Jan 5, 2023
Simple library to handle ANSI functions and parsing of color formatting strings

Emerald A basic color library for use in my Go projects, built on top of mgutz/ansi. Package ansi is a small, fast library to create ANSI colored stri

Oct 28, 2022
A library for parsing ANSI encoded strings
 A library for parsing ANSI encoded strings

Go ANSI Parser converts strings with ANSI escape codes into a slice of structs that represent styled text

Sep 20, 2022
A small & fast dependency-free library for parsing micro expressions.

MicroExpr A small & fast dependency-free library for parsing micro expressions. This library was originally built for use in templating languages (e.g

Nov 25, 2022
A utility library to do files/io/bytes processing/parsing in file-system or network.

goreader A utility library to do files/io/bytes processing/parsing in file-system or network. These features are really common to be implemented for a

Nov 1, 2021
This library provides an ASTERIX Frame(binary data) decoding/parsing(json,xml) capabilities for Go.

GoAsterix This library provides an ASTERIX Frame(binary data) decoding/parsing(json,xml) capabilities for Go. ASTERIX ASTERIX (All Purpose Structured

Dec 13, 2022
Golang source code parsing, usage like reflect package

gotype Golang source code parsing, usage like reflect package English 简体中文 Usage API Documentation Examples License Pouch is licensed under the MIT Li

Dec 9, 2022
Go-path - A helper package that provides utilities for parsing and using ipfs paths

go-path is a helper package that provides utilities for parsing and using ipfs paths

Jan 18, 2022
Small utility to allow simpler, quicker testing of parsing files in crowdsec

cs_parser_test Small utility to allow simpler, quicker testing of parsing files in crowdsec Usage $ sudo cs_parser_test -t syslog /var/log/mail.log N

Jul 13, 2021
Gene parsing package for Axie Infinity

agp Package agp is a gene parsing package for Axie Infinity. The name agp stands for "Axie Gene Parser" which decodes the hex representation of an Axi

Apr 18, 2022
Yet another semantic version incrementor and tagger for git

git-tag-inc Increments the version number and tags it. (You will need to push) Usage ./git-tag-inc [major] [minor] [release] [test] [uat] git-tag-in

Apr 30, 2022
yaml-patch is a version of Evan Phoenix's json-patch, which is an implementation of JSON Patch, directly transposed to YAML

yaml-patch yaml-patch is a version of Evan Phoenix's json-patch, which is an implementation of JavaScript Object Notation (JSON) Patch, directly trans

Jan 15, 2022
Goridge is high performance PHP-to-Golang codec library which works over native PHP sockets and Golang net/rpc package.
Goridge is high performance PHP-to-Golang codec library which works over native PHP sockets and Golang net/rpc package.

Goridge is high performance PHP-to-Golang codec library which works over native PHP sockets and Golang net/rpc package. The library allows you to call Go service methods from PHP with a minimal footprint, structures and []byte support.

Dec 28, 2022
A well tested and comprehensive Golang statistics library package with no dependencies.

Stats - Golang Statistics Package A well tested and comprehensive Golang statistics library / package / module with no dependencies. If you have any s

Dec 30, 2022
Cogger is a standalone binary and a golang library that reads an internally tiled geotiff

Cogger is a standalone binary and a golang library that reads an internally tiled geotiff (optionally with overviews and masks) and rewrites it

Dec 12, 2022
GoDynamic can load and run Golang dynamic library compiled by -buildmode=shared -linkshared

GoDynamic can load and run Golang dynamic library compiled by -buildmode=shared -linkshared How does it work? GoDynamic works like a dynamic

Sep 30, 2022
A golang library to validate and format swiss social security numbers

s3n is a golang library to validate and format swiss social security numbers (aka. AVS in french and AHV in german).

Nov 15, 2021
Govalid is a data validation library that can validate most data types supported by golang

Govalid is a data validation library that can validate most data types supported by golang. Custom validators can be used where the supplied ones are not enough.

Apr 22, 2022
Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs.

go-attr Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs. This package provides user frien

Dec 16, 2022