mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-09 15:44:33 -04:00
Consolidate go.mod.sri and go.toolchain.rev.sri into a single flakehashes.json file at the repo root, owned by a new Go program at tool/updateflakes. The JSON is consumed by flake.nix via builtins.fromJSON and by any future Go code via the FlakeHashes struct that defines its schema. Each block records its input fingerprint alongside the SRI it produced: the goModSum (a sha256 over go.mod and go.sum) for the vendor block, and the literal rev string from go.toolchain.rev for the toolchain block. updateflakes regenerates a block only when its recorded fingerprint disagrees with the current input. Doing the gating by content rather than file mtimes avoids the usual mtime hazards across git checkouts, clones, and merges. It also means re-runs with no input changes are essentially free, and a re-run that touches only one input pays only for that one block. The two blocks have no shared state -- vendor invokes go mod vendor into one tempdir, toolchain fetches and extracts a tarball into another -- so they run concurrently via errgroup. Cold time is bounded by the slower of the two rather than their sum. Also takes the opportunity to fold the toolchain fetch into a single curl|tar pipeline (no intermediate .tar.gz on disk). Split cmd/nardump into a thin package main and a new package nardump library at cmd/nardump/nardump that holds the NAR encoder and SRI helper. tool/updateflakes imports the library directly rather than building and exec'ing the nardump binary at runtime. The library uses fs.ReadLink (Go 1.25+) instead of os.Readlink, so it no longer requires the caller to chdir into the FS root for symlink targets to resolve. WriteNAR now wraps its writer in a bufio.Writer internally (unless the caller already passed one) and flushes on return, so callers don't pay for tiny writes against slow underlying writers. The cache-busting line in flake.nix and shell.nix is known to live at end of file, so updateCacheBust walks the lines in reverse. make tidy timings on this machine, before: ~14s every run. After: warm (no input changes): 0.05s vendor block stale only: 1.4s toolchain block stale only: 5.0s cold (no flakehashes.json): 5.0s Updates #6845 Change-Id: I0340608798f1614abf147a491bf7c68a198a0db4 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
168 lines
6.4 KiB
Nix
168 lines
6.4 KiB
Nix
# flake.nix describes a Nix source repository that provides
|
|
# development builds of Tailscale and the fork of the Go compiler
|
|
# toolchain that Tailscale maintains. It also provides a development
|
|
# environment for working on tailscale, for use with "nix develop".
|
|
#
|
|
# For more information about this and why this file is useful, see:
|
|
# https://wiki.nixos.org/wiki/Flakes
|
|
#
|
|
# Also look into direnv: https://direnv.net/, this can make it so that you can
|
|
# automatically get your environment set up when you change folders into the
|
|
# project.
|
|
#
|
|
# WARNING: currently, the packages provided by this flake are brittle,
|
|
# and importing this flake into your own Nix configs is likely to
|
|
# leave you with broken builds periodically.
|
|
#
|
|
# The issue is that building Tailscale binaries uses the buildGoModule
|
|
# helper from nixpkgs. This helper demands to know the content hash of
|
|
# all of the Go dependencies of this repo, in the form of a Nix SRI
|
|
# hash. This hash isn't automatically kept in sync with changes made
|
|
# to go.mod yet, and so every time we update go.mod while hacking on
|
|
# Tailscale, this flake ends up with a broken build due to hash
|
|
# mismatches.
|
|
#
|
|
# Right now, this flake is intended for use by Tailscale developers,
|
|
# who are aware of this mismatch and willing to live with it. At some
|
|
# point, we'll add automation to keep the hashes more in sync, at
|
|
# which point this caveat should go away.
|
|
#
|
|
# See https://github.com/tailscale/tailscale/issues/6845 for tracking
|
|
# how to fix this mismatch.
|
|
{
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
|
systems.url = "github:nix-systems/default";
|
|
# Used by shell.nix as a compat shim.
|
|
flake-compat = {
|
|
url = "github:edolstra/flake-compat";
|
|
flake = false;
|
|
};
|
|
};
|
|
|
|
outputs = {
|
|
self,
|
|
nixpkgs,
|
|
systems,
|
|
flake-compat,
|
|
}: let
|
|
goVersion = nixpkgs.lib.fileContents ./go.toolchain.version;
|
|
toolChainRev = nixpkgs.lib.fileContents ./go.toolchain.rev;
|
|
flakeHashes = builtins.fromJSON (builtins.readFile ./flakehashes.json);
|
|
gitHash = flakeHashes.toolchain.sri;
|
|
eachSystem = f:
|
|
nixpkgs.lib.genAttrs (import systems) (system:
|
|
f (import nixpkgs {
|
|
system = system;
|
|
overlays = [
|
|
(final: prev: {
|
|
go_1_26 = prev.go_1_26.overrideAttrs (old: {
|
|
version = goVersion;
|
|
src = prev.fetchFromGitHub {
|
|
owner = "tailscale";
|
|
repo = "go";
|
|
rev = toolChainRev;
|
|
sha256 = gitHash;
|
|
};
|
|
# The Tailscale Go fork carries a placeholder in
|
|
# src/runtime/debug/mod.go that must be replaced with
|
|
# the actual toolchain git rev at build time. Without
|
|
# this, binaries report an empty tailscale.toolchain.rev
|
|
# and the runtime assertion in
|
|
# assert_ts_toolchain_match.go panics.
|
|
postPatch =
|
|
(old.postPatch or "")
|
|
+ ''
|
|
substituteInPlace src/runtime/debug/mod.go \
|
|
--replace-fail "TAILSCALE_GIT_REV_TO_BE_REPLACED_AT_BUILD_TIME" "${toolChainRev}"
|
|
'';
|
|
});
|
|
})
|
|
];
|
|
}));
|
|
tailscaleRev = self.rev or "";
|
|
in {
|
|
# tailscale takes a nixpkgs package set, and builds Tailscale from
|
|
# the same commit as this flake. IOW, it provides "tailscale built
|
|
# from HEAD", where HEAD is "whatever commit you imported the
|
|
# flake at".
|
|
#
|
|
# This is currently unfortunately brittle, because we have to
|
|
# specify vendorHash, and that sha changes any time we alter
|
|
# go.mod. We don't want to force a nix dependency on everyone
|
|
# hacking on Tailscale, so this flake is likely to have broken
|
|
# builds periodically until someone comes through and manually
|
|
# fixes them up. I sure wish there was a way to express "please
|
|
# just trust the local go.mod, vendorHash has no benefit here",
|
|
# but alas.
|
|
#
|
|
# So really, this flake is for tailscale devs to dogfood with, if
|
|
# you're an end user you should be prepared for this flake to not
|
|
# build periodically.
|
|
packages = eachSystem (pkgs: rec {
|
|
default = pkgs.buildGo126Module {
|
|
name = "tailscale";
|
|
pname = "tailscale";
|
|
src = ./.;
|
|
vendorHash = flakeHashes.vendor.sri;
|
|
nativeBuildInputs = [pkgs.makeWrapper pkgs.installShellFiles];
|
|
ldflags = ["-X tailscale.com/version.gitCommitStamp=${tailscaleRev}"];
|
|
env.CGO_ENABLED = 0;
|
|
subPackages = [
|
|
"cmd/tailscale"
|
|
"cmd/tailscaled"
|
|
"cmd/tsidp"
|
|
];
|
|
doCheck = false;
|
|
|
|
# NOTE: We strip the ${PORT} and $FLAGS because they are unset in the
|
|
# environment and cause issues (specifically the unset PORT). At some
|
|
# point, there should be a NixOS module that allows configuration of these
|
|
# things, but for now, we hardcode the default of port 41641 (taken from
|
|
# ./cmd/tailscaled/tailscaled.defaults).
|
|
postInstall =
|
|
pkgs.lib.optionalString pkgs.stdenv.isLinux ''
|
|
wrapProgram $out/bin/tailscaled --prefix PATH : ${pkgs.lib.makeBinPath [pkgs.iproute2 pkgs.iptables pkgs.getent pkgs.shadow]}
|
|
wrapProgram $out/bin/tailscale --suffix PATH : ${pkgs.lib.makeBinPath [pkgs.procps]}
|
|
|
|
sed -i \
|
|
-e "s#/usr/sbin#$out/bin#" \
|
|
-e "/^EnvironmentFile/d" \
|
|
-e 's/''${PORT}/41641/' \
|
|
-e 's/$FLAGS//' \
|
|
./cmd/tailscaled/tailscaled.service
|
|
|
|
install -D -m0444 -t $out/lib/systemd/system ./cmd/tailscaled/tailscaled.service
|
|
''
|
|
+ pkgs.lib.optionalString (pkgs.stdenv.buildPlatform.canExecute pkgs.stdenv.hostPlatform) ''
|
|
installShellCompletion --cmd tailscale \
|
|
--bash <($out/bin/tailscale completion bash) \
|
|
--fish <($out/bin/tailscale completion fish) \
|
|
--zsh <($out/bin/tailscale completion zsh)
|
|
'';
|
|
};
|
|
tailscale = default;
|
|
});
|
|
|
|
devShells = eachSystem (pkgs: {
|
|
default = pkgs.mkShell {
|
|
packages = with pkgs; [
|
|
curl
|
|
git
|
|
gopls
|
|
gotools
|
|
graphviz
|
|
perl
|
|
go_1_26
|
|
yarn
|
|
|
|
# qemu and e2fsprogs are needed for natlab
|
|
qemu
|
|
e2fsprogs
|
|
];
|
|
};
|
|
});
|
|
};
|
|
}
|
|
# nix-direnv cache busting line: sha256-ruRbOB2W9snyOYY0+6OD5IndI/JJKqrhTuPlBsKikRc=
|