Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go Modules: finding out right pseudo-version (vX.Y.Z-<timestamp>-<commit>) of required package

Tags:

go

go-modules

I am trying out Go modules. My project requires the libarary golang.org/x/net/html, so I defined this go.mod file:

module github.com/patrickbucher/prettyprint

require golang.org/x/net/html

And wrote this demo program to check if the dependency gets loaded upon compilation:

package main

import (
        "fmt"
        "log"
        "os"

        "golang.org/x/net/html"
)

func main() {
        doc, err := html.Parse(os.Stdin)
        if err != nil {
                log.Fatal(err)
        }
        fmt.Println(doc)
}

When I run go build, I get this error message:

go: errors parsing go.mod:
~/prettyprint/go.mod:3: usage: require module/path v1.2.3

Obviously, I missed the version number. But which one to take? I stumbled an article called Takig Go Modules for a Spin, where I found an example of a go.mod file containing references to golang.org/x packages:

module github.com/davecheney/httpstat

require (
        github.com/fatih/color v1.5.0
        github.com/mattn/go-colorable v0.0.9
        github.com/mattn/go-isatty v0.0.3
        golang.org/x/net v0.0.0-20170922011244-0744d001aa84
        golang.org/x/sys v0.0.0-20170922123423-429f518978ab
        golang.org/x/text v0.0.0-20170915090833-1cbadb444a80
)

The author is using version strings like v0.0.0-20170922011244-0744d001aa84, consisting of the semver indication v0.0.0, a timestamp and something that looks like a git commit ID.

How do I figure out those version strings? I guess those golang.org/x packages will be versioned according to semantic versioning at some point, but to really trying out go mod, I need to figure out those now.

like image 288
Patrick Bucher Avatar asked Sep 09 '18 06:09

Patrick Bucher


People also ask

How do I update my Go module version?

You can upgrade or downgrade a dependency module by using Go tools to discover available versions, then add a different version as a dependency. To discover new versions use the go list command as described in Discovering available updates.

What are Go modules?

Go modules commonly consist of one project or library and contain a collection of Go packages that are then released together. Go modules solve many problems with GOPATH , the original system, by allowing users to put their project code in their chosen directory and specify versions of dependencies for each module.

Where does Go mod download packages?

A Module is a collection of Go packages stored in a file tree under $GOPATH/pkg folder with a go. mod file at its root.

What is a pseudo version of a module?

Pseudo-versions with the same base version prefix sort chronologically. The go command performs several checks to ensure that module authors have control over how pseudo-versions are compared with other versions and that pseudo-versions refer to revisions that are actually part of a module’s commit history.

How do I find the version of a go module?

GoVersion = string | ident . /* valid release version; see above */ A use adds a module on disk to the set of main modules in a workspace. Its argument is a relative path to the directory containing the module’s go.mod file. A use directive does not add modules contained in subdirectories of its argument directory.

What are pseudo-versions in $base/$module/@V/list in go?

In Go 1.12 and earlier, the go command considered pseudo-versions in $base/$module/@v/list to be pre-release versions, but this is no longer true since Go 1.13. A module proxy must always serve the same content for successful responses for $base/$module/$version.mod and $base/$module/$version.zip queries.

How do I know which module provides the package in go?

See Compatibility with non-module repositories for more information. When the go command loads a package using a package path, it needs to determine which module provides the package. The go command starts by searching the build list for modules with paths that are prefixes of the package path.


3 Answers

Update

This command is the better solution to adding the replace command to go.mod rather than doing it manually using git that I initially posted:

go mod edit -replace github.com/docker/docker=github.com/docker/engine@ea84732a7725

produces a similar result but instead of using a pseudo-version, it finds the tagged engine version.

replace github.com/docker/docker => github.com/docker/engine v17.12.0-ce-rc1.0.20191113042239-ea84732a7725+incompatible

Alternatively, include a tagged docker version.

go mod edit -replace github.com/docker/[email protected]=github.com/docker/engine@ea84732a7725

for

replace github.com/docker/docker v1.13.1 => github.com/docker/engine v17.12.0-ce-rc1.0.20191113042239-ea84732a7725+incompatible

Thanks to @Shivam010 on Medium


Original, deprecated answer

Here's how I did it.

Checkout the repository on the desired branch/tag. e.g.

git clone -b v19.03.5 [email protected]:docker/engine.git

Then

cd engine
TZ=UTC git --no-pager show \
  --quiet \
  --abbrev=12 \
  --date='format-local:%Y%m%d%H%M%S' \
  --format="%cd-%h"

And i get

20191113042239-ea84732a7725

For use in go.mod as

replace github.com/docker/docker v1.13.1 => github.com/docker/engine v0.0.0-20191113042239-ea84732a7725
like image 167
nicerobot Avatar answered Oct 15 '22 10:10

nicerobot


A version of the form v0.0.0-20180906233101-161cd47e91fd means that there are no tagged versions on the git repository. So go mod generates one based on the latest commit time and the prefix of the commit hash.

To get a correct go.mod file start out using the following command (assuming go 1.11):

go mod init yourmodulename

Or create an empty go.mod file that just contains the following:

module yourmodulename

then run go mod tidy, this will find all dependencies, add the missing and remove the unused dependencies.

like image 37
nijm Avatar answered Oct 15 '22 10:10

nijm


The author is using version strings like v0.0.0-20170922011244-0744d001aa84, consisting of the semver indication v0.0.0, a timestamp and something that looks like a git commit ID.

How do I figure out those version strings?

You never need to manually figure out those complex version strings, which are called pseudo-versions.

Daily workflow

Your typical daily workflow can be:

  • Add import statements to your .go code as needed.
  • Standard commands like go build, go test, or go mod tidy will automatically add new dependencies as needed to satisfy imports (updating go.mod and downloading the new dependencies). By default, the @latest version of a new direct dependency will be used.
  • When needed, more specific versions of dependencies can be chosen with commands such as:
    • go get [email protected]
    • go get foo@e3702bed2
    • go get foo@latest
    • go get foo@branch
    • or by editing go.mod directly.

Note that you did not need to come up with a pseudo-version on your own in any of those examples, even when requesting a specific commit (e.g., @e3702bed2), or the latest commit on a branch (e.g., @master).

When do I see pseudo-versions in my go.mod?

If you end up with a version that resolves to a valid semver tag with a leading v such as v1.2.3 or v1.2.4-beta-1, then that semver tag will be recorded in your go.mod file. If the version does not have a valid semver tag, then it will be instead recorded with as a pseudo-version in your go.mod file, such as v0.0.0-20171006230638-a6e239ea1c69, which includes a version section, a commit timestamp, and a commit hash.

In your particular case, golang.org/x/net/html does not have any semver tags, which means if you do go get golang.org/x/net/html@latest, or go get golang.org/x/net/html@0744d001aa84, or just do go build after first including import "golang.org/x/net/html" in your .go file, then golang.org/x/net/html will be recorded in your go.mod as a pseudo-version, but note that you did not need to figure out the complex string yourself (because the go command translates modules queries such as go get golang.org/x/net/html@0744d001aa84 into the corresponding pseudo-version when needed, and records the result in your go.mod).

Why was the pseudo-version format chosen?

The pseudo-version format helps provide a simple total ordering across all versions based on standard semver ordering, which makes it easier to reason about what commit will be considered "later" than another commit, or whether an actual semver tag is considered "later" than a separate commit.

Controlling dependency versions

You can read more about all of the above in the "How to Upgrade and Downgrade Dependencies" section of the Go Modules wiki, which also contains additional links into the official documentation.

like image 27
thepudds Avatar answered Oct 15 '22 09:10

thepudds