Skip to content

Commit

Permalink
Introduce the NativeLink Cloud flake module
Browse files Browse the repository at this point in the history
This change enables LRE by default and adds a config file that connects
Nix users to the NativeLink Cloud. This means that users running Bazel
from within the nix flake can fetch artifacts directly from CI builds.
  • Loading branch information
aaronmondal committed Sep 26, 2024
1 parent 4d331f7 commit be9b576
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,8 @@ build --extra_toolchains=@rust_toolchains//:all
# Generated by the LRE flake module.
try-import %workspace%/lre.bazelrc

# Generated by the nativelink flake module.
try-import %workspace%/nativelink.bazelrc

# Allow user-side customization.
try-import %workspace%/user.bazelrc
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ trivy-results.sarif
Pulumi.dev.yaml
lre.bazelrc
rust-project.json
nativelink.bazelrc
44 changes: 44 additions & 0 deletions flake-module.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
lib,
flake-parts-lib,
...
}: {
options = {
perSystem = flake-parts-lib.mkPerSystemOption (
{
config,
options,
pkgs,
...
}: let
cfg = config.nativelink;
in {
options = {
nativelink = {
pkgs = lib.mkOption {
type = lib.types.uniq (lib.types.lazyAttrsOf (lib.types.raw or lib.types.unspecified));
description = "Nixpkgs to use.";
default = pkgs;
defaultText = lib.literalMD "`pkgs` (module argument)";
};
settings = lib.mkOption {
type = lib.types.submoduleWith {
modules = [./modules/nativelink.nix];
specialArgs = {inherit (cfg) pkgs;};
};
default = {};
description = "Configuration for Bazel on Darwin.";
};
installationScript = lib.mkOption {
type = lib.types.str;
description = "Create nativelink.bazelrc.";
default = cfg.settings.installationScript;
defaultText = lib.literalMD "bazelrc content";
readOnly = true;
};
};
};
}
);
};
}
12 changes: 10 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
imports = [
inputs.git-hooks.flakeModule
./local-remote-execution/flake-module.nix
./flake-module.nix
];
perSystem = {
config,
Expand Down Expand Up @@ -418,7 +419,7 @@
if pkgs.stdenv.isDarwin
then [] # Doesn't support Darwin yet.
else lre-cc.meta.Env;
prefix = "lre";
prefix = "linux";
};
devShells.default = pkgs.mkShell {
nativeBuildInputs = let
Expand Down Expand Up @@ -479,6 +480,10 @@
# development shell.
${config.pre-commit.installationScript}
# Generate nativelink.bazelrc which gives Bazel invocations access
# to NativeLink's read-only cache.
${config.nativelink.installationScript}
# Generate lre.bazelrc which configures LRE toolchains when running
# in the nix environment.
${config.local-remote-execution.installationScript}
Expand All @@ -499,6 +504,9 @@
};
}
// {
flakeModule = ./local-remote-execution/flake-module.nix;
flakeModule = {
default = ./flake-module.nix;
local-remote-execution = ./local-remote-execution/flake-module.nix;
};
};
}
116 changes: 116 additions & 0 deletions modules/nativelink.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{
config,
lib,
pkgs,
...
}: let
# These flags cause Bazel builds to connect to NativeLink's read-only cache.
#
# ```nix
# devShells.default = pkgs.mkShell {
# shellHook = ''
# # Generate the `lre.bazelrc` config file.
# ${config.nativelink.installationScript}
# '';
# };
# ```
defaultConfig = [
"--remote_cache=${config.endpoint}"
"--remote_header=x-nativelink-api-key=${config.api-key}"
"--remote_instance_name=main"
"--remote_header=x-nativelink-project=nativelink-ci"
"--nogenerate_json_trace_profile"
"--remote_upload_local_results=false"
"--experimental_remote_cache_async"
];

# If the `nativelink.settings.prefix` is set to a nonempty string,
# prefix the Bazel build commands with that string. This will disable
# connecting to the nativelink-cloud by default and require adding
# `--config=<prefix>` to Bazel invocations.
maybePrefixedConfig =
if (config.prefix == "")
then map (x: "build " + x) defaultConfig
else map (x: "build:" + config.prefix + " " + x) defaultConfig;

