Using go.mod for local packages

on captainepoch's log

Golang provides the tools which enable the users to manage the dependencies of a project using mod. This allows one to host the dependencies for each project individually but, more importantly, to select the version of that dependency they wish to use.

But the problem I faced these last two days was not that, but another. I wanted to be able to host the code of my Go projects outside the src folder inside the $GOPATH. Also, I wanted to be able to develop closed-source projects without using the $gitserver/captainepoch/project naming scheme.

To be able to do that, first of all you need to create a folder (with the name of your liking because it will be the root package. For this example, my main package will be called potato:

$ go mod init potato

And you get a bare modules file:

module potato

go 1.15

Now, let’s create a config local package. The point of this is to be able to use it locally without getting it from any repo. Create a folder, a .go file, and a go.mod file:

$ mkdir config && cd config
$ go init mod config
$ touch config.go

Inside the config.go file:

package config

import "fmt"

func GetConfig() {
	fmt.Println("GetConfig executed")
}

To be able to use this in the main package, you have to use the replace directive:

module potato

go 1.15

replace potato/config => ./config

And the main.go content:

package main

import (
	"fmt"
	"potato/config"
)

func main() {
	fmt.Println("Potato is life")

	config.GetConfig()
}

You should see your modules file already changed when adding this to your main package. However, if you do not see anything, you have to run the tidy command:

$ go mod tidy

If you need to use some local package inside another local package, you have to use replace but using ..: replace potato/utils => ../utils which allows you to use a local package inside the project’s root inside another local package. Also, you have to add the replace in the root modules file, so Go knows where to look at to find that local package.

Be aware that this can cause circular dependencies which is not something good. So if you use this, use it with something you know will not create any circular dependency problem.

I know this is far from being orthodox but sometimes this hack helps more than other solutions.

If you have any questions, please reach me at my NixNet account captainepoch@nixnet.social (the mailist is WIP).


I want to thank jbauer and Tedster for the corrections of this post :).