Package versioning in Golang - go

I have a question about package versioning in Golang projects.
After creating a tag, for example v1.0.0.
I can pull this tag using go get pkg_address/#v1.0.0 which is fine and works correctly.
But when I see Go packages in github I see that it's written in their installation section to install the package using pkg_address/v1.0.0.
In fact they are pulling a specific version without #.
And they even import packages in their code using pkg_address/v1 even though there is no directory called v1 in their project.
I get error if I install a specific tag without #.
Even after using pkg_address/#v1.0.0 my import paths don't change and I don't need to specify version in my import paths.
For example you install echo package using this command go get github.com/labstack/echo/v4 and you import the package using the v4 tag in your code and there is no v4 in the package directories.
How can I do versioning like github packages?
P.S. I'm using gitlab.

This is a module path naming convention and it applies to major versions higher than v1.
https://go.dev/ref/mod#module-path
If the module is released at major version 2 or higher, the module path MUST end with a major version suffix like /v2. This may or may
not be part of the subdirectory name. For example, the module with
path golang.org/x/repo/sub/v2 could be in the /sub or /sub/v2
subdirectory of the repository golang.org/x/repo.
https://go.dev/ref/mod#major-version-suffixes
Starting with major version 2, module paths MUST have a major
version suffix like /v2 that matches the major version. For example,
if a module has the path example.com/mod at v1.0.0, it must have the
path example.com/mod/v2 at version v2.0.0.
And echo's v4 module path can be found here.

Related

How to prevent from `go build` to update the latest version of modules

I am using an open source project called "yaegi" in a large project.
I need to use older version of "yaegi": v.0.8.11, so I modified the go.mod file and replaced:
github.com/traefik/yaegi v0.9.2 // indirect with github.com/containous/yaegi v0.8.11
But when I build the project, it starts to update all the modules and replace it back to the most updated version:
root#ubuntu:~/myproj1# go build main.go
go: finding module for package github.com/traefik/yaegi/stdlib/unsafe
go: finding module for package github.com/traefik/yaegi/stdlib
go: finding module for package github.com/traefik/yaegi/interp
go: found github.com/traefik/yaegi/interp in github.com/traefik/yaegi v0.9.2
How can I prevent it and keep it using the old version v.0.8.11?
I read that according to https://tip.golang.org/cmd/go/#hdr-Maintaining_module_requirements:
The go command itself automatically updates the go.mod file to maintain a standard formatting and the accuracy of require statements.
Any go command that finds an unfamiliar import will look up the module containing that import and add the latest version of that module to go.mod automatically. […]
Any go command can determine that a module requirement is missing and must be added […].
Maybe there is a way to bypass it?
//indirect in go.mod means that at least one of your other modules you’re importing is dependent on that version of module, therefore go build shall automatically update that module, no matter how you changed that line in your go.mod. In your case if you don’t want to use yaegi module of v0.9.2 you have to first get rid of other dependencies dependent on that module from your project, and then fix your go.mod to make your project require yaegi v0.8.11. You can just delete them or make them require earlier version of yaegi by using their older version or editing their source code. Also, instead of editing go.mod directly, I’d run something like go get -v github.com/containous/yaegi#0.8.11 to checkout to a specific version of a module.

How are versions of a sub module managed?

If a Go repository has a go.mod file in its root but also in a sub folder, how are versions of the submodule released?
By way of example, My team have been working with vault on our internal cli tool.
We have ended up using:
github.com/hashicorp/vault/api <-- has a go.mod
and
github.com/hashicorp/vault/commands <-- doesn't have a go.mod so inherits from github.com/hashicorp/vault
I am trying to update vault/api to the latest version 1.3.3:
github.com/hashicorp/vault v1.3.3
github.com/hashicorp/vault/api v1.3.3
The problem is I get:
go: github.com/hashicorp/vault/api#v1.3.3: reading github.com/hashicorp/vault/api/api/go.mod at revision api/v1.3.3: unknown revision api/v1.3.3
Which I think is caused by this root module and a conflict.
The versions of sub-modules aren't necessarily going lock-step with versions of parent modules. They should be treated as completely separate modules that just happen to live in the same repository / directory structure.
Check https://github.com/hashicorp/vault/releases for the official releases/tags -- Go supports hierarchical Git tags to mark versions of submodules. For example, while as of today the latest version of vault itself is 1.3.3, I only find vault/api at v1.0.4 (this is the latest tag with api/v1.0.4)
Just do a go get to get its latest version. You don't actually have to specify versions in go.mod when importing initially - the go tool will find the latest versions for you.
As an addition to answers above you can use go list command to see which versions of the module are available at the moment:
go list -m -versions github.com/hashicorp/vault/api
github.com/hashicorp/vault/api v1.0.1 v1.0.2 v1.0.3 v1.0.4 v1.1.0
Or use -u flag to see which (most recent) version you can upgrade to (if any):
go list -m -u github.com/hashicorp/vault/api
github.com/hashicorp/vault/api v1.0.3 [v1.1.0]
Where v1.0.3 is your current version, and [v1.1.0] is a most recent possible version.
go list docs
A new module version may be published by pushing a tag to the repository that contains the module source code. The tag is formed by concatenating two strings: a prefix and a version.
The version is the semantic import version for the release. It should be chosen by following the rules of semantic import versioning.
The prefix indicates where a module is defined within a repository. If the module is defined at the root of the repository, the prefix is empty, and the tag is just the version. However, in multi-module repositories, the prefix distinguishes versions for different modules. The prefix is the directory within the repository where the module is defined. If the repository follows the major subdirectory pattern described above, the prefix does not include the major version suffix.
For example, suppose we have a module example.com/repo/sub/v2, and we want to publish version v2.1.6. The repository root corresponds to example.com/repo, and the module is defined in sub/v2/go.mod within the repository. The prefix for this module is sub/. The full tag for this release should be sub/v2.1.6.
Defined here: https://github.com/golang/go/wiki/Modules#publishing-a-release
A Go Module is nothing but a collection of Go packages (Folders). Source files belonging to a package should be placed in separate folders of their own. It is a convention in Go to name this folder with the same name as the package.
Whenever you run the go get comment under any subfolders, it will update to current directory & any parent directory has a go.mod.

