Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a bit of documentation #1

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Binary file added .github/profile/banner.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 84 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# ❄️+🪁 Generate Buildkite Pipelines from Nix Flakes

This repository contains some functions that'll transform a Nix flake into a
valid BuildKite Pipeline, represented in JSON.

## 🧑‍💻 Usage

We'll take a look at a minimal example flake that “builds” a packages named
`hello`, and “checks” that the output of running the `hello` package matches
`Hello, world!`. Here is that flake.

```nix
{
description = "flake-buildkite-pipeline example";

inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05";
flake-buildkite-pipeline.url = "github:tweag/flake-buildkite-pipeline";
};

outputs = { self, nixpkgs, flake-buildkite-pipeline }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.hello = pkgs.hello;

checks.${system}.demoCheck = pkgs.runCommandNoCC "demoCheck" {
meta.checkDescription = "demoCheck";
} ''
set -o verbose
want='Hello, world!'
got="$(${self.packages.${system}.hello}/bin/hello)"
[ "$want" == "$got" ]
touch $out
'';

pipelines.buildkite.steps = flake-buildkite-pipeline.lib.flakeSteps {
commonExtraStepConfig = {
agents = [ "nix" ];
plugins = [{ "thedyrt/skip-checkout#v0.1.1" = null; }];
};
} self;
};
}
```

Apart from the `pipelines` attribute, everything looks like a normal flake, and
we can still use it a such. Try running `nix flake check`, and you can observe
that it builds the `hello` package, and runs the singular check.

Now, to get the actual BuildKite `pipeline.json`, run `nix eval --json
.#pipelines.buildkite`. This will produce a valid BuildKite pipeline, the
`--json` flag will print it out as JSON instead of Nix. You can use this in
combination with the BuildKite Agent CLI tool to upload the pipeline. E.g. to
use your own Nix flake with BuildKite, make sure the attribute
`pipelines.buildkite.steps` gets defined in the flake's outputs. And then run
the following command as a first step in the BuildKite pipeline.

```shell
nix eval .#pipelines.buildkite --json | buildkite-agent pipeline upload
```

You can look at [the example flake][example-flake], and how we set it up on
BuildKite.

## 👋 Hello from the Tweag team

[![Scale your engineering power][banner]][website]

At Tweag we love using open source methods and research ideas to improve
software quality and reliability. We are big on composable software: functional,
typed, immutable. You might have seen some of our work in your own favourite
technical community, or read some of our [articles][blog].

**We're hiring, globally**, so if you want to shape the future of the software
industry working with innovative clients and smart, friendly colleagues, [read
more here][careers]!

[banner]: .github/profile/banner.jpg
[website]: https://tweag.io/
[blog]: https://tweag.io/blog
[careers]: https://tweag.io/careers
[example-flake]: ./examples/flake.nix
54 changes: 54 additions & 0 deletions examples/flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 62 additions & 0 deletions examples/flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
description = "flake-buildkite-pipeline example";

inputs.flake-buildkite-pipeline.url = "..";

outputs = { self, nixpkgs, flake-buildkite-pipeline }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
inherit (flake-buildkite-pipeline.lib) mkPipeline flakeSteps;
in {
packages.${system}.hello = pkgs.hello;

checks.${system} = let
mkHelloCheck = { checkName, traditional ? false, greeting ? "" }:
let
inherit (pkgs.lib.strings) escapeShellArg;
inherit (self.packages.${system}) hello;
want = if traditional then
"hello, world"
else if greeting != "" then
"${greeting}"
else
"Hello, world!";
shellArgs = if traditional then
"--traditional"
else if greeting != "" then
"--greeting=${escapeShellArg greeting}"
else
"";
command = "${hello}/bin/hello ${shellArgs}";
in (pkgs.runCommandNoCC "${checkName}" {
meta.checkDescription = "${checkName}";
} ''
set -o verbose
want='${want}'
got="$(${command})"
[ "$want" == "$got" ]
touch $out
'');
in {
hello = mkHelloCheck { checkName = "hello"; };

helloTraditional = mkHelloCheck {
checkName = "helloTraditional";
traditional = true;
};

helloBryan = mkHelloCheck {
checkName = "helloBryan";
greeting = "Hello, Bryan!";
};
};

pipelines.buildkite.steps = flakeSteps {
commonExtraStepConfig = {
agents = [ "nix" ];
plugins = [{ "thedyrt/skip-checkout#v0.1.1" = null; }];
};
} self;
};
}
32 changes: 31 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@
else
"${description} (${name})";
in rec {
# [Step] -> JSON
/* Generate a JSON representation from a list of steps.

Type:
mkPipeline :: [a] -> b
*/
mkPipeline = steps: builtins.toJSON { inherit steps; };

/* Generate a _build_ step that will run `nix build ...`.

Type:
build :: AttrSet -> a -> String
*/
build = { buildArgs ? [ ], reproducePath ? null, reproduceRepo ? null }:
installable:
''
Expand All @@ -37,12 +46,22 @@
}\e[0m"
'';

/* Generate a _sign_ step that will sign a store path with `nix store sign ...`.

Type:
sign :: [a] -> b -> String
*/
sign = keys: installable:
[ "echo '--- :black_nib: Sign the paths'" ] ++ map (key:
"nix store sign -k ${escapeShellArg key} -r ${
escapeShellArg installable
}") keys;

/* Generate a _push_ step that will push a store path to binary caches

Type:
sign :: [a] -> b -> String
*/
push = caches: installable:
[ "echo '--- :arrow_up: Push to binary cache'" ] ++ map (cache:
"nix copy --to ${escapeShellArg cache} ${
Expand Down Expand Up @@ -73,11 +92,22 @@
] ++ optional (signWithKeys != [ ]) (sign signWithKeys installable)
++ optionals (pushToBinaryCaches != [ ]) push);

/* Generate a `nix develop ...` script that runs a given _command_ in a
specified _environment_.

Type:
runInEnv :: a -> String -> String
*/
runInEnv = environment: command:
"nix develop ${escapeShellArg environment.drvPath} -c sh -c ${
escapeShellArg command
}";

/* Generate a list of buildkite pipeline steps which build&test some of the flake's outputs.

Type:
flakeSteps' :: AttrSet -> Flake -> [a]
*/
flakeSteps' = { mkBuildCommands, signWithKeys, pushToBinaryCaches
, buildArgs, systems, commonExtraStepConfig ? { }
, reproduceRepo ? null }:
Expand Down