Contents
Debian Go Team: Module-Aware Builds Transition Guide (WIP)
This Guide explains the new workflow for Modern, Module-Aware builds using dh-go.
The Long-Term Benefits
By aligning Debian's build environment with modern upstream Go standards via Go Workspaces, we eliminate Debian-specific GOPATH hacks that previously made local debugging difficult and almost impossible for upstream to help. When a Debian maintainer or user encounters a test failure or runtime bug in a package, they can now reproduce and debug it in almost the exact same build environment that the upstream developer expected.
This makes it easier for the whole Debian community to contribute back to the Go community. Build fixes and patches that work in Debian can be directly applied upstream with minimal rework, turning the whole Debian community (maintainers and users) into active, direct contributors to the Go ecosystem.
How Module-Aware Builds Work in Debian
Under Modern Module-Aware Builds (GO111MODULE=on), dependency resolution is entirely driven by the native Go compiler reading the upstream go.mod file.
To satisfy Debian's strict offline build requirement without fetching modules over the internet, dh-go automatically generates a Go Workspace under the build directory (e.g., debian/_build/go.work) during the build process. This go.work file redirect Go compiler to lookup module from the Go Import Path in go.mod file to the module on filesystem under /usr/share/gocode/src (provided by installed -dev packages).
Important: Because dh-go uses the standard Go compiler instead of old GOPATH workarounds, the Go Import Path in Debian sources should stay as identical as possible to upstream. Keeping our import path identical makes it easy for both Debian maintainers and upstream developers to reproduce build failures, test failures, and security issues. This also makes it much easier to get help from upstream and give back to upstream.
Real World Transition Examples
Since dh-go is available in unstable.
Porting golang-github-pocketbase-pocketbase to dh-go
1. Build-deps update in debian/control
2. Clean up dh-sequence-golang
3. Upload to unstable
Releasing debian version 0.39.4+ds1-1.
Newer upstream release with Go Import Path changes
When we updating golang-github-pocketbase-pocketbase to version 0.40.2+ds1-1, the we got FTBFS with a missing module dependency error.
1. Check missing module dependency FTBFS error
The build stopped due to a missing module dependency in the workspace:
src/github.com/pocketbase/pocketbase/apis/backup_create.go:8:2: no required module provides package github.com/pocketbase/ozzo-validation/v4; to add it:
cd /build/reproducible-path/golang-github-pocketbase-pocketbase-0.40.2+ds1/debian/_build/src/github.com/pocketbase/pocketbase
go get github.com/pocketbase/ozzo-validation/v4
2. Investigation Upstream Dependency Changes
When we unsure what happened or what to use. Check the https://pkg.go.dev and found this note from upstream:
[!NOTE] This is a fork of github.com/go-ozzo/ozzo-validation@v4.3.0 as the original project has changed ownership and the new maintainer cannot be trusted. I do plan to create eventually a new validation library from scratch more suited for our needs in PocketBase since ozzo-validation is known to have some minor performance and obscure regex issues, but until then this fork will have to be used.
We confirmed why upstream switched from github.com/go-ozzo/ozzo-validation/v4(the name convention was wrong on golang-github-go-ozzo-ozzo-validation.v4-dev) to forked github.com/pocketbase/ozzo-validation/v4.
3. Resolution Strategy
Instead of patching to use the untrusted library, we should package the fork github.com/pocketbase/ozzo-validation/v4 as golang-github-pocketbase-ozzo-validation-v4-dev package. This keeps import paths identical to upstream and ensures proper support from upstream for a long run.
4. Satisfy Upstream Dependency
Fork or create a new package with ITP. After that. Package builds fine without additional patches after updated build-deps to golang-github-pocketbase-ozzo-validation-v4.
Renaming Legacy Package
Example: Renaming golang-github-google-go-github to golang-github-google-go-github-v60.
1. Fork from old one
Find the package repo and then fork to go-team/packages namespace on salsa.
2. Renaming with Major API Version
In debian/control:
- Update Source: to golang-github-google-go-github-v60.
- Update the binary package to golang-github-google-go-github-v60-dev.
- Update Go-Import-Path: to github.com/google/go-github/v60
- Update VCS-Tags: point to the new Salsa repository name with -v60.
In debian/watch:
- Upgrade to Version 5 format.
Set Major API Version in Matching-Pattern: refs/tags/v(60\.[\d.]+)
In debian/changelog:
- Rename the package.
Target unstable with a new debian version entry (60.0.0-1).
- Explaining the package rename for the v60 API version transition.
3. If Tests Failed
- This package has some tests FAILED. The go.mod requires
github.com/google/go-cmp v0.6.0 github.com/google/go-querystring v1.1.0- and we have in Debian:
golang-github-google-go-cmp-dev | 0.7.0-1 | unstable | all golang-github-google-go-querystring-dev | 1.2.0-1 | unstable | all
Suspect we need backport upstream changes.
Add upstream git as remote: git remote add upstream https://github.com/google/go-github.git, and then git fetch upstream.
Check git log from upstream/master with the FAILED Strings: git log upstream/master -S "UnsupportedTypeError" -p.
Cherry-pick the upstream commit and then run dpkg-source --commit to generate patch in quilt format under debian/patches/.
4. Lintian clean and Upload
- Polish the package until it's lintian clean.
- If package builds and all tests passed. Do Source+Binary upload to NEW. (Source-only upload will get reject from NEW)
Packaging Rules & Best Practices
1. Respect Upstream Import Paths & Package Naming Conventions
Canonical Identity: Source and binary -dev package names must strictly match upstream's canonical Go import path as declared in go.mod (and set via XS-Go-Import-Path).
Verification via pkg.go.dev: Check pkg.go.dev to verify the canonical import path and align major version suffixes with the package name when you are unsure what to use while creating or renaming a package.
Naming Conventions (Hyphens vs. Dots): Convert slashes (/) in Go import paths into hyphens (-) rather than dots (.) when naming Debian packages (e.g., /v3 becomes -v3, and /pkg.v3 becomes -pkg.v3). Because dots are valid characters inside domain names and paths, using hyphens avoids naming confusion in the future.
Upstream Forks: If upstream forks a dependency or switches import paths (e.g., moving from ?GitHub to Codeberg), package it under the upstream canonical import path (renaming in the newer version) rather than patching it or adding symbolic links locally.
Missing Major API Version in Debian: File an ITP and package it instead of patching the source to relax version constraints.
2. Handling Upstream Module Relocations & Legacy Package Renames
If dependencies in Build-Depends or Depends still use old legacy GOPATH names, follow this renaming process:
Import Path Changes & NEW Queue: Changing an upstream import path (e.g., adding a major version suffix like /v2) requires creating a new source and binary package. File an ITP whether the rename is for Module-Aware Builds or an upstream move. The FTP/NEW team noticed this during ?DebConf26 and will prioritize these renamed packages through the NEW queue quickly.
Preserve Git History: When creating a new Salsa repository for a renamed module, it is recommended to fork or copy the Git history from the original repository rather than starting from scratch with a fresh dh-make-golang make run.
Archive Cleanup (Reverse Dependencies & Removal): Update all reverse-dependency packages to depend on the new package name. Once all reverse dependencies have migrated, file a RM (Removal) request against ftp.debian.org to remove the legacy package from the archive.
3. Minimal Patching & Identical Sources
Remove Old GOPATH Patches: Old Debian-specific patches made as GOPATH workarounds are no longer needed and should be removed.
Identical Upstream Source: Keep the Debian package source as close to upstream as possible. If upstream tests pass natively, they should pass in Debian without extra patches.
4. Always Run Tests & Avoid Path Exclusion
Enable Upstream Test Suites: Always run the upstream test suite during package builds (dh_auto_test). Disabling tests hides runtime regressions or bugs.
Avoid DH_GOLANG_EXCLUDE: Try to package everything without using DH_GOLANG_EXCLUDE. Excluding paths can break import paths for other (future reverse-dependency) packages.
Package Missing Build-Dependencies: If tests require extra test helpers, do not disable the tests. Instead, package the missing test dependencies. This improves the whole Go ecosystem in Debian, as other packages may need them in the import path too.
Skip Specific Tests Only: If a test fails because of a Debian-specific architecture not supported upstream or the build environment (for example, requiring network access or specific hardware), skip only that test with a comment using go test -skip instead of disabling the entire test suite.
5. Handling Circular Build-Dependencies
In modern Go ecosystems, core libraries often depend on each other (Package A needs Package B, but Package B needs Package A). While Go's upstream ecosystem relies on dynamic "get the latest" fetching, Debian requires strict order-of-source builds.
Use go.mod to resolve locally: Instead of adding manual patches to remove dependencies, use go mod vendor to resolve dependencies locally and build a seed package first. Then, build the rest against the seed package locally.
Watch ?DebConf26 Talk: For a detailed breakdown on how to solve these dependency loops and upload to Debian, watch the talk "Chicken, Egg, or go.mod? Solving Go Circular Build-Dependencies in Debian" from ?DebConf26.
6. Upstream First Reporting
Fix issues upstream: If you find upstream problems like outdated dependencies, missing go.mod files, or broken imports, send bug reports or pull requests directly to the upstream project.
Track Upstream Issues: Do not carry patches silently in Debian. If a local patch is temporarily required to unblock a build, file a bug in both the Debian BTS and upstream, referencing the upstream issue URL inside the DEP-3 patch header.
