How are versions of a sub module managed? - go

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.

Related

Package versioning in Golang

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.

go get fetching a pre-release version always

I am trying to wrap my head around go modules. This is what I have done so far.
Created a simple module and published it as 0.0.1 on github.
Referenced the above mentioned module in a separate go project and used it.
so far it works fine. Now I update the initial module to 0.0.2 and publish it as a release on github. Now when I try go get <published module> it still seems to fetch the original 0.0.1 version and run it. I also deleted all references to 0.0.1 including in go.mod and go.sum and run go get and it still fetches the original 0.0.1 version instead of 0.0.2.
I also tried publishing a major version 1.0.0 and tried to use it, but its still fetching the older 0.0.1 version. I thought it will automatically fetch the latest version and use that.
My go.mod file also shows the following even for the major version
require github.com/user/module v0.0.0-20210223020204-1b5fb712826f // indirect
I feel there could be something wrong in the way its being published on git. Any help, anyone.
It looks like the comments already highlighted the required steps to take, but to capture the information in an answer, here is the relevant snippet from go help get
The -u flag instructs get to use the network to update the named packages
and their dependencies. By default, get uses the network to check out
missing packages but does not use it to look for updates to existing packages.
Basically, go get will only fetch the versions locked in your go.{mod,sum} files and you need to explicitly ask for updates using one of:
# upgrade to the latest release
go get -u github.com/user/module
# upgrade to a specific version
go get github.com/user/module#2.0.0
# use the `latest` version alias
go get github.com/user/module#latest

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.

Is there a good way to initialize go mods with defaulting to dependency versions in the GOPATH?

I'm starting the process of switching my applications to using go modules from currently not using any dependency manager. I want to use all of the same versions of the dependencies I currently use to avoid the risk of a different version of something causing unforeseen issues. Since I have a microservice architecture with a lot of applications I'm trying to figure out if there's a better way to do this than checking each application and its individual dependencies against what is currently in the build server's GOPATH.
Is there any way, even if just once when first initializing go mods, to have go modules default to the versions in the GOPATH.
If that's not possible (which I have a strong feeling it's not), is it possible to use go list or something similar to print the imported dependencies and the current git sha that exists in the GOPATH?
From the root of your project directory:
go mod init
To pull in build (and optionally test dependencies):
go build ./... # ... notation will scan any subdirectories for any nested packages/tools
go test ./... # optional
The above will pull the latest (semver) version of each dependency. This may NOT be the version you are using with a GOPATH build.
So to ensure you get the latest commit (which is what GOPATH builds use) - I'd go through each dependency in go.mod and issue a manual update to master. So for example lets say you had logrus as a dependency, to update to the latest commit:
go get github.com/sirupsen/logrus#master
If the latest semver matches the latest commit - no change will occur - but if not you will get a tag version plus commit style pseudo-version.
The Go wiki has other go-modules daily workflows e.g. fast-forward, a month/year from now, to pull the latest version of your dependencies into your go.mod (and go.sum):
go get -u ./...
but again be aware if a dependency does not use semver or has switched to a v2 breaking change - the above will not work.
The best practice is to eyeball the repo:
does it support go-modules (i.e. does it have a go.mod at the top level or at the import path level)
is the git repo tagged
and if the latest commit is tagged
only then can you be sure you are getting the version you expect.

How do I import a specific version of a package using go get?