`go get` : unexpected end of JSON input

What version of Go are you using (go version)?
$ go version
v1.12
The module yiigo has tag v3.0.0, but when I run go get github.com/iiinsomnia/yiigo, it gets the v2.1.0 and when I run go get github.com/iiinsomnia/yiigo#v3.0.0, it shows: go get github.com/iiinsomnia/yiigo#v3.0.0: unexpected end of JSON input
The primary issue seems to be that the v3.0.0 version of iiinsomnia/yiigo is missing the required /v3 at the end of the module line in its go.mod file:
https://github.com/iiinsomnia/yiigo/blob/v3.0.0/go.mod#L1
module github.com/iiinsomnia/yiigo <<<< wrong, missing required /v3 at end
go 1.12
require (
github.com/go-sql-driver/mysql v1.4.1-0.20190217072658-972a708cf979
...
That has since been corrected.
Because it is now a proper v3 module, the go get command should include a /v3 before the #:
module github.com/iiinsomnia/yiigo/v3#v3.2.2
From the Go modules wiki:
If the module is version v2 or higher, the major version of the module must be included as a /vN at the end of the module paths used in go.mod files (e.g., module github.com/my/mod/v2, require github.com/my/mod/v2 v2.0.0) and in the package import path (e.g., import "github.com/my/mod/v2/mypkg").
Also, it looks like a related issue was opened, with the theory being that the odd "unexpected end of JSON input" error might have come from some proxy:
https://github.com/golang/go/issues/30494
A way I've accomplished this in the past is by using git tags- for your case this should work fine.
Steps:
go get -u github.com/iiinsomnia/yiigo
cd ~/go/src/github.com/iiinsomnia/yiigo
git tag
locate tag release version you'd like to install within list
git checkout v3.0.0
go install
This will overwrite the package previously installed in your GOPATH with a new one for the specific tag version you have checked out.
Note: there is likely a better way to do this since the release of go modules.
This related post also provides alternative solutions on how to retrieve a specific version of a project's source code that may lend some help.

Using modules, newly installed package cannot be referenced within project

go version go1.11.4 darwin/amd64
GOPATH has been unset but was previously set to $HOME/Development/go
Project path is under $HOME/code/
I’m able to successfully (no errors at least) install the go-sql-driver/mysql package using the command
go get github.com/go-sql-driver/mysql#v1
When I include a reference to that package in an import statement
import(
_ "github.com/go-sql-driver/mysql")
in my code I see the error
could not import github.com/go-sql-driver/mysql (can’t find import:
“github.com/go-sql-driver/mysql”)
I have run go mod init in my project root and it creates a go.mod file. When I run the go get command I see a require statement added to that file for the package. But it seems the files for the package get installed in the default $HOME/go directory (since I've unset GOPATH).
Should I be doing things differently so that my import statement can find the newly installed package? Using modules shouldn't all the packages be installed in the project path somewhere?
Should I be doing things differently so that my import statement can find the newly installed package?
No. With modules there is no need to install the packages upfront at all.
Using modules shouldn't all the packages be installed in the project path somewhere?
No. They get downloaded somewhere in some format and used from that location but they are not "installed" like in the old GOPATH variant of go get.
Show output of go env and what go mod vendor produces.
I'm pretty sure I was doing things wrong. I was able to resolve this after referencing the following closely the steps documented at golang modules wiki. The summary is that there is no need to "install" a package via 'go get'. Instead simply make sure your project is initialized to use modules using the 'go mod init' command and then include the package name in an import statement. The next build event will pull down the package and all its dependencies.

How to reference v2.0.0 of a Go package that doesn't adhere to the major sub folder convention as a module

We are running a spike to determine how or indeed if we can migrate our stack to Go modules. We depend on v2.0.0 https://github.com/gbrlsnchs/jwt but it is neither converted to a module itself nor does it adhere to the Go convention of putting major versions under sub-folders.
What are our options here when it come to converting our internal package to a module that has this dependency?
github.com/gbrlsnchs/jwt at v2.0.0 has a go.mod file that indicates that its canonical import path is github.com/gbrlsnchs/jwt/v2.
Update your import statements to use that path — which should also work in GOPATH mode in Go 1.9.7, 1.10.3, 1.11.0, and above using “minimal module compatibility” — and you should be good to go.

Resources