Daily-refreshed Nix flake mirroring upstream Go and Go tooling. Use it when nixpkgs lags an upstream security patch or tool release.
| Tool | Source | Pinning | Refresh |
|---|---|---|---|
go |
go.dev/dl/?mode=json |
published SHA256 | daily |
golangci-lint |
GitHub releases | per-release <name>-checksums.txt |
daily |
goreleaser |
GitHub releases | per-release checksums.txt |
daily |
gofumpt |
GitHub releases | per-asset API digest field |
daily |
doclint |
GitHub releases (first-party) | per-release checksums.txt |
daily |
hugo (extended, Linux only) |
GitHub releases | per-release hugo_<ver>_checksums.txt |
daily |
govulncheck |
GitHub git tags | source tarball + vendor SHA256 | daily |
gopls |
GitHub git tags | source tarball + vendor SHA256 | daily |
delve |
GitHub git tags | source tarball (vendored) | daily |
staticcheck |
GitHub git tags | source tarball + vendor SHA256 | daily |
For the binary mirrors, every (os, arch) the upstream actually publishes is
mirrored — coverage is sparse per tool, since a tool appears on a system only
if its upstream ships a binary for that arch. govulncheck, gopls, delve,
and staticcheck are the exceptions: none of them ship upstream binaries for
Linux/Darwin/FreeBSD that we'd want to mirror, so the flake builds them from
source via buildGoModule and exposes them on every system the flake spans.
All four are compiled against this flake's own latest Go rather than
nixpkgs' Go — gopls and staticcheck misbehave when built with a Go minor
older than the project they're analyzing, and delve needs to track Go's
binary symbol format. Pinning the toolchain to the latest mirrored Go is what
makes the "no nixpkgs lag" guarantee hold for these tools too.
delve's release tarballs ship a vendor/ directory, so its data file
records vendor = null; rather than a vendor SRI hash — buildGoModule uses
the bundled vendor tree directly.
Add the flake reference directly to your packages list in devbox.json:
{
"packages": [
"github:openserbia/go-flake#go",
"github:openserbia/go-flake#golangci-lint",
"github:openserbia/go-flake#goreleaser",
"github:openserbia/go-flake#gofumpt",
"github:openserbia/go-flake#doclint",
"github:openserbia/go-flake#hugo",
"github:openserbia/go-flake#govulncheck",
"github:openserbia/go-flake#gopls",
"github:openserbia/go-flake#delve",
"github:openserbia/go-flake#staticcheck"
]
}Do not use the <pkg>@github:... syntax; Devbox treats flake references as full
package identifiers. Pin a specific version with _<major>_<minor>_<patch>,
e.g. #go_1_26_5. The short fragment lets Devbox/Nix infer the current
system, which is the most portable form when it resolves cleanly.
If Devbox reports that a pinned flake package is ambiguous between packages
and legacyPackages, qualify the standard flake output explicitly. In that
form the system is part of the flake output path, so replace x86_64-linux
with the system you are building on:
{
"packages": [
"github:openserbia/go-flake#packages.x86_64-linux.go_1_26_5"
]
}For object-style packages, use the fully qualified reference as the key:
{
"packages": {
"github:openserbia/go-flake#packages.x86_64-linux.go_1_26_5": ""
}
}This works around Devbox resolver paths that see both
packages.<system>.<attr> and legacyPackages.<system>.<attr> for the same
short fragment and refuse to pick one.
If you get a "package not found" error when pinning a version, you may need to update your flake lock to pull the latest versions from this repository:
devbox update goTo pull the latest daily refresh for a package, run:
devbox update github:openserbia/go-flake#gonix run github:openserbia/go-flake#go -- version
nix shell github:openserbia/go-flake#golangci-lint
nix build github:openserbia/go-flake#gofumpt_0_10_0The Go package installs under $out/share/go/ (matching the nixpkgs
layout), with $out/bin/{go,gofmt} as symlinks into it. devbox merges the
package into its profile, so just point GoLand / IntelliJ's Go SDK at
<project>/.devbox/nix/profile/default/share/go — it has bin/, src/,
and VERSION at the top level, which is what the IDE validates against.
The github: URL scheme hits the GitHub API to resolve refs, which has a low
unauthenticated rate limit. For CI or shared environments that hit it, swap in
the tarball+codeload URL — same content, served via the codeload CDN:
{
"packages": [
"tarball+https://codeload.github.com/openserbia/go-flake/tar.gz/main#packages.x86_64-linux.go_1_26_5"
]
}docker/Dockerfile builds
ghcr.io/openserbia/go-builder, a builder-stage base with a pinned devbox
and a Nix store already holding the toolchain every openserbia service
shares (Go, go-task, golangci-lint, govulncheck). Services start
their Dockerfile from it instead of installing devbox and provisioning Nix
on every build:
FROM ghcr.io/openserbia/go-builder:latest AS builder
COPY devbox.json devbox.lock ./
RUN devbox install # near no-op when the lock matches the warm store
COPY . .
RUN devbox run -- task buildlatest is the intended tag for consumers. The base warms the flake's
newest Go and linters (the unversioned #go, #golangci-lint,
#govulncheck attributes) and is republished whenever a mirrored upstream
release lands, so when the services bump their pinned Go the base already
holds it. A service's own devbox.lock still decides what gets built: a
base holding a different version only costs warm-store hits. The
devbox<version> tag and the digest the publish job prints in its run
summary exist for anyone who needs a byte-for-byte reproducible builder. The image is
published by .github/workflows/docker.yml
on every push to main that touches docker/, and it is a builder only:
runtime images stay distroless.
nix flake show github:openserbia/go-flakeOr browse the data files directly:
versions.nix— Gogolangci-lint-versions.nixgoreleaser-versions.nixgofumpt-versions.nixdoclint-versions.nix— first-partyhugo-versions.nix— extended edition, Linux amd64/arm64govulncheck-versions.nixgopls-versions.nixdelve-versions.nixstaticcheck-versions.nix
Attribute naming: <tool>_<major>_<minor>_<patch> (dots → underscores). The
bare attribute <tool> is an alias for the newest version available on the
current system. default (and go) point at the latest Go.
Coverage matches each upstream's published artifacts; the flake exposes a tool on a Nix system iff that system maps to a platform key in the data file. Currently exposed systems:
| Linux | Darwin | FreeBSD |
|---|---|---|
x86_64-linux |
x86_64-darwin |
x86_64-freebsd |
aarch64-linux |
aarch64-darwin |
|
i686-linux |
||
armv6l-linux |
||
armv7l-linux |
||
riscv64-linux |
||
powerpc64le-linux |
Run nix flake show on your system to see which tools and versions resolve.
A daily self-hosted GitHub Actions workflow
(.github/workflows/update-versions.yml)
runs five idempotent updater scripts, then commits any diff straight to main:
| Script | Updates | Trust source |
|---|---|---|
scripts/update-versions.py |
versions.nix |
go.dev JSON's sha256 field |
scripts/update-github-tool.py --tool golangci-lint |
golangci-lint-versions.nix |
per-release golangci-lint-<ver>-checksums.txt |
scripts/update-github-tool.py --tool goreleaser |
goreleaser-versions.nix |
per-release checksums.txt |
scripts/update-github-tool.py --tool gofumpt |
gofumpt-versions.nix |
GitHub asset API digest: sha256:… field |
scripts/update-github-tool.py --tool doclint |
doclint-versions.nix |
per-release checksums.txt (first-party) |
scripts/update-github-tool.py --tool hugo |
hugo-versions.nix |
per-release hugo_<ver>_checksums.txt |
scripts/update-source-tool.py --tool govulncheck |
govulncheck-versions.nix |
nix-prefetch-url --unpack + buildGoModule vendor discovery |
scripts/update-source-tool.py --tool gopls |
gopls-versions.nix |
nix-prefetch-url --unpack + buildGoModule vendor discovery |
scripts/update-source-tool.py --tool delve |
delve-versions.nix |
nix-prefetch-url --unpack (vendor=null; bundled vendor/) |
scripts/update-source-tool.py --tool staticcheck |
staticcheck-versions.nix |
nix-prefetch-url --unpack + buildGoModule vendor discovery |
Each script supports --check (exit non-zero if its data file is stale, no
write). All three updaters accept --min-version to lower the floor;
gofumpt's floor is fixed at 0.9.0 because GitHub only populates asset
digests for releases uploaded after mid-2024.
Trust chain for the binary mirrors: SHA256 comes from the channel above, then
Nix's own fetchurl verifies the downloaded archive against that hash at
build time — no intermediate proxy, URLs point at go.dev and
github.com/<repo>/releases/download/... directly. For govulncheck the
chain is two-step: the source tarball SHA pins the GitHub archive, and the
vendor hash pins the resolved Go module set against go.sum at build time.
When CVEs land in the Go stdlib (DNS resolver, net/mail, HTTP/2, etc.),
upstream ships a patch within days. nixpkgs maintainers usually follow within a
week, but production builds can be left exposed in the interim. The same lag
hits major tool releases (golangci-lint, gofumpt) and frequently slips on Go
sub-minor cadence. This flake mirrors upstream exactly, so a one-line data-file
change is enough to be back on a clean stdlib or a current linter.
This flake is MIT. Each mirrored binary is licensed by its upstream project (Go: BSD-3-Clause; golangci-lint: GPL-3.0-or-later; goreleaser: MIT; gofumpt: BSD-3-Clause; govulncheck: BSD-3-Clause; doclint: MIT, openserbia first-party; hugo: Apache-2.0).