iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💻

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.

https://github.com/goark/sshql
https://text.baldanders.info/release/sshql/

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:

https://github.com/goark/sshql/blob/v0.1.3/go.mod
https://github.com/goark/sshql/blob/mysqldrv/v0.1.3/mysqldrv/go.mod
https://github.com/goark/sshql/blob/pgdrv/v0.1.3/pgdrv/go.mod

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.

https://go.dev/doc/tutorial/workspaces
https://go.dev/blog/get-familiar-with-workspaces
https://future-architect.github.io/articles/20220216a/

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:

https://github.com/goark/sshql/blob/v0.1.3/go.work

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:

go.mod
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.

脚注
  1. 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 called go.work.sum might be generated during the build process; it's probably better to exclude that from the repository. ↩︎

GitHubで編集を提案

Discussion