diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 72533be6c4e3e..60eb067f17e67 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -6760,6 +6760,12 @@ githubId = 767083; name = "Derek Guenther"; }; + dhashs = { + email = "me@dha.sh"; + github = "DhashS"; + githubId = 4737200; + name = "Dhash Shrivathsa"; + }; dhkl = { email = "david@davidslab.com"; github = "dhl"; diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index feb535c2142c2..d839650031f7a 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -1710,6 +1710,9 @@ "module-security-acme-reload-dependencies": [ "index.html#module-security-acme-reload-dependencies" ], + "module-programs-bazelisk": [ + "index.html#module-programs-bazelisk" + ], "module-programs-zsh-ohmyzsh": [ "index.html#module-programs-zsh-ohmyzsh" ], diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index f72dcccf3e879..37549d4ac134a 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -180,6 +180,7 @@ ./programs/bash/undistract-me.nix ./programs/bat.nix ./programs/bazecor.nix + ./programs/bazelisk.nix ./programs/bcc.nix ./programs/benchexec.nix ./programs/browserpass.nix diff --git a/nixos/modules/programs/bazelisk.md b/nixos/modules/programs/bazelisk.md new file mode 100644 index 0000000000000..8d6e30c07ac91 --- /dev/null +++ b/nixos/modules/programs/bazelisk.md @@ -0,0 +1,8 @@ +# Bazelisk {#module-programs-bazelisk} + +The `programs.bazelisk` module installs Bazelisk as both `bazelisk` and +`bazel`, enables envfs so unpatched Bazel binaries can resolve `/bin/bash`, +and writes `/etc/bazel.bazelrc` with Nix store paths for common action tools. + +Set `programs.bazelisk.cc` and `programs.bazelisk.cxx` when a project needs C +or C++ toolchain detection through `rules_cc`. diff --git a/nixos/modules/programs/bazelisk.nix b/nixos/modules/programs/bazelisk.nix new file mode 100644 index 0000000000000..a2af507c3fbb6 --- /dev/null +++ b/nixos/modules/programs/bazelisk.nix @@ -0,0 +1,125 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.programs.bazelisk; + + # Build a PATH containing common tools that Bazel actions need. + defaultActionPath = lib.makeBinPath ( + with pkgs; + [ + bash + binutils + coreutils + findutils + gawk + gnugrep + gnused + gnutar + gzip + which + unzip + ] + ++ cfg.extraPackages + ); + + bazelrc = pkgs.writeText "bazel.bazelrc" '' + # Generated by NixOS programs.bazelisk module. + # NixOS needs an explicit PATH for Bazel actions since tools live in the + # Nix store and are not at standard FHS locations. + build --action_env=PATH=${defaultActionPath} + build --host_action_env=PATH=${defaultActionPath} + + ${lib.optionalString (cfg.cc != null) '' + build --repo_env=CC=${cfg.cc} + build --action_env=CC=${cfg.cc} + ''} + ${lib.optionalString (cfg.cxx != null) '' + build --repo_env=CXX=${cfg.cxx} + build --action_env=CXX=${cfg.cxx} + ''} + ${cfg.extraBazelrc} + ''; +in +{ + options.programs.bazelisk = { + enable = lib.mkEnableOption '' + bazelisk, a version-switching launcher for Bazel. + + Enabling this module installs bazelisk with a `bazel` symlink, enables + envfs (so unpatched Bazel binaries can find `/bin/bash`), and writes a + system-wide `/etc/bazel.bazelrc` that sets `--action_env=PATH` and + `--host_action_env=PATH` to Nix store paths for common tools. + + Per-project settings (e.g. extra rustc flags, pkg-config paths) still + belong in `.bazelrc.user` + ''; + + package = lib.mkPackageOption pkgs "bazelisk" { }; + + cc = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = lib.literalExpression ''"''${pkgs.clang}/bin/clang"''; + description = '' + Path to the C compiler. Set via `--repo_env=CC` and `--action_env=CC` + in the system bazelrc so that rules_cc toolchain detection works. + ''; + }; + + cxx = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = lib.literalExpression ''"''${pkgs.clang}/bin/clang++"''; + description = '' + Path to the C++ compiler. Set via `--repo_env=CXX` and `--action_env=CXX` + in the system bazelrc so that rules_cc toolchain detection works. + ''; + }; + + extraPackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = '' + Additional packages to include in the Bazel action PATH. + ''; + }; + + extraBazelrc = lib.mkOption { + type = lib.types.lines; + default = ""; + description = '' + Extra lines appended to the system `/etc/bazel.bazelrc`. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + # Install bazelisk with a `bazel` symlink so it's a drop-in replacement. + environment.systemPackages = [ + (pkgs.symlinkJoin { + name = "bazelisk-with-bazel"; + paths = [ cfg.package ]; + postBuild = "ln -sf $out/bin/bazelisk $out/bin/bazel"; + }) + ]; + + # envfs provides /bin/bash and /usr/bin/env for unpatched Bazel binaries. + services.envfs.enable = true; + services.envfs.extraFallbackPathCommands = '' + ln -s ${pkgs.bash}/bin/bash $out/bash + ''; + + # System-wide bazelrc with NixOS-compatible defaults. + environment.etc."bazel.bazelrc".source = bazelrc; + }; + + meta = { + doc = ./bazelisk.md; + maintainers = with lib.maintainers; [ dhashs ]; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 8384ee882f134..660a599bea0e0 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -254,6 +254,7 @@ in ayatana-indicators = runTest ./ayatana-indicators.nix; babeld = runTest ./babeld.nix; bazarr = runTest ./bazarr.nix; + bazelisk = runTest ./bazelisk.nix; bcache = runTestOn [ "x86_64-linux" "aarch64-linux" ] ./bcache.nix; bcachefs = runTestOn [ "x86_64-linux" "aarch64-linux" ] ./bcachefs.nix; beanstalkd = runTest ./beanstalkd.nix; diff --git a/nixos/tests/bazelisk.nix b/nixos/tests/bazelisk.nix new file mode 100644 index 0000000000000..3c0bc9e800449 --- /dev/null +++ b/nixos/tests/bazelisk.nix @@ -0,0 +1,66 @@ +{ + pkgs, + ... +}: + +let + fakeBazelisk = pkgs.writeShellScriptBin "bazelisk" '' + echo "fake bazelisk $*" + ''; + + fakeCc = pkgs.writeShellScriptBin "bazelisk-test-cc" '' + exit 0 + ''; + + fakeCxx = pkgs.writeShellScriptBin "bazelisk-test-cxx" '' + exit 0 + ''; + + extraTool = pkgs.writeShellScriptBin "bazelisk-extra-tool" '' + echo "extra tool" + ''; +in +{ + name = "bazelisk"; + + nodes.machine = { + programs.bazelisk = { + enable = true; + package = fakeBazelisk; + cc = "${fakeCc}/bin/bazelisk-test-cc"; + cxx = "${fakeCxx}/bin/bazelisk-test-cxx"; + extraPackages = [ extraTool ]; + extraBazelrc = '' + build --action_env=BAZELISK_TEST=1 + ''; + }; + }; + + testScript = '' + start_all() + + machine.wait_until_succeeds("mountpoint -q /usr/bin") + machine.wait_until_succeeds("test -e /bin/bash") + + machine.succeed("test -x /run/current-system/sw/bin/bazelisk") + machine.succeed("test -x /run/current-system/sw/bin/bazel") + machine.succeed("bazel --version | grep 'fake bazelisk --version'") + machine.succeed("PATH= /bin/bash --version") + + bazelrc = machine.succeed("cat /etc/bazel.bazelrc") + for expected in [ + "build --action_env=PATH=", + "build --host_action_env=PATH=", + "${pkgs.bash}/bin", + "${pkgs.binutils}/bin", + "${pkgs.coreutils}/bin", + "${extraTool}/bin", + "build --repo_env=CC=${fakeCc}/bin/bazelisk-test-cc", + "build --action_env=CC=${fakeCc}/bin/bazelisk-test-cc", + "build --repo_env=CXX=${fakeCxx}/bin/bazelisk-test-cxx", + "build --action_env=CXX=${fakeCxx}/bin/bazelisk-test-cxx", + "build --action_env=BAZELISK_TEST=1", + ]: + assert expected in bazelrc, f"{expected!r} missing from bazelrc" + ''; +} diff --git a/pkgs/by-name/ba/bazelisk/package.nix b/pkgs/by-name/ba/bazelisk/package.nix index 9244fbbb5eaf3..1fde717679c73 100644 --- a/pkgs/by-name/ba/bazelisk/package.nix +++ b/pkgs/by-name/ba/bazelisk/package.nix @@ -2,6 +2,7 @@ lib, buildGoModule, fetchFromGitHub, + nixosTests, }: buildGoModule (finalAttrs: { @@ -23,11 +24,19 @@ buildGoModule (finalAttrs: { "-X github.com/bazelbuild/bazelisk/core.BazeliskVersion=v${finalAttrs.version}" ]; + passthru.tests = { + inherit (nixosTests) bazelisk; + }; + meta = { description = "User-friendly launcher for Bazel"; mainProgram = "bazelisk"; longDescription = '' - BEWARE: This package does not work on NixOS. + A user-friendly launcher for Bazel that automatically downloads and + runs the correct Bazel version for a given project. + + On NixOS, enable the `programs.bazelisk` module to set up envfs and + a system bazelrc with the correct tool paths. ''; homepage = "https://github.com/bazelbuild/bazelisk"; changelog = "https://github.com/bazelbuild/bazelisk/releases/tag/v${finalAttrs.version}";