iTranslated by AI
Trying Out Go Workspace Mode in a Single Repository
I want to handle multiple modules in a single repository
Over the weekend, I released a Go package that accesses RDBMS services via SSH.
This package consists of three modules. The directory structure looks like this:
$ tree sshql
sshql
├── go.mod
├── go.sum
├── mysqldrv
│ ├── go.mod
│ └── go.sum
└── pgdrv
├── go.mod
└── go.sum
By the way, the mysqldrv and pgdrv packages depend on the parent sshql package. Also, there is no relationship between mysqldrv and pgdrv. If I were to draw it as a UML diagram, it would look something like this:

The reason I made these three packages into separate modules was because I didn't want to mix external packages for each driver into the root go.mod file. By separating the modules:
You can cleanly separate the driver packages like this.
The problem is that since these three modules are (at least on the surface) independent, importing between them causes the GitHub repository to be referenced instead of the local files. This is problematic for a development environment.
Trying Workspace mode
So, I decided to try the workspace mode introduced in Go 1.18. However, based on the following documentation, the standard approach seems to be creating a parent directory for workspace management and then placing the modules within it.
This wouldn't work in this specific case. But well, let's give it a try anyway.
First, I'll try creating a go.work file directly under the repository root. Let's see how this works:
$ go work init . mysqldrv pgdrv
This created the following file:
It seems this is enough to recognize the current sshql module and the mysqldrv and pgdrv modules under it.
With this, you can run tests for each module, for example:
$ go test -shuffle on ./pgdrv/...
If there is no go.work file...
If you try to run tests without a go.work file:
$ go test -shuffle on ./pgdrv/...
pattern ./pgdrv/...: directory prefix pgdrv does not contain main module or its selected dependencies
You'll get an error like this. Well, it would pass if you did something like:
$ pushd pgdrv
$ go test -shuffle on ./...
But then it would try to import the parent module from the GitHub repository. It just doesn't work out. I went through a lot of trial and error in this area.
Versioning each sub-module
Moving on.
By placing go.work, I can now develop entirely within the local environment. Also, by including go.work in the repository, it becomes possible to handle multiple modules in GitHub Actions as well[1].
I managed to get it working properly, so I'll set the version tags. However, if I tag it with something like v0.1.0, this version tag only applies to the module directly under the repository root. In this case, it won't be applied to the sub-modules mysqldrv and pgdrv, and they would be given uncool versions like v0.0.0-20220910000000-abcde012345.
To set a version tag on a sub-module, it seems I should prefix it with the package name, like mysqldrv/v0.1.0. This way, Go recognizes it as the version for the sub-module. If I run go mod tidy in another project:
module sample
go 1.19
require (
github.com/goark/sshql v0.1.0
github.com/goark/sshql/mysqldrv v0.1.0
)
require (
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/goark/errs v1.1.0 // indirect
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
)
The mysqldrv module now has a version assigned. It's also helpful to be able to manage versions independently for each module.
-
On the Go side, it seems they recommend not sharing
go.work. For a configuration like the one in the tutorial, it seems better not to include it. But since this case is different (lol). By the way, a file calledgo.work.summight be generated during the build process; it's probably better to exclude that from the repository. ↩︎
Discussion