Inthis article, we will explore what Go workspaces are and the various use cases you can explore with the workspaces feature. Before we start, make sure you have working knowledge of Go, setting up Go projects, and Go modules. You will also need Go v1.18 or later installed on your machine.
Before Go workspaces, Go developers had the option of creating multiple Go modules with distinct Go modules (go.mod) files and editing the files for changes or using the replace directory for changes in the go.mod. This was cumbersome, especially as the project size increased.
Go workspaces deliver a productivity boost to developers working on multiple Go projects and allow you to initialize a Go workspace to work with multiple modules simultaneously. You get simpler workflows and monorepos while the tool handles the go.mod file changes. This change lets you focus on writing code that works with multiple modules for your varying use cases.
You can initialize a Go workspace with the init command. After initialization, the init command writes a workspace file (
go.work) to the root directory. You can specify the paths as optional arguments to the init command or have the Go tool create an empty workspace:
Go workspaces may foster open source collaboration among Go developers because you can use multiple modules seamlessly. It is handy for large projects because switching and inter-operating between projects have become easier.
One of the use cases for Go workspaces is versioning. Go workspaces make it easier to build different versions and features, simultaneously, while syncing dependencies between packages without breaking existing code.
Go workspaces was released along with many other highly anticipated features, from fuzzing to generics and performance improvements across machines. Go authors constantly work to improve the language and recently released Go v1.19.
Now this approach works and was taken from here but I'm not sure if that's the correct approach for this or whether this approach is just meant for updating a local package temporarily while it's outside version control.
Each approach has pros and cons, but if you are working on a large code base with many modules, I'd suggest sticking to use version handling based on commits or tags, and use Go Workspace for your day to day development.
With the main go.mod defined with
github.com/name/project module path,
github.com/name/project module cannot make a reproducible build, because the dependency target for replace directive may have had its content updated. This can be especially problematic if the dependency target of
github.com/name/project/players is used by many modules. Any change in such a common package can result in a behaviour change for all the dependents, all at the same time.
This is probably the easiest approach. For each module, there is a clear commit history and versioning. As long as you refer to the module via remote repository, this is probably the easiest setup to start with, and dependency setup is very clear.
However, note that this approach would mean you'd need to manage multiple repositories, and making
go.work to help is going to require appropriate local directory mapping, which can be difficult for someone new to the code base.
It is still possible to deterministically define dependency with version information so that you can build your code, within a single repository. Commit based approach requires least step, and still works nicely. There are some catches to be noted, though.
If you have
go.work on a parent directory with go work use
github.com/name/project/players, etc., that takes precedence and uses the local files. This is even when you have a version specified in your go.mod.
For local development, which spans across multiple projects, Go Workspace is a great way to work on multiple things at once, without needing to push the code change for the dependency only first. But at the same time, actual release will still require broken up commits, so that first commit can be referenced later in other code change.
But still you can create modules of single packages. As Volker said, this might only make sense, if you want these packages to have a different lifecycle. It could also make sense, when you want to import these modules from another project and don't want the overhead of the whole collection of packages.
Most often, a version control repository contains exactly one module defined in the repository root. (Multiple modules are supported in a single repository, but typically that would result in more work on an on-going basis than a single module per repository).
In 2022, the best practice approach to having multiple modules within the same repository and importing them in other modules.
This is supported with a new "go module workspace".
Released with Go 1.18 and the new go work command.
The presence of a
go.work file in the working directory or a containing directory will put the go command into workspace mode.
The
go.work file specifies a set of local modules that comprise a workspace.
When invoked in workspace mode, the go command will always select these modules and a consistent set of dependencies.
If you like this blog post and want to support me to write more whilst learning more about Go, you can check out my book titled "Domain-driven design with Golang". You can get it from Amazon: [UK link], [US link].
With workspaces, this whole process becomes much easier. We can make a
go.work file that effectively works the same as the replace directive we used previously. Let's follow the same example again but this time use a workspace file.
to build the code without using the workfile, but this didn't work for me with the production release. I raised a bug against the go tool here and it looks like it was removed before release which is a shame.
Early posts from the community are suggesting that not committing your
go.work file is preferable. This makes sense, it is used for local development and after you have made and tested your changes to a module, you should push it, tag it and then reference it in your go.mod file as usual. Therefore, sharing
go.work files doesn't make sense.
A Warning though, if you do happen to push your
go.work file it looks like if you run go build it will use your
go.work file by default unless you explicitly turn it off. This could lead to production builds having development code in unintentionally. It therefore might be worth always running production builds with GOWORK=off go build ./... to be on the safe side.
Its called "Go workspaceS" but the feature appears to only support a single "
go.work" file (ie a single workspace) ... Is it possible to configure and use, say "
go2.work" (or something similar) and point to it?
The workspace file should be in the parent directory of your project und the other module parallel to your project. So you don't need to change any thing in your project (.gitignore etc.). Go search on all parents for a
go.work file.
I think this only worked for you because you've probably executed go mod edit -replace= and go mod tidy commands before the creation of your
go.work file.
I think until that issue is resolved, using go.mod and its replace directive should remain the best way to go for importing an un-published/local version of a dependency.
Should i use go work file if my project is not multi module? If so, Why am i supposed to have go mod (and in some cases go sum) files under every single directory while i can make my project run perfectly fine with only one go mod and one go sum file in root directory (in case i dont have go work file. Its kinda visual pollution for me :)
Lerna and Yarn workspaces provide tools to ease the lives of multi-package mono-repo maintainers. Since version 7 NPM follows suit by adding support for workspaces, opening up the world of multi-package tooling for NPM-only users.
While I was setting up a project, I tried to search for a way to combine workspaces with scoped packages. It wasn't immediately clear how to set this up. After figuring out how to do it, I thought I'd share this for those who are looking to do the same.
NPM's documentation shows how to create packages in a work space. To create a scoped workspace package, use the --scope option. For demonstration purposes we're going to re-create the is-even and is-odd packages with the same dependency setup they have. The is-odd will depend on the is-even package to implement its logic.
The install command uses the -w option to indicate which package should receive the dependency. This can be used to add any dependencies to the workspace. We can now implement the packages. For each package we create an index.ts file.
This is probably the most complicated step. The majority of our TypeScript configuration will be centralised, preventing the configuration of our packages to get out of sync. First up is a top-level config file (tsconfig.build.json) that we'll use as configuration baseline.
To get our tests to run, we need to create a Jest configuration file located at the root of our project (jest.config.js). This setup generates a code-coverage report and uses ts-jest which allows us to use TypeScript.
As our last step we'll go ahead and release our packages by publishing then to NPM. Before shipping our packages we need to ensure everything is ready to go. First, we'll want to be sure that all our packages are compiled.
Next we'll add an .npmignore file to each package to ensure the packages only contain the files intended for shipping. In the following example everything is ignored and shippable artefacts are excluded from the ignore list.
That's all it takes to setup a multi-package mono-repo for a TypeScript project. I've published the packages and pushed the entire setup to a github repository in case you want to see it in its entirety.
In a previous article, we wrote about how you should be using replace in go.mod files for modules local to the repository, like in a large monorepo This works because we can safely make assumptions about the organization of the checked-out directory structure. But what to do when you are working on dependant modules spread across multiple repos?
3a8082e126