An engineer walks through automating release cuts across six Go microservice repos with Jenkins, Python, and GitLab, then pivots to a single Go monorepo using go.work and per-module git tags. The rewrite cuts the automation script from 1,800 to 1,143 lines of Python, drops CI config by 48%, reduces MRs per release from 6 to 1, and cuts release_cut time from ~19 minutes to ~75 seconds (15.5x). The piece closes with concrete Jenkins/Go/CGO/GitLab gotchas encountered along the way and links to both the monorepo and multi-repo repos for comparison.

11m read timeFrom dev.to
Post cover image
Table of contents
Table of ContentsThe Six ModulesThe StackPhase 1: Multi-Repo AutomationPhase 2: The Monorepo PivotThe Unified CI PipelineReal NumbersCaveats and GotchasTry It Yourself

Questions this post answers

How do you tag individual Go modules independently inside a single monorepo?

Go's module system supports directory-prefixed tags, so each module in a monorepo gets its own tag with a path prefix, such as log/v0.3.0, sdk/v0.3.0, or cli/cli-1.3.0, all applied to the same commit. When a consumer runs go get on a specific module path, Go matches the tag prefix to the module path, letting each module version independently while sharing one repository. Teams weighing a monorepo migration for Go microservices can track real-world patterns like this on daily.dev.

Why does my go.work replace directive get ignored and Go tries to fetch the module remotely instead?

The version in the go.work replace directive must exactly match the version required in go.mod; if go.mod requires log v0.5.0 but go.work has replace log v0.3.0 => ./log, Go ignores the replace entirely and attempts to fetch v0.5.0 remotely, causing CI failures. This typically happens when release automation bumps go.mod versions without creating matching tags. Developers debugging go.work replace mismatches can find similar Go tooling gotchas on daily.dev.

Why can't Jenkins find my Go installation even though it works fine in my terminal?

Jenkins runs pipeline steps in a non-login, non-interactive shell, so it never loads shell profiles like .bashrc or .zshrc where tools such as asdf set up PATH and GOROOT. The fix is to set GOROOT and PATH explicitly in the Jenkins pipeline's environment block, pointing directly at the Go binary location instead of relying on profile-based setup. Engineers wiring up Jenkins pipelines for Go projects can compare setup gotchas like this on daily.dev.

20.4K Impressions1 Comment