configFile = pkgs.runCommand "nativelink.bazelrc" {} ''
printf '# These flags are dynamically generated by the nativelink flake module.
#
# Add `try-import %%workspace%%/nativelink.bazelrc` to your .bazelrc to
# include these flags when running Bazel in a nix environment.
${lib.concatLines maybePrefixedConfig}' >$out
'';
in {
options = {
installationScript = lib.mkOption {
type = lib.types.str;
description = lib.mkDoc ''
A bash snippet which creates a nativelink.bazelrc file in the repository.
'';
};
endpoint = lib.mkOption {
type = lib.types.str;
description = lib.mdDoc ''
The NativeLink Cloud endpoint.
Defaults to NativeLink's shared cache.
'';
default = "grpcs://cas-tracemachina-shared.build-faster.nativelink.net";
};
api-key = lib.mkOption {
type = lib.types.str;
description = lib.mdDoc ''
The API key to connect to the NativeLink Cloud.
You should only use read-only keys here to prevent cache-poisoning and
malicious artifact extractions.
Defaults to NativeLink's shared read-only api key.
'';
default = "065f02f53f26a12331d5cfd00a778fb243bfb4e857b8fcd4c99273edfb15deae";
};
prefix = lib.mkOption {
type = lib.types.str;
description = lib.mdDoc ''
An optional Bazel config prefix for the flags in `nativelink.bazelrc`.
If set, builds need to explicitly enable the nativelink config via
`--config=<prefix>`.
Defaults to an empty string, enabling the cache by default.
'';
default = "";
};
};

config = {
installationScript = ''
if ! type -t git >/dev/null; then
# In pure shells
echo 1>&2 "WARNING: NativeLink: git command not found; skipping installation."
elif ! ${pkgs.git}/bin/git rev-parse --git-dir &> /dev/null; then
echo 1>&2 "WARNING: NativeLink: .git not found; skipping installation."
else
GIT_WC=`${pkgs.git}/bin/git rev-parse --show-toplevel`
# These update procedures compare before they write, to avoid
# filesystem churn. This improves performance with watch tools like
# lorri and prevents installation loops by lorri.
if ! readlink "''${GIT_WC}/nativelink.bazelrc" >/dev/null \
|| [[ $(readlink "''${GIT_WC}/nativelink.bazelrc") != ${configFile} ]]; then
echo 1>&2 "NativeLink: updating $PWD repository"
[ -L nativelink.bazelrc ] && unlink nativelink.bazelrc
if [ -e "''${GIT_WC}/nativelink.bazelrc" ]; then
echo 1>&2 "NativeLink: WARNING: Refusing to install because of pre-existing nativelink.bazelrc"
echo 1>&2 " Remove the nativelink.bazelrc file and add nativelink.bazelrc to .gitignore."
else
ln -fs ${configFile} "''${GIT_WC}/nativelink.bazelrc"
fi
fi
fi
'';
};
}
122 changes: 122 additions & 0 deletions web/platform/src/content/docs/docs/nativelink-cloud/nix.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: "Nix flake module"
description: "How to use NativeLink Cloud with Nix"
pagefind: true
---

The NativeLink Cloud flake module lets your contributors conveniently reuse
artifacts from your CI builds.

## Prerequisites

Cache sharing between CI and local development environments requires perfect
reproducibility between the two.

Consider using [Local Remote Execution](./explanations/lre) to create
environments that are reproducible across distributions.

Containerized environments that are the same for local development and CI might
work as well.

## Setup

import { Steps } from "@astrojs/starlight/components";

<Steps>

1. Add the `nativelink` flake module to your flake:

```nix
# flake.nix
# In your flake inputs:
inputs.nativelink.url = "github:TraceMachina/nativelink";
# In your flake-parts.lib.mkFlake imports:
imports = [
nativelink.flakeModule
];
# In your shellHook:
devShells.default = pkgs.mkShell {
shellHook = ''
# Generate nativelink.bazelrc which gives Bazel invocations access
# to NativeLink's read-only cache.
${config.nativelink.installationScript}
'';
```

2. Add the following to your `.bazelrc`:

```bash
# .bazelrc
try-import %workspace%/nativelink.bazelrc
```

3. Ignore the generated file:

```bash
# .gitignore
nativelink.bazelrc

4. Optionally, customize the endpoint and API key, or gate the configuration
behind a `--config=nativelink` Bazel flag:

```nix
# flake.nix
nativelink.settings = {
endpoint = "grpcs://my-custom-endpoint.com";
api-key = "my-custom-readonly-api-key";
prefix = "nativelink";
};
```

:::tip
When using custom `nativelink.settings` you can use arbitrary logic in Nix to
set the fields dynamically.
:::

</Steps>

## How it works

The `nativelink` flake module creates a `nativelink.bazel` file. The default
configuration points to NativeLink's public cache:
```
# nativelink.bazelrc
# These flags are dynamically generated by the nativelink flake module.
#
# Add `try-import %workspace%/nativelink.bazelrc` to your .bazelrc to
# include these flags when running Bazel in a nix environment.
build --remote_cache=grpcs://cas-tracemachina-shared.build-faster.nativelink.net
build --remote_header=x-nativelink-api-key=065f02f53f26a12331d5cfd00a778fb243bfb4e857b8fcd4c99273edfb15deae
build --remote_instance_name=main
build --remote_header=x-nativelink-project=nativelink-ci
build --nogenerate_json_trace_profile
build --remote_upload_local_results=false
build --experimental_remote_cache_async
```
:::tip
Feel free to ping the NativeLink authors on [Slack](https://nativelink.slack.com/join/shared_invite/zt-281qk1ho0-krT7HfTUIYfQMdwflRuq7A#/shared-invite/email) if you'd
like to add an LRE-based project to the default cache.
:::

With the modifications from the previous section it looks like this:

```
# nativelink.bazelrc
# These flags are dynamically generated by the nativelink flake module.
#
# Add `try-import %workspace%/nativelink.bazelrc` to your .bazelrc to
# include these flags when running Bazel in a nix environment.

build:nativelink --remote_cache=grpcs://my-custom-endpoints.com
build:nativelink --remote_header=x-nativelink-api-key=my-custom-readonly-api-key
build:nativelink --remote_header=x-nativelink-project=nativelink-ci
build:nativelink --nogenerate_json_trace_profile
build:nativelink --remote_upload_local_results=false
build:nativelink --experimental_remote_cache_async
```
4 changes: 4 additions & 0 deletions web/platform/starlight.conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export const starlightConfig = {
label: "API Keys in CI",
link: `${docsRoot}/nativelink-cloud/api-key`,
},
{
label: "Nix flake module",
link: `${docsRoot}/nativelink-cloud/nix`,
},
],
},
{
Expand Down

0 comments on commit be9b576

Please sign in to comment.