coming from a Node environment I used to install a specific version of a vendor lib into the project folder (node_modules) by telling npm to install that version of that lib from the package.json or even directly from the console, like so:
$ npm install express#4.0.0
Then I used to import that version of that package in my project just with:
var express = require('express');
Now, I want to do the same thing with go. How can I do that?
Is it possible to install a specific version of a package? If so, using a centralized $GOPATH, how can I import one version instead of another?
I would do something like this:
$ go get github.com/wilk/uuid#0.0.1
$ go get github.com/wilk/uuid#0.0.2
But then, how can I make a difference during the import?
Go 1.11 will have a feature called go modules and you can simply add a dependency with a version. Follow these steps:
go mod init .
go mod edit -require github.com/wilk/uuid#0.0.1
go get -v -t ./...
go build
go install
Here's more info on that topic - https://github.com/golang/go/wiki/Modules
Really surprised nobody has mentioned gopkg.in.
gopkg.in is a service that provides a wrapper (redirect) that lets you express versions as repo urls, without actually creating repos. E.g. gopkg.in/yaml.v1 vs gopkg.in/yaml.v2, even though they both live at https://github.com/go-yaml/yaml
gopkg.in/yaml.v1 redirects to https://github.com/go-yaml/yaml/tree/v1
gopkg.in/yaml.v2 redirects to https://github.com/go-yaml/yaml/tree/v2
This isn't perfect if the author is not following proper versioning practices (by incrementing the version number when breaking backwards compatibility), but it does work with branches and tags.
A little cheat sheet on module queries.
To check all existing versions: e.g. go list -m -versions github.com/gorilla/mux
Specific version #v1.2.8
Specific commit #c783230
Specific branch #master
Version prefix #v2
Comparison #>=2.1.5
Latest #latest
E.g. go get github.com/gorilla/mux#v1.7.4
You can use git checkout to get an specific version and build your program using this version.
Example:
export GOPATH=~/
go get github.com/whateveruser/whateverrepo
cd ~/src/github.com/whateveruser/whateverrepo
git tag -l
# supose tag v0.0.2 is correct version
git checkout tags/v0.0.2
go run whateverpackage/main.go
Glide is a really elegant package management for Go especially if you come from Node's npm or Rust's cargo.
It behaves closely to Godep's new vendor feature in 1.6 but is way more easier. Your dependencies and versions are "locked" inside your projectdir/vendor directory without relying on GOPATH.
Install with brew (OS X)
$ brew install glide
Init the glide.yaml file (akin to package.json). This also grabs the existing imported packages in your project from GOPATH and copy then to the project's vendor/ directory.
$ glide init
Get new packages
$ glide get vcs/namespace/package
Update and lock the packages' versions. This creates glide.lock file in your project directory to lock the versions.
$ glide up
I tried glide and been happily using it for my current project.
Nowadays you can just use go get for it. You can fetch your dependency by the version tag, branch or even the commit.
go get github.com/someone/some_module#master
go get github.com/someone/some_module#v1.1.0
go get github.com/someone/some_module#commit_hash
more details here - How to point Go module dependency in go.mod to a latest commit in a repo?
Go get will also install the binary, like it says in the documentation -
Get downloads the packages named by the import paths, along with their dependencies. It then installs the named packages, like 'go install'.
(from https://golang.org/cmd/go/)
Update 18-11-23: From Go 1.11 mod is official experiment. Please see #krish answer.
Update 19-01-01: From Go 1.12 mod is still official experiment.
Starting in Go 1.13, module mode will be the default for all development.
Update 19-10-17: From Go 1.13 mod is official package manager.
https://blog.golang.org/using-go-modules
Old answer:
You can set version by offical dep
dep ensure --add github.com/gorilla/websocket#1.2.0
From Go 1.5 there's the "vendor experiment" that helps you manage dependencies. As of Go 1.6 this is no longer an experiment. Theres also some other options on the Go wiki..
Edit: as mentioned in this answer gopkg.in is a good option for pinning github-depdencies pre-1.5.
dep is the official experiment for dependency management for Go language. It requires Go 1.8 or newer to compile.
To start managing dependencies using dep, run the following command from your project's root directory:
dep init
After execution two files will be generated: Gopkg.toml ("manifest"), Gopkg.lock and necessary packages will be downloaded into vendor directory.
Let's assume that you have the project which uses github.com/gorilla/websocket package. dep will generate following files:
Gopkg.toml
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/gorilla/websocket"
version = "1.2.0"
Gopkg.lock
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "941e8dbe52e16e8a7dff4068b7ba53ae69a5748b29fbf2bcb5df3a063ac52261"
solver-name = "gps-cdcl"
solver-version = 1
There are commands which help you to update/delete/etc packages, please find more info on official github repo of dep (dependency management tool for Go).
go get is the Go package manager. It works in a completely decentralized way and how package discovery still possible without a central package hosting repository.
Besides locating and downloading packages, the other big role of a package manager is handling multiple versions of the same package. Go takes the most minimal and pragmatic approach of any package manager. There is no such thing as multiple versions of a Go package.
go get always pulls from the HEAD of the default branch in the repository. Always. This has two important implications:
As a package author, you must adhere to the stable HEAD philosophy. Your default branch must always be the stable, released version of your package. You must do work in feature branches and only merge when ready to release.
New major versions of your package must have their own repository. Put simply, each major version of your package (following semantic versioning) would have its own repository and thus its own import path.
e.g. github.com/jpoehls/gophermail-v1 and github.com/jpoehls/gophermail-v2.
As someone building an application in Go, the above philosophy really doesn't have a downside. Every import path is a stable API. There are no version numbers to worry about. Awesome!
For more details : http://zduck.com/2014/go-and-package-versioning/
The approach I've found workable is git's submodule system. Using that you can submodule in a given version of the code and upgrading/downgrading is explicit and recorded - never haphazard.
The folder structure I've taken with this is:
+ myproject
++ src
+++ myproject
+++ github.com
++++ submoduled_project of some kind.
That worked for me
GO111MODULE=on go get -u github.com/segmentio/aws-okta#v0.22.1
There's a go edit -replace command to append a specific commit (even from another forked repository) on top of the current version of a package.
What's cool about this option, is that you don't need to know the exact pseudo version beforehand, just the commit hash id.
For example, I'm using the stable version of package "github.com/onsi/ginkgo v1.8.0".
Now I want - without modifying this line of required package in go.mod - to append a patch from my fork, on top of the ginkgo version:
$ GO111MODULE="on" go mod edit -replace=github.com/onsi/ginkgo=github.com/manosnoam/ginkgo#d6423c2
After the first time you build or test your module, GO will try to pull the new version, and then generate the "replace" line with the correct pseudo version. For example in my case, it will add on the bottom of go.mod:
replace github.com/onsi/ginkgo => github.com/manosnoam/ginkgo v0.0.0-20190902135631-1995eead7451
It might be useful.
Just type this into your command prompt while cd your/package/src/
go get github.com/go-gl/mathgl#v1.0.0
You get specific revision of package in question right into your source code, ready to use in import statement.
The current way to do this is to use go install
https://golang.org/doc/go-get-install-deprecation
Starting in Go 1.17, installing executables with go get is deprecated. go install may be used instead.
go install github.com/someone/some_module
Specific version
go install github.com/someone/some_module#v1.1.0
Specific commit
go install github.com/someone/some_module#commit_hash

Resources