Go modules, private repos and gopath - go

We are converting our internal codebase from the dep dependency manager to go modules (vgo or built in with go1.11.2). Imagine we have code like this:
$GOPATH/src/mycompany/myprogram/main.go:
package main
import (
"fmt"
lib "mycompany/mylib" )
func main() {
fmt.Println("2+3=", lib.add(2, 3))
}
$GOPATH/src/mycompany/myprogram/go.mod:
module mycompany/myprogram
(it doesn't have any dependencies; our real-world code does).
$GOPATH/src/mycompany/mylib/lib.go:
package mylib
func Add(x int, y int) int {
return x + y
}
I didn't module-ize this code; it doesn't seem to matter whether I do or don't.
These are trivial examples but our internal code follows a similar structure as this worked historically.
Since these directories are on the Gopath, export GO111MODULE=auto still builds as before and this works fine (modules not used because we are on the gopath). However, when I set export GO111MODULE=on I immediately get the error:
build mycompany/myprogram: cannot find module for path mycompany/mylib
So I did some research and I would like to validate my understanding. First let me say our old approach worked, but I am more interested in changing to use go modules as it appears to be where the go project itself is headed. So.
It seems the intention of the golang authors was that "dotless" paths belong to the standard repository only; that is there should be a binding between domain name and project. We don't use go get on our internal project, unsurprisingly. Here is the source specifically:
Dotless paths in general are reserved for the standard library; go get has (to my knowledge) never worked with them, but go get is also the main entry point for working with versioned modules.
Can anyone with more knowledge of golang than me confirm this?
My key assumption is that once go decides to use modules, all dependencies must be modules and the gopath becomes somewhat irrelevant, except as a cache (for downloaded modules). Is this correct?
If this is true, we need to use a private gitlab (in our case) repository on the path. There's an open issue on handling this that I'm aware of so we can implement this if necessary. I'm more interested in the consequences, specifically for iterating in the private repositories. Previously we could develop these libraries locally before committing any changes; now it seems we have a choice:
Accept this remote dependency and iterate. I was hoping to avoid needing to push and pull remotely like this. There are workarounds to needing an internet connection if strictly necessary.
Merge everything into one big git repository.
If it matters, I'm using go version go1.11.2 linux/amd64 and my colleagues are using darwin/amd64. If it helps, my golang is exactly as installed by Fedora's repositories.
So, tl;dr, my question is: are go modules all-or-nothing, in that any dependency must be resolved using the module system (go get, it seems) and the gopath has become redundant? Or is there something about my setup that might trigger this to fail? Is there some way to indicate a dependency should be resolved explicitly from the gopath?
Updates since asking the question:
I can move myprogram out of the gopath. The same issue occurs (mylib has been left in the gopath).
I can run, or not run, go mod init mycompany/mylib in the mylib directory; it makes no difference at all.
I came across Russ Cox's blog post on vgo. My concerns about offline development that I tried not to dive into too far are resolved by $GOPROXY.

I use a workaround with GITHUB_TOKEN to solve this.
Generate GITHUB_TOKEN here https://github.com/settings/tokens
export GITHUB_TOKEN=xxx
git config --global url."https://${GITHUB_TOKEN}:x-oauth-basic#github.com/mycompany".insteadOf "https://github.com/mycompany"

I wrote up a solution for this on Medium: Go Modules with Private Git Repositories.
The way we handle it is basically the same as the answer above from Alex Pliutau, and the blog goes into some more detail with examples for how to set up your git config with tokens from GitHub/GitLab/BitBucket.
The relevant bit for GitLab:
git config --global \
url."https://oauth2:${personal_access_token}#privategitlab.com".insteadOf \
"https://privategitlab.com"
#or
git config --global \
url."https://${user}:${personal_access_token}#privategitlab.com".insteadOf \
"https://privategitlab.com"
I hope it's helpful.

I use ls-remote git command to help resolve private repo tags and go get after it.
$ go env GO111MODULE=on
$ go env GOPRIVATE=yourprivaterepo.com
$ git ls-remote -q https://yourprivaterepo.com/yourproject.git
$ go get

Related

Go modules when running get vs build

I have a project where I use go modules. There I need to specify that I depend on a particular fork of a library (call it ), because it has an important patch. When I run go get -u <my_project>, I get a compilation error that clearly means that go took the main repo of , instead of the fork.
After that, I switch to the directory where go downloaded and run go build. Then, go takes the proper version of and compilation is successful.
Could you tell me what could be the reason for that and how to fix it?
Here is specific command to get :
go get -u github.com/planetA/konk
The dependency is "github.com/opencontainers/runc". For this dependency, go.mod contains following:
replace (
github.com/opencontainers/runc => github.com/planeta/runc v1.0.0-rc9.0.20191206160324-51eabe724369
)
require (
github.com/opencontainers/runc v1.0.0-rc9.0.20191206223258-201b06374548
)
Interestingly, in following sequence of commands, second go get does not produce error
go get -u github.com/planetA/konk
cd ~/go/src/github.com/planetA/konk
go build -tags seccomp
go get -u github.com/planetA/konk
I had a similar problem and I resolved this by detaching the fork itself and making a separate repo. Depends on your use case, this is also a much cleaner solution as you don't need to add too many edits to make it work. You can do so by:
Clone the fork repository on your local.
Delete the fork repository from github/gitlab etc. if you want the package name to be the same as the fork if not you can rename the new repo you create.
Create a new repo and push your content onto it
Now go get github.com/myusername/mynewrepo
But if you still want to make it work using the fork here is a great reference.

Is there an easier way to keep local Go packages updated

I am using multiple packages that I import into different projects, these range from custom adapters for my business logic that are shared by lambda and google cloud functions and other public packages. The way I do this right now is that I vendor them and include them for cloud functions. For applications that can be compiled and deployed on a VM, I compile them separately. This works fine for me, however, its a pain developing these modules.
If I update the method signature and names in the package, I have to push my changes to github / gitlab (my package path is something like gitlab.com/groupName/projectName/pkg/packageName) and then do a go get -u <pacakgeName> to update the package.
This also, does not really update it, sometimes am stuck with an older version with no idea on how to update it. Is there an easier way of working with this I wonder.
For sake of clarity:
Exported package 1
Path: gitlab.com/some/name/group/pkg/clients/psql
psql-client
|
|_ pkg
|
|_psql.go
Application 1 uses psql-client
Path: gitlab.com/some/name/app1
Application 2 uses psql-client
Path: gitlab.com/some/name/app2
My understanding is that (a) you are using the new Go modules system, and that (b) part of the problem is that you don't want to keep pushing changes to github or gitlab across different repositories when you are doing local development.
In other words, if you have your changes locally, it sounds like you don't want to round-trip those changes through github/gitlab in order for those changes to be visible across your related repositories that you are working on locally.
Most important advice
It is greatly complicating your workflow to have > 1 module in a single repository.
As is illustrated by your example, in general it is almost always more work on an on-going basis to have > 1 module in a single repository. It is also very hard to get right. For most people, the cost is almost always not worth it. Also, often the benefit is not what people expect, or in some cases, there is no practical benefit to having > 1 module in a repo.
I would definitely recommend you follow the commonly followed rule of "1 repo == 1 module", at least for now. This answer has more details about why.
Working with multiple repos
Given you are using Go modules, one approach is you can add a replace directive to a module's go.mod file that informs that Go module about the on-disk location of other Go modules.
Example structure
For example, if you had three repos repo1, repo2, repo3, you could clone them so that they all sit next to each other on your local disk:
myproject/
├── repo1
├── repo2
└── repo3
Then, if repo1 depends on repo2 and repo3, you could set the go.mod file for repo1 to know the relative on-disk location of the other two modules:
repo1 go.mod:
replace github.com/me/repo2 => ../repo2
replace github.com/me/repo3 => ../repo3
When you are inside the repo1 directory or any of its child directories, a go command like go build or go test ./.... will use the on-disk versions of repo2 and repo3.
repo2 go.mod:
If repo2 depends on repo3, you could also set:
replace github.com/me/repo3 => ../repo3
repo3 go.mod:
If for example repo3 does not depend on either of repo1 or repo2, then you would not need to add a replace to its go.mod.
Additional details
The replace directive is covered in more detail in the replace FAQ on the Modules wiki.
Finally, it depends on your exact use case, but a common solution at this point is to use gohack, which automates some of this process. In particular, it creates a mutable copy of a dependency (by default in $HOME/gohack, but the location is controlled by $GOHACK variable). gohackalso sets your current go.mod file to have a replace directive to point to that mutable copy.
go get is transitive, so you can just add it to your build process. A typical Go project build is basically:
go get -u ./... && go test ./... && go build ./cmd/myapp
Which gets & updates dependencies, runs all project tests, then builds the binary.

'is not within a known GOPATH/src' error on dep init

When I run dep init in project folder, the error occurs:
init failed: unable to detect the containing GOPATH: D:\projects\foo is not within a known GOPATH/src
My projects are located on another drive and not %GOPATH%/src (i.e. %USERPROFILE%\go\src).
It's a known error but it's unclear what is the solution.
How can I use dep without moving Go projects to %GOPATH%/src?
Go makes this choice so that there is nothing like a CLASSPATH (ie: Java) to deal with. You specify a $GOPATH that has a consistent src tree inside of it. If your repo makes references to particular git commits (rather than the ones checked out into $GOPATH/src/github.com/$githubUser/$githubProjectName), then those will be in the ./vendor directory of your project.
If you have a different Go project that uses a completely different set of checkouts, due to versioning issues, then you can have multiple $GOPATH values to deal with that.
How can I use dep without moving Go projects to %GOPATH%/src?
Not at all.
Go projects require that your project is within its path..
So first do a
$ go env
to find out where that is. Lets say it says /home/turgut/go
move the project that you downloaded that needs the dep to:
/home/turgut/go/src/myproject
then cd /home/turgut/go/src/myproject
and try the
dep ensure
command now.
what does go env command say your GOPATH is? Set GOPATH for your environment as per this doc
Follow steps here https://deployer.org/docs/7.x/installation
you can use this command vendor/bin/dep init instead of dep init

Go dep and forks of libraries

I'm trying to understand how to work with Golang and forks. The situation is the following, I'm writing a library project which depends on library github.com/other/some_dependency, which isn't mine.
Because some_dependency is missing some methods that I need, I fork it to github.com/me/some_dependency. However, I can't just do go get github.com/me/some_dependency, the library references itself so it breaks.
In this article they give a possible solution:
go get github.com/other/some_dependency
cd $GOPATH/src/github.com/other/some_dependency
git remote add fork git#github.com:me/some_dependency
git rebase fork/master
Now, this is hacky at best. There is no way from the code of the library to know that the dependency is coming from a different repo. Anyone doing go get of my library wouldn't manage to make it work.
As dep is expected to be the official dependency manager. I've found how to fix the version:
dep ensure -add github.com/foo/bar#v1.0.0
But I cannot find how to set a different remote. Is is possible to do it?
As an example, in Node.js with npm it is dead simple:
npm install git+https://git#github.com/visionmedia/express.git
If you look at the help you will see this:
<import path>[:alt source URL][#<constraint>]
So to add github.com/foo/bar from location github.com/fork/bar you have to add it like this:
dep ensure -add github.com/foo/bar:github.com/fork/bar
The source location will be added as source attribute in the Gopkg.toml.
Gopkg docs for dependency rules constraint and override

Modifying an imported library in Go

My Problem
Elastic Beats is an open source project for log shippers written in Go. It features several log outputs, including console, Elasticsearch and Redis. I would like to add an output of my own - to AWS Kinesis.
I have cloned the repo to ~/github/beats, and tried building it:
$ cd filebeat; go build main.go
However, it failed due to a missing library which is a part of the project:
main.go:6:2: cannot find package "github.com/elastic/beats/filebeat/cmd" in any of:
/usr/local/go/src/github.com/elastic/beats/filebeat/cmd (from $GOROOT)
/Users/adam/go/src/github.com/elastic/beats/filebeat/cmd (from $GOPATH)
A directory of the project is dependent on a package from the same repo, but instead of looking one directory up the hierarchy it looks in the GOPATH.
So, go get github.com/elastic/beats/filebeat/cmd fetched the code, and now go build main.go works. Changing the code in my GOPATH is reflected in these builds.
This leaves me with an structural inconvenience. Some of my code is at a working directory, and some of it is at my GOPATH and included by the code in my working directory.
I would like to have all my code in a single directory for various reasons, not the least being keeping everything under version control.
What Have I Tried
Mostly searching for the problem. I am quite new to Go, so I might have missed the correct terminology.
My Question
What is the right way to edit the code of an imported library in Go?
One of the recommended ways to work with other's packages is:
Get the sources of the original package:
go get github.com/elastic/beats
As a result you will clone project's git repository to the folder
$GOPATH/src/github.com/elastic/beats
Make some fixes, compile code, fix, compile... When you make go install package will be compiled and installed to your system. When you need merge updates from original repository you can git pull them.
Everything is OK. What's next? How to share your work with others?
Fork project on github, suppose it will be github.com/username/beats
Add this fork as another remote mycopy (or any other name you like) to your local repository
git remote add mycopy git://github.com/username/beats.git
When all is done you can push updated sources to your repo on github
git push mycopy
and then open a pull-request to original sources. This way you can share your work with others. And keep your changes in sync with mainstream.
Previous answers to this question are obsolete when developing projects that using Go Modules.
For projects that using Go Modules, one may use the following command to replace an imported library(eg. example.com/imported/module) with a local module(eg. ./local/module):
go mod edit -replace=example.com/imported/module=./local/module
Or by adding the following line into the go.mod file:
replace example.com/imported/module => ./local/module
Reference Docs: https://golang.org/doc/modules/managing-dependencies#unpublished
A project working copy should be checked out into $GOPATH/src/package/import/path - for example, this project should be checked out into /Users/adam/go/src/github.com/elastic/beats. With the project in the correct location, the go tooling will be able to operate on it normally; otherwise, it will not be able to resolve imports correctly. See go help gopath for more info.

Resources