# 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://nixos.wiki/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"; flake-utils.url = "github:numtide/flake-utils"; # Used by shell.nix as a compat shim. flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; }; outputs = { self, nixpkgs, flake-utils, flake-compat }: let # Grab a helper func out of the Nix language libraries. Annoyingly # these are only accessible through legacyPackages right now, # which forces us to indirect through a platform-specific # path. The x86_64-linux in here doesn't really matter, since all # we're grabbing is a pure Nix string manipulation function that # doesn't build any software. fileContents = nixpkgs.legacyPackages.x86_64-linux.lib.fileContents; tailscale-go-rev = fileContents ./go.toolchain.rev; tailscale-go-sri = fileContents ./go.toolchain.sri; # pkgsWithTailscaleGo takes a nixpkgs package set, and replaces # its Go 1.19 compiler with tailscale's fork. # # We need to do this because the buildGoModule helper function is # constructed with legacy nix imports, so we cannot construct a # buildGoModule variant that uses tailscale's toolchain. Instead, # we have to replace the toolchain in nixpkgs, and let lazy # evaluation propagate it into the nixpkgs instance of # buildGoModule. # # This is a bit roundabout, but there doesn't seem to be a more # elegant way of resolving the impedance mismatch between legacy # nixpkgs style imports and flake semantics, unless upstream # nixpkgs exposes the buildGoModule constructor func explicitly. pkgsWithTailscaleGo = pkgs: pkgs.extend (final: prev: rec { tailscale_go = prev.lib.overrideDerivation prev.go_1_20 (attrs: rec { name = "tailscale-go-${version}"; version = tailscale-go-rev; src = pkgs.fetchFromGitHub { owner = "tailscale"; repo = "go"; rev = tailscale-go-rev; sha256 = tailscale-go-sri; }; nativeBuildInputs = attrs.nativeBuildInputs ++ [ pkgs.git ]; # Remove dependency on xcbuild as that causes iOS/macOS builds to fail. propagatedBuildInputs = []; checkPhase = ""; TAILSCALE_TOOLCHAIN_REV = tailscale-go-rev; }); # Override go_1_20 so that buildGo120Module below uses # tailscale's toolchain as well. go_1_20 = tailscale_go; }); # tailscaleRev is the git commit at which this flake was imported, # or the empty string when building from a local checkout of the # tailscale repo. tailscaleRev = if builtins.hasAttr "rev" self then self.rev else ""; # 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 vendorSha256, 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 somoene comes through and manually # fixes them up. I sure wish there was a way to express "please # just trust the local go.mod, vendorSha256 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. tailscale = pkgs: pkgs.buildGo120Module rec { name = "tailscale"; src = ./.; vendorSha256 = fileContents ./go.mod.sri; nativeBuildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.makeWrapper pkgs.git ]; ldflags = ["-X tailscale.com/version.GitCommit=${tailscaleRev}"]; CGO_ENABLED = 0; subPackages = [ "cmd/tailscale" "cmd/tailscaled" ]; doCheck = false; 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" ./cmd/tailscaled/tailscaled.service install -D -m0444 -t $out/lib/systemd/system ./cmd/tailscaled/tailscaled.service ''; }; # This whole blob makes the tailscale package available for all # OS/CPU combos that nix supports, as well as a dev shell so that # "nix develop" and "nix-shell" give you a dev env. flakeForSystem = nixpkgs: system: let upstreamPkgs = nixpkgs.legacyPackages.${system}; pkgs = pkgsWithTailscaleGo upstreamPkgs; ts = tailscale pkgs; in { packages = { tailscale-go = pkgs.tailscale-go; tailscale = ts; }; devShell = pkgs.mkShell { packages = with upstreamPkgs; [ curl git gopls gotools graphviz perl pkgs.tailscale_go ]; }; }; in flake-utils.lib.eachDefaultSystem (system: flakeForSystem nixpkgs system); } # nix-direnv cache busting line: sha256-afcuo/pcLnfFHYTViYi8rPM0ovnUuawuZ26cYhZ1hss= sha256-dhoXBuYV9lE+ssIK4i/TG4cFbzUKSOKnQP47qEEcvsQ=