diff --git a/README.md b/README.md index 0019f02..ee501da 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ -blueprint is an opinionated library that maps a standard folder structure to -flake outputs. It makes common use cases easy both for the author and -consumers. +blueprint is an opinionated library that maps a standard folder structure to flake outputs, allowing you to divide up your flake into individual files across these folders. This allows you to modularize and isolate these files so that they can be maintained individually and even shared across multiple projects. + +Blueprint also makes common use cases easy for both the author and consumers. Eg: @@ -44,28 +44,15 @@ Support for: and more! -## Documentation - -* [Getting started](docs/getting-started.md) -* [Configuring blueprint](docs/configuration.md) -* [Folder structure mapping](docs/folder-structure.md) - ## Rationale -Nix is just a tool. It should help you, and stay out of the way. But because -it's so flexible, everybody goes through a long period where they figure out -how to structure their repo. `flake.nix` files become noisy with boilerplate. +Nix is just a tool. It should help you, and stay out of the way. But because it's so flexible, everybody goes through a long period where they figure out how to structure their repo. `flake.nix` files become noisy with boilerplate. -By making a few opinionated choices, we're able to cut 99% of the glue code -you would find in most repos. A bit like Ruby on Rails or NextJS did for web -frameworks, we do it for Nix packages. We map folder and files to flake -outputs. +By making a few opinionated choices, we're able to cut 99% of the glue code you would find in most repos. A bit like Ruby on Rails or NextJS did for web frameworks, we do it for Nix packages. We map folder and files to flake outputs. -In some ways, this is the spiritual successor to `flake-utils`, my first -attempt at making flakes easier to use. +In some ways, this is the spiritual successor to `flake-utils`, my first attempt at making flakes easier to use. -Blueprint isn't suitable for complex flakes but it does allow you to easily -break out once your project becomes complicated beyond its capability. +Blueprint isn't suitable for complex flakes but it does allow you to easily break out once your project becomes complicated beyond its capability. ## Design principles @@ -80,3 +67,101 @@ break out once your project becomes complicated beyond its capability. * [std](https://github.com/divnix/std) * [snowflake-lib](https://github.com/snowfallorg/lib) * [clan-core](https://git.clan.lol/clan/clan-core) is an all-in-one solution to manage your deployments. + +## Full Documentation + +You can find the [full documentation here](https://numtide.github.io/blueprint/main/). + +## Quickstart + +Meanwhile, if you're ready to get started right away, here's what you do. + +1. [Install Nix](https://nix.dev/install-nix) or use NixOS. +2. Run `mkdir my-project && cd my-project` +3. Run `nix flake init -t github:numtide/blueprint` + +Now you're ready to create some folders and special files. The full documentation shows you all the folders and special files available, but for now let's create a couple of development shells, and a formatter. + +Remember, the goal is to divide up the flake.nix file into individual modular parts. This not only helps keep your flake.nix file size down, it lets you create reusable modules that you can easily drop into other projects. + +Let's create a package the builds a docker container from our source, assuming your source lives in a folder called src off the root folder. Assume your src entry point is a file called hello.py; in this example, just put the following in `hello.py`: + +``` +print('Hello from docker!') +``` + +Also from the root folder create a folder called `packages`, and under that a folder called `docker-python-hello`. Inside that folder create a file called `default.nix`, and place the following in it: + +```nix +{ pkgs, system, ... }: + +let + python = pkgs.python3; + + pythonApp = pkgs.stdenv.mkDerivation { + pname = "my-python-app"; + version = "1.0"; + src = ../../src; + + installPhase = '' + mkdir -p $out/app + cp hello.py $out/app/ + ''; + }; + + rootImage = pkgs.buildEnv { + name = "my-docker-root"; + paths = [ python pythonApp ]; + pathsToLink = [ "/bin" "/app" ]; # python will be linked in /bin + }; +in +pkgs.dockerTools.buildImage { + name = "my-python-hello"; + tag = "latest"; + + fromImage = pkgs.dockerTools.pullImage { + imageName = "python"; + finalImageTag = "3.11-slim"; + imageDigest = "sha256:7029b00486ac40bed03e36775b864d3f3d39dcbdf19cd45e6a52d541e6c178f0"; + sha256 = "sha256-lUrhG5Omgdk81NmQwQTo4wnEfq2+r2nGePpgTSYgVU0="; + }; + + copyToRoot = rootImage; + + config = { + WorkingDir = "/app"; + Cmd = [ "python" "/app/hello.py" ]; # will now work since python is in /bin + }; +} +``` + +This will build an image into a folder called results; to do so, type the following: + +``` +nix build .#docker-python-hello +``` + +Note that the name to use must match the name of the folder under the packages folder. + +Also notice that nix is able to find the default.nix file thanks to Blueprint. You can then load the image and run it by typing: + +``` +docker load < result +docker run --rm my-python-hello:latest +``` + +This should print the message: + +``` +Hello from docker! +``` + +Note that result is really a symbolic link to a tar.gz file containing the image in the store. + +You can view your image by typing the usual: + +``` +docker images +``` + + diff --git a/docs/configuration.md b/docs/configuration.md deleted file mode 100644 index af04a95..0000000 --- a/docs/configuration.md +++ /dev/null @@ -1,77 +0,0 @@ -# Configuration - -In this section we describe the blueprint configuration options. - -Those are available by changing the `flake.nix` output invocation with additional parameters. - -## prefix - -Set this if you want to load the blueprint from a directory within the repositiry other than the flake location. - -Default: "." - -Type: string. - -## systems - -Defines for which systems the project should be used and deployed on. - -Default: it will load the `inputs.systems` flake input, first from the current flake, and then fallback to the blueprint one. (see ). - -Type: list of `-` strings. - -Example: - -```nix -{ - outputs = inputs: inputs.blueprint { - inherit inputs; - systems = [ "aarch64-linux" "x86_64-linux" ]; - }; -} -``` - -## nixpkgs.config - -If set, blueprint will create a new instance of nixpkgs for each systems, with the passed config. - -Default: `inputs.nixpkgs.legacyPackages.`. - -Type: attrset. - -Example: - -```nix -{ - outputs = inputs: inputs.blueprint { - inherit inputs; - nixpkgs.config.allowUnfree = true; - }; -} -``` - -## nixpkgs.overlays - -> NOTE: It's better to use `perSystem` composition style instead of overlays if you can. - -If set, blueprint will create a new instance of nixpkgs for each systems, with the passed config. - -Default: `inputs.nixpkgs.legacyPackages.`. - -Type: list of functions. - -Example: - -```nix -{ - outputs = inputs: inputs.blueprint { - inherit inputs; - nixpkgs.overlays = [ - inputs.otherflake.overlays.default - (final: prev: { - git = final.gitMinimal; - }) - ]; - }; -} -``` diff --git a/docs/content/getting-started/.pages b/docs/content/getting-started/.pages new file mode 100644 index 0000000..ca1b6a9 --- /dev/null +++ b/docs/content/getting-started/.pages @@ -0,0 +1,5 @@ +nav: + - install.md + - folder_structure.md + - built_in_templates.md + - configuration.md \ No newline at end of file diff --git a/docs/content/getting-started/built_in_templates.md b/docs/content/getting-started/built_in_templates.md new file mode 100644 index 0000000..8bfb9e6 --- /dev/null +++ b/docs/content/getting-started/built_in_templates.md @@ -0,0 +1,59 @@ +# Using Templates + +Blueprint comes with several templates to help you get started with your project. + +> Note: Feel free to contribute new templates! + +To install from a template, use the following format; for example, to use the template called system manager, type: + +``` +nix flake init -t github:numtide/blueprint#system-manager +``` + +where we appended a hash symbol followed by the template name. + +## Default Template + +Init command: + +```bash +nix flake init -t github:numtide/blueprint +``` + +This is a bare-bones project as described in [install](./install.md). + +## NixOS and Darwin Shared Homes Template + +``` +nix flake init -t github:numtide/blueprint#nixos-and-darwin-shared-homes +``` + +This template is a bit of an example plus a template. You'll want to study all the files carefully. It shows how you can define and reuse modules, in this case nixos and home-manager. + +Look carefully at the folder structure; in this case we're using `hosts` and `modules` folders which are both picked up by Blueprint. + +If you drill down into the folders, you'll see inside the `hosts` folder, are a `my-darwin` folder and a `my-nixos` folder, both of which are imported by Blueprint. This defines the two hosts called `my-darwin` and `my-nixos`. + +Their respective configuration files both import a shared `modules/nixos/host-shared.nix` module between them. + +Also, both hosts define a `me` user and their home-manager configuration simply imports `modules/homes/home-shared.nix`. + +Finally, notice in the root `flake.nix` we're adding the home-manager and nix-darwin inputs, which serve as dependencies for managing home configurations and macOS integrations, respectively. + +The idea with this template is that you can use this example to get started on how to share configurations between different system and home environments on different hosts. + + +## Toml-DevEnvs + +Members of your team might be intimidated by Nix and flake files, and prefer a more traditional method of configuring their devshells. As such, we provide full support for TOML files. + +For more information, please visit our [devshell repo](https://github.com/numtide/devshell), which is what powers this template behind-the-scenes. + +## System Manager Template + +``` +nix flake init -t github:numtide/blueprint#system-manager +``` + +Notice that the root flake.nix file we're adding the system-manager input, which is our own project. You can find it on GitHub at [system-manager](https://github.com/numtide/system-manager), where you can read more information on how to use it. + diff --git a/docs/content/reference/configuration.md b/docs/content/getting-started/configuration.md similarity index 57% rename from docs/content/reference/configuration.md rename to docs/content/getting-started/configuration.md index 39fdafd..0610d11 100644 --- a/docs/content/reference/configuration.md +++ b/docs/content/getting-started/configuration.md @@ -1,24 +1,38 @@ # Configuration -The flake.nix file offers a few configuration options: +In this section we describe the blueprint configuration options: * **prefix**: This lets you specify a directory to hold the folders other than the flake.nix location. * **systems**: Defines which systems the project should be used and deployed on. -* **nixpkgs.config**: If set, Blueprint will create a new instance of nixpkgs for each systems. -* **nixpkgs.overlays**: If set, blueprint will create a new instance of nixpkgs for each systems. +* **nixpkgs.config**: If set, Blueprint will create a new instance of nixpkgs for each system. +* **nixpkgs.overlays**: If set, blueprint will create a new instance of nixpkgs for each system. -Below we provide more detail on each, along with examples. +These are available by changing the `flake.nix` output invocation with additional parameters. -[TODO: More detail and each; meanwhile I've copied in the current configuration.md file] +Below we provide more detail on each, along with examples. ## prefix -Set this if you want to load the blueprint from a directory within the repositiry other than the flake location. +Set this if you want to load the blueprint from a directory within the repository other than the flake location. Default: "." Type: string. +For example, add the following prefix line to your output invocation: + +```nix +outputs = inputs: + inputs.blueprint { + inherit inputs; + prefix = "nix/"; + }; +``` + +Then, you can add a `nix` folder inside the same folder that holds your flake file, and place all your folders within this `nix` folder. + +> **Tip:** Although you can choose any folder you like, we recommend the name "nix" for your folder, as this is becoming the defacto standard. + ## systems Defines for which systems the project should be used and deployed on. @@ -40,13 +54,13 @@ Example: ## nixpkgs.config -If set, blueprint will create a new instance of nixpkgs for each systems, with the passed config. +If set, blueprint will create a new instance of nixpkgs for each system, with the passed config. Default: `inputs.nixpkgs.legacyPackages.`. Type: attrset. -Example: +For example, the following sets the allowUnfree attribute of nixpkgs.config to true: ```nix { @@ -61,13 +75,11 @@ Example: > NOTE: It's better to use `perSystem` composition style instead of overlays if you can. -If set, blueprint will create a new instance of nixpkgs for each systems, with the passed config. - -Default: `inputs.nixpkgs.legacyPackages.`. +If set, blueprint will create a new overlay with the passed config. Type: list of functions. -Example: +Here's an example that creates a couple of overlays; the first ("otherflake") reuses an overlay defined in another flake, and the second, an inline overlay, defines an overlay that replaces the default git package with the smaller gitMinimal: ```nix { diff --git a/docs/content/getting-started/folder_structure.md b/docs/content/getting-started/folder_structure.md new file mode 100644 index 0000000..2f315ee --- /dev/null +++ b/docs/content/getting-started/folder_structure.md @@ -0,0 +1,579 @@ +# Folder Structure + +Here's a rundown of the options for your folders and default files, followed by detailed explanations of each. + +> **Tip:** We recommend using a [prefix](configuration.md#prefix) (usually `nix/`) that specifies a root folder that in turn holds these folders. + +## High-level + +* `flake.nix` for the default flake +* `formatter.nix` for the default formatter +* `devshell.nix` for the default devshell +* `package.nix` for the default package +* `checks/` for flake checks. +* `devshells/` for devshells. +* `hosts/` for machine configurations. +* `lib/` for Nix functions. +* `modules/` for NixOS and other modules. +* `packages/` for packages. +* `templates/` for flake templates. + + +## File arguments + +Each file typically gets passed a number of arguments. + +### per-system + +Some of the files are instantiated multiple times, once per configured system. See [configuration](configuration.md#systems) on how the list of systems is defined. + +Those take the following arguments: + +* `inputs`: maps to the flake inputs. +* `flake`: maps to the flake itself. It's a shorthand for `inputs.self`. +* `system`: the current system attribute. +* `perSystem`: contains the packages of all the inputs, filtered per system. + Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. +* `pkgs`: an instance of nixpkgs, see [configuration](configuration.md#nixpkgsconfig) on how it's configured. + + +## **flake.nix** for the default flake + +This is the default flake.nix file. In general you won't need to modify this very much, except for some basic configurations (described [here](configuration.md)), as you'll be putting your main configurations in their own nix files in their own folders as described here in this document. + +## **formatter.nix** for the default formatter + +This is where you can provide default formatting. For an example, check out the one [used right here in Blueprint](https://github.com/numtide/blueprint/blob/main/formatter.nix). + +## **devshell.nix** for the default devshell + +This file holds the configuration for the default devshell, which you can run by simply typing: + +``` +nix develop +``` + +(We provide an example in our [install guide](install.md).) + + +## **devshells/** + +In addition to the default devshell.nix file, you can configure multiple devshells for different scenarios, such as one for a backend build and one for a frontend build. (See later in this doc for an example.) You can configure devshells through either .nix files or through .toml files. + +The `nix` files under the devshells folder are expected to evaluate into a shell derivation, normally the result of calling `mkShell`. + +There might be many different `mkShell` implementations, like the one present in `nixpkgs` or the one from [`numtide/devshell`](https://github.com/numtide/devshell), and perhaps others. The one you choose depends on the features you might want to use in your environment, like service management, modularity, command menu, etc. + +Here's an example with the mkShell from nixpkgs: + +```nix +# devshell.nix +# Using mkShell from nixpkgs +{ pkgs, perSystem, ... }: +pkgs.mkShell { + packages = [ + perSystem.blueprint.default + pkgs.terraform + ]; +} +``` + +And here's an example of one that uses mkShell from [numtide/devshell](https://github.com/numtide/devshell): + +```nix +# devshell.nix +# Using mkShell from numtide/devshell +# You are expected to add inputs.devshell in your flake. +{ pkgs, perSystem, ... }: +perSystem.devshell.mkShell { + + imports = [ + # You might want to import other reusable modules + (perSystem.devshell.importTOML ./devshell.toml) + ]; + + env = [ + # Add bin/ to the beginning of PATH + { name = "PATH"; prefix = "bin"; } + ]; + + # terraform will be present in the environment menu. + commands = [ { package = pkgs.terraform; } ]; +} +``` + +#### TOML devshells + +`toml` shells are loaded with [devshell](https://numtide.github.io/devshell) but you are required to add +`inputs.devshell` to your flake. + +```toml +# devshell.toml + +# see https://numtide.github.io/devshell/extending.html +imports = [ "./modules/common.toml" ] + +[[commands]] +package = "dbmate" + +[devshell] +packages = ["sops"] + +[[env]] +name = "DB_PASS" +eval = "$(sops --config secrets/sops.yaml --decrypt secrets/db_pass)" + +[serviceGroups.database] +description = "Runs a database in the backgroup" +[serviceGroups.database.services.postgres] +command = "postgres" +[serviceGroups.database.services.memcached] +command = "memcached" +``` + +## **Hosts** for machine configurations + +## `hosts//(default.nix|configuration.nix|darwin-configuration.nix,system-configuration.nix)` + +Nix runs on many different operating systems and architecture. When you create a flake, you can define what systems it can produce outputs for. + +You can configure your project to work with different hosts, which are specific computers or systems. + +> **Note:** Whereas systems refer to operating systems running in conjunction with a specific architecture, a host refers to specific, single machine (virtual or physical) that runs Nix or NixOS. + +Each folder contains either a NixOS or nix-darwin configuration: + +### `configuration.nix` + +Evaluates to a NixOS configuration. + +Additional values passed: + +* `inputs` maps to the current flake inputs. +* `flake` maps to `inputs.self`. +* `perSystem`: contains the packages of all the inputs, filtered per system. + Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. + +Flake outputs: + +* `nixosConfigurations.` +* `checks..nixos-` - contains the system closure. + +##### NixOS example + +```nix +{ flake, inputs, perSystem, ... }: +{ + imports = [ + inputs.srvos.nixosModules.hardware-hetzner-cloud + flake.modules.nixos.server + ]; + + environment.systemPackages = [ + perSystem.nixos-anywhere.default + ]; + + nixpkgs.hostPlatform = "x86_64-linux"; + + system.stateVersion = "24.05"; +} +``` + +### `darwin-configuration.nix` + +Evaluates to a [nix-darwin](https://github.com/LnL7/nix-darwin) configuration. + +To support it, also add the following lines to the `flake.nix` file: + +```nix +{ + inputs.nix-darwin.url = "github:LnL7/nix-darwin"; +} +``` + +Additional values passed: + +* `inputs` maps to the current flake inputs. +* `flake` maps to `inputs.self`. +* `perSystem`: contains the packages of all the inputs, filtered per system. + Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. + +Flake outputs: + +* `darwinConfiguration.` +* `checks..darwin-` - contains the system closure. + +### `system-configuration.nix` + +Evaluates to a [system-manager](https://github.com/numtide/system-manager) configuration. + +To support it, also add the following lines to the `flake.nix` file: + +```nix +{ + inputs.system-manager.url = "github:numtide/system-manager"; +} +``` + +Additional values passed: + +* `inputs` maps to the current flake inputs. +* `flake` maps to `inputs.self`. +* `perSystem`: contains the packages of all the inputs, filtered per system. + Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. + +Flake outputs: + +* `systemConfiguration.` +* `checks..system-` - contains the system closure. + +### `default.nix` + +If present, this file takes precedence over `configuration.nix` and `darwin-configuration.nix` and is designed as an escape hatch, allowing the user complete control over `nixosSystem` or `darwinSystem` calls. + +```nix +{ flake, inputs, ... }: +{ + class = "nixos"; + + value = inputs.nixpkgs-unstable.lib.nixosSystem { + system = "x86_64-linux"; + ... + }; +} +``` + +Additional values passed: + +* `inputs` maps to the current flake inputs. +* `flake` maps to `inputs.self`. + +Expected return value: + +* `class` - type of system. Currently "nixos" or "nix-darwin". +* `value` - the evaluated system. + +Flake outputs: + +> Depending on the system type returned, the flake outputs will be the same as detailed for NixOS or Darwin above. + +## `hosts//users/(.nix|/home-configuration.nix)` + +Defines a configuration for a Home Manager user. Users can either be defined as a nix file or directory containing a `home-configuration.nix` file. + +Before using this mapping, add the `home-manager` input to your `flake.nix` file: + +```nix +{ + inputs = { + home-manager.url = "github:nix-community/home-manager"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + }; +} +``` + +Additional values passed: + +* `inputs` maps to the current flake inputs. +* `flake` maps to `inputs.self`. +* `perSystem`: contains the packages of all the inputs, filtered per system. + Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. +* other provided module arguments. + Eg: home-manager provides `osConfig`, the host nixos/nix-darwin configuration. + +> **Tip:** The simplest way to have a common/shared user configuration between multiple systems is to create a module at `modules/home/.nix` ([docs](#modulestypenamenamenix)), and import that module from `inputs.self.homeModules.` for each user that should inherit it. This pattern makes it easy to apply system-specific customizations on top of a shared, generic configuration. An example of this setup is shown in the following template: `templates/nixos-and-darwin-shared-homes`. + +#### NixOS and nix-darwin + +If `home-manager` is an input to the flake, each host with any users defined will have the appropriate home-manager module imported and each user created automatically. + +The options `home-manager.useGlobalPkgs` and `home-manager.useUserPkgs` will default to true. + +#### Standalone configurations + +Users are also standalone Home Manager configurations. A user defined as `hosts/pc1/users/max.nix` can be applied using the `home-manager` CLI as `.#max@pc1`. The output name can be elided entirely if the current username and hostname match it, e.g. `home-manager switch --flake .` (note the lack of `#`). + +Because the username is part of the path to the configuration, the `home.username` option will default to this username. This can be overridden manually. Likewise, `home.homeDirectory` will be set by default based on the username and operating system (`/Users/${username}` on macOS, `/home/${username}` on Linux). + +## **lib/** for Nix functions. + +### `lib/default.nix` + +Loaded if it exists. + +Inputs: + +* `flake` +* `inputs` + +Flake outputs: + +* `lib` - contains the return value of `lib/default.nix` + +Eg: + +```nix +{ flake, inputs }: +{ } +``` + +## **`modules/`** for NixOS and other modules. + +### `modules//(|.nix)` + +Where the type can be any folder name. + +For the following folder names, we also map them to the following outputs: + +* "darwin" → `darwinModules.` +* "home" → `homeModules.` +* "nixos" → `nixosModules.` + +These and other unrecognized types also exposed as `modules..`. + +If a module is wrapped in a function that accepts one (or more) of the following arguments: + +* `flake` +* `inputs` + +Then that function is called before exposing the module as an output. This allows modules to refer to the flake where it is defined, while the module arguments refer to the flake where the module is consumed. Those can be but do not need to be the same flake. + + +## **`packages/`** for packages. + +### `package.nix`, `formatter.nix`, `packages/(.nix|/default.nix)` + +This `packages/` folder contains all your packages. + +For single-package repositories, we also allow a top-level `package.nix` that +maps to the "default" package. + +Inputs: + +The [per-system](#per-system) values, plus the `pname` attribute, where pname refers to the package name. + +Flake outputs: + +* `packages..` - will contain the package +* `checks..pkgs-` - also contains the package for `nix flake check`. +* `checks..pkgs--` - adds all the package `passthru.tests` + +To consume a package inside a host from the same flake, `perSystem.self.` + +#### `default.nix` or top-level `package.nix` + +Takes the "per-system" arguments. On top of this, it also takes a `pname` argument. + +## **`checks/`** for flake checks. + +### `checks/(.nix|/default.nix)` + +The `checks/` folder can be populated by packages that will be run when `nix flake checks` is invoked. + +The flake checks are also populate by some of the other attributes, like `packages` and `hosts`. + +Inputs: + +* The [per-system](#per-system) values, plus the `pname` attribute. + +Flake outputs: + +* `checks..` - will contain the package + +## **`templates/`** for flake templates. + +### `templates//` + +Use this if you want your project to be initializable using `nix flake init`. + +This is what is used by blueprint in the [install](install.md) section. + +If no name is passed, it will look for the "default" folder. + +Flake outputs: + +* `templates. -> path` + +# Example devshells + +Because of the presence of Bluprint, nix files contained in these folders and their subfolders are immediately available. + +As an example, let's create two devshell setups and put them under the devshells folder. + +1. Create a new Blueprint project by creating a new folder and typing `nix flake init -t github:numtide/blueprint` +2. Create a folder inside the project folder called `devshells` (all lowercase) by typing `mkdir devshells` (if one doesn't already exist). +3. Move to the packages folder can create two folders under it: `mkdir backend && mkdir frontend`. + +Go into the backend folder, and create a file called `default.nix` and paste the following into it: + +```nix +{ pkgs }: +pkgs.mkShell { + # Add build dependencies + packages = [ + pkgs.nodejs_23 + pkgs.geany + ]; + + # Add environment variables + env = { }; + + # Load custom bash code + shellHook = '' + export PS1="(backend) $PS1" + ''; +} +``` + +This code will create a devshell that includes node.js and the IDE called Geany. It also sets the prompt to show the word `(backend)` as a reminder you're working in the bakcend. You can use this devshell for backend development. + +Now move over to the `frontend` folder. Create a file called `default.nix` and paste the following into it: + +```nix +{ pkgs }: +pkgs.mkShell { + # Add build dependencies + packages = [ + pkgs.nodejs_23 + pkgs.geany + pkgs.nodePackages."@angular/cli" + ]; + + # Add environment variables + env = { }; + + # Load custom bash code + shellHook = '' + export PS1="(frontend) $PS1" + ''; +} +``` + +This is similar to the backend, but you'll notice it also includes the CLI tools for Angular for frontend development. This code also sets the prompt to say `(frontend)` to remind you you're working in the front end. + +Save both files and move to the root folder of the project. + +Now you can invoke either development shell by typing one of the following: + +* `nix develop .#backend` to launch the back end shell +* `nix develop .#frontend` to launch the front end shell + +# Example Hosts and Modules + +This example comes from one of our available templates called [NixOS and Darwin Shared Homes Template](./built_in_templates.md#nixos-and-darwin-shared-homes-template). + +Here we create two Blueprint folders, hosts and modules with the following subfolders: + +``` +root folder +├── flake.nix +├── hosts +│   ├── my-darwin +│   │   ├── darwin-configuration.nix +│   │   └── users +│   │   └── me +│   │   └── home-configuration.nix +│   └── my-nixos +│   ├── configuration.nix +│   └── users +│   └── me +│   └── home-configuration.nix +├── modules +│   ├── home +│   │   └── home-shared.nix +│   └── nixos +│   └── host-shared.nix +└── README.md + +``` + +If you run the above command, this is the set of files you'll get. Take a look at the difference between darwin-configuration.nix under hosts/my-darwin and configuration.nix under hosts/my-nixos. + +# Example Checks + +Let's look at how you can put individual tests in the checks folder. + +Start by creating a new folder and initializing the Flake with Blueprint: + +``` +nix flake init -t github:numtide/blueprint +``` + +Then create a folder called src, and another folder next to it called checks. + +In the src folder, create three python files: + +1. main.py + +```python +from utils import string_length + +if __name__ == "__main__": + test_str = "hello" + print(f"Length of '{test_str}' is {string_length(test_str)}") +``` + +2. utils.py + +```python +def string_length(s): + return len(s) +``` + +(As you can see, we're keeping this incredibly simple for demonstration purposes.) + +3. test_length.py + +```python +from utils import string_length + +def test_string_length(): + assert string_length("hello") == 5 + assert string_length("") == 0 + assert string_length("squirrel!") == 8 +``` + +Next, in the checks folder, create a file called test.nix. (Really you can call it anything you want, as long as it has a nix extension.) And place the following in it: + +```nix +{ pkgs, system, ... }: + +let + pythonEnv = pkgs.python3.withPackages (ps: with ps; [ pytest ]); +in +pkgs.runCommand "string-length-test" + { + buildInputs = [ pythonEnv ]; + src = ./../src; + } '' + cp -r $src/* . + # Run pytest, save output to file + if ! pytest > result.log 2>&1; then + cat result.log >&2 # dump the error to stderr so nix shows it + exit 1 + fi + touch $out + '' +``` + +Now run: + +``` +nix flake check +``` + +And your test will run. Because it's correct, you won't see any output. So perhaps try adjusting the function to make it purposely return the wrong number: + +```python +def string_length(s): + return len(s) + 1 +``` + +Then when you run `nix flake check` you should see the output from the pytest tool. + +> **Note:** You'll actually only see the last part of the output. At the bottom will be a message explaining how to view the full logs. It will be similar to this: +> +> *For full logs, run 'nix log /nix/store/8qqfm9i0b3idljh1n14yqhc12c5dv8j2-string-length-test.drv'.* +> + +From there you can see the full output from pytest, including the assertion failures. diff --git a/docs/content/getting-started/install.md b/docs/content/getting-started/install.md index ececcc4..0cf5a23 100644 --- a/docs/content/getting-started/install.md +++ b/docs/content/getting-started/install.md @@ -1,19 +1,14 @@ -*[todo: We need to put together a short style guide for consistency, including* -* Nix (not nix) -* NixOS (not NixOs etc.) -* Should we say "folder" or "directory"? Younger people seem to prefer "folder" ] - # Installing Blueprint Let's create a small project with Nix, and you'll see how to add Blueprint to your project. -1. Install [Nix](https://nix.dev/install-nix). +1. [Install Nix](https://nix.dev/install-nix) or use NixOS. 2. Run `mkdir my-project && cd my-project` 3. Run `nix flake init -t github:numtide/blueprint`. -Note: After you install Nix, you'll need to enable "experimental features." Find out how here. +Note: After you install Nix, you'll need to enable "experimental features." -This will give you a barebone project structure with a single `flake.nix` file and a single `devshell.nix` file. (It also provides a basic .envrc, which [TODO] and a starter .gitignore file. Make sure you're aware of this .gitignore file before you accidentally overwrite it.) +This will give you a barebone project structure with a single `flake.nix` file and a single `devshell.nix` file. (It also provides a basic .envrc, which lets you configure direnv and a starter .gitignore file. Make sure you're aware of this .gitignore file before you accidentally overwrite it.) Normally, without Blueprint, you would typically include a devShell section inside your flake.nix file. In that scenario, when you want to start a new project with a similar toolset, you'll likely need to copy over the devShell section of your flake.nix file to the new project's flake.nix file. But by using Blueprint, we've split out the devShell into its own file, allowing you to simply copy the file over. @@ -60,7 +55,7 @@ When you run a nix command (such as `nix develop`), this flake.nix file is evalu * The devShell.nix file in the root of your project * A folder structure -You create the folder structure based on the available list of folders (found here). +You create the folder structure based on the available list of folders [found here](folder_structure.md). # A Sample Environment @@ -69,6 +64,8 @@ Let's set up a development environment that includes: * Python * Python's numpy package +> **Tip:** In this section we'll be creating a default developer environment. You can also set up multiple developer environments and place them in the devshell folder as shown in the devshell section [here](folder_structure.md). + Open up the devshell.nix file in your favorite editor, and update it to look like this: ```nix @@ -138,23 +135,113 @@ Check out: * Guides * Contributing -# (Optional) Configuring direnv +# Adding folders -Included in the initial files created by Blueprint is a filed called .envrc. This file contains code to configure direnv, which allows you to enter a devshell simply by switching to the folder containing your project. That means you don't need to type `nix develop` after entering the folder. Then when you move up and out of the folder, you'll automatically exit the environment. +Next, we'll add some folders into your project to give you an idea of how the +folder system works. -For more information on configuring this feature, check out our guide at [Configuring Direnv](../guides/configuring_direnv.md) +> **Tip:** It's often good practice to put the folder structure inside its own root folder. That way the folders will be grouped together and easy to distinguish from other folders. As an example, look at [NumTide treefmt](https://github.com/numtide/treefmt). + +Let's start with a root folder to hold the other folders. We'll use "nix" as that's the standard one we've created. Open up your root flake.nix file and expand the outputs line so it takes up multiple lines, and then add in the following prefix attribute: + +```nix + outputs = inputs: + inputs.blueprint { + inherit inputs; + prefix = "nix/"; + }; +``` + +Now create a `nix` folder at the root of your project alongside the flake.nix and devshell.nix files. + +Now you're ready to create some folders. + +First, remember that folders are detected automatically by Blueprint. That way, you can drop in place pre-built packages. For example, on another project, you might have built a package that configures mysql. In that project you placed it in a folder called packages. You can then simply create a folder in your new project also called packages, and drop the mysql file in there, and you're good to go. No messing around with giant monolithic flake.nix file. + +Let's do something similar. Let's add some documentation to your app. Suppose we want to set up MkDocs with your project. + +> **Tip:** Remember, the whole point of Nix is to be able to set up reproducible environments. What that means is you don't need to install MkDocs globally. Instead, you can configure it directly in your project. + +1. Under the `nix` folder, create another folder called `packages` (all lowercase). +2. Then under `packages` create a folder called `docs`. +3. Inside the `docs` folder, paste the following code into a file called `default.nix`: + +```nix +{ + pkgs, + perSystem, + ... +}: +pkgs.stdenvNoCC.mkDerivation { + name = "docs"; + + unpackPhase = '' + cp ${../../../mkdocs.yml} mkdocs.yaml + cp -r ${../../../docs} docs + ''; + + nativeBuildInputs = with pkgs.python3Packages; [ + mike + mkdocs + mkdocs-material + mkdocs-awesome-nav + ]; + + buildPhase = '' + mkdocs build + ''; + installPhase = '' + mv site $out + ''; +} +``` -## Creating a root folder +> **Tip:** Because Blueprint is present, this code will get loaded automatically as needed. And notice how it can be reused; indeed for this example, we simply copied it over from the [Blueprint project itself](https://github.com/numtide/blueprint/blob/main/packages/docs/default.nix). +This code defines a derivation that builds the documentation. Before you can use it, however, you'll need some documentation. So again off the root folder of your project, create a folder called `docs`. This is where you'll put the documentation. + +Inside the `docs` folder, create file called `index.md` and paste in perhaps the following: + +```md +# Welcome to my amazing app! +We've built this amazing app and hope you enjoy it! +``` +Next, we need a file that configures MkDocs called mkdocs.yml. In the root folder, create the file `mkdocs.yml` and paste the following in it: +``` +site_name: AwesomeProject +``` +Now let's build the docs using the mkdocs app. We'll build a static site. From your root folder, type: -## Adding a host +``` +nix build .#docs +``` -TODO +You'll see a `results` folder appear. This contains the output from the mkdocs, which is the built website. +If you want to run the built-in mkdocs server to try out your site, type: -## Adding a package +``` +nix develop .#docs +``` -TODO +Notice by calling nix develop, we're entering a development shell. But that happens only after we run the derivation. The derivation will compile our documents into a static site again (if necessary) and make the mkdocs command available to us while in the shell. + +Open up a browser and head to `http://127.0.0.1:8000/` and you should see the +documentation open with a header "Welcome to my amazing app!" and so on. + +## What did this demonstrate? + +Without Blueprint installed, you would have had to place the above default.nix file containing the mkdocs code inside your main flake.nix file, or link to it manually. But because of Blueprint, your setup will automatically scan a set of predetermined folders (including Packages) for files and find them automatically, making them available to use. + +> **Tip:** If you want to see what folders are available, head over to our +[folder strutures](folder_structure.md) documentation. + + +# (Optional) Configuring direnv + +Included in the initial files created by Blueprint is a filed called .envrc. This file contains code to configure direnv, which allows you to enter a devshell simply by switching to the folder containing your project. That means you don't need to type `nix develop` after entering the folder. Then when you move up and out of the folder, you'll automatically exit the environment. + +For more information on configuring this feature, check out our guide at [Configuring Direnv](../guides/configuring_direnv.md) diff --git a/docs/content/examples/examples.md b/docs/content/guides/examples.md similarity index 100% rename from docs/content/examples/examples.md rename to docs/content/guides/examples.md diff --git a/docs/content/reference/reference.md b/docs/content/reference/reference.md deleted file mode 100644 index 4a938e0..0000000 --- a/docs/content/reference/reference.md +++ /dev/null @@ -1 +0,0 @@ -# Reference \ No newline at end of file diff --git a/docs/folder-structure.md b/docs/folder-structure.md deleted file mode 100644 index c323bfa..0000000 --- a/docs/folder-structure.md +++ /dev/null @@ -1,388 +0,0 @@ -# Folder structure - -## High-level - -* `checks/` for flake checks. -* `devshells/` for devshells. -* `hosts/` for machine configurations. -* `hosts/*/users/` for Home Manager configurations. -* `lib/` for Nix functions. -* `modules/` for NixOS and other modules. -* `packages/` for packages. -* `templates/` for flake templates. - -* `devshell.nix` for the default devshell -* `formatter.nix` for the default formatter -* `package.nix` for the default package - -## File arguments - -Each file typically gets passed a number of arguments. - -### per-system - -Some of the files are instantiated multiple times, once per configured system. See [configuration](configuration.md) on how the list of systems is defined. - -Those take the following arguments: - -* `inputs`: maps to the flake inputs. -* `flake`: maps to the flake itself. It's a shorthand for `inputs.self`. -* `system`: the current system attribute. -* `perSystem`: contains the packages of all the inputs, filtered per system. - Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. -* `pkgs`: and instance of nixpkgs, see [configuration](configuration.md) on how it's configured. - -## Mapping - -### `devshell.nix`, `devshells/(.nix|/default.nix)`, `devshell.toml`, `devshells/.toml` - -Contains the developer shell if specified. - -Inputs: - -The [per-system](#per-system) values, plus the `pname` attribute. - -Flake outputs: - -* `devShells..` -* `checks..devshell-` - -Developer shells can be defined in two ways: `.nix` files or `.toml` files. - -Shells with `.nix` syntax take preference if present. - -#### Nix devshells - -`nix` files are expected to evaluate into a shell derivation, normally the result of calling `mkShell`. - -There might be many different `mkShell` implementations, like the one present in `nixpkgs` or the one -from `numtide/devshell`, and perhaps others. The one you choose depends on the features you might want -to use in your environment, like service management, modularity, command menu, etc. - - -```nix -# devshell.nix -# Using mkShell from nixpkgs -{ pkgs, perSystem, ... }: -pkgs.mkShell { - packages = [ - perSystem.blueprint.default - pkgs.terraform - ]; -} -``` - -```nix -# devshell.nix -# Using mkShell from numtide/devshell -# You are expected to add inputs.devshell in your flake. -{ pkgs, perSystem, ... }: -perSystem.devshell.mkShell { - - imports = [ - # You might want to import other reusable modules - (perSystem.devshell.importTOML ./devshell.toml) - ]; - - env = [ - # Add bin/ to the beginning of PATH - { name = "PATH"; prefix = "bin"; } - ]; - - # terraform will be present in the environment menu. - commands = [ { package = pkgs.terraform; } ]; -} -``` - -#### TOML devshells - -`toml` shells are loaded with [devshell](https://numtide.github.io/devshell) but you are required to add -`inputs.devshell` to your flake. - -```toml -# devshell.toml - -# see https://numtide.github.io/devshell/extending.html -imports = [ "./modules/common.toml" ] - -[[commands]] -package = "dbmate" - -[devshell] -packages = ["sops"] - -[[env]] -name = "DB_PASS" -eval = "$(sops --config secrets/sops.yaml --decrypt secrets/db_pass)" - -[serviceGroups.database] -description = "Runs a database in the backgroup" -[serviceGroups.database.services.postgres] -command = "postgres" -[serviceGroups.database.services.memcached] -command = "memcached" -``` - - -### `hosts//(default.nix|configuration.nix|darwin-configuration.nix,system-configuration.nix)` - -Each folder contains either a NixOS, [nix-darwin](https://github.com/LnL7/nix-darwin) or [system-manager](https://github.com/numtide/system-manager) configuration: - -#### `configuration.nix` - -Evaluates to a NixOS configuration. - -Additional values passed: - -* `inputs` maps to the current flake inputs. -* `flake` maps to `inputs.self`. -* `perSystem`: contains the packages of all the inputs, filtered per system. - Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. -* `hostName` as per the hosts directory name. - -Flake outputs: - -* `nixosConfigurations.` -* `checks..nixos-` - contains the system closure. - -##### NixOS example - -```nix -{ flake, inputs, perSystem, ... }: -{ - imports = [ - inputs.srvos.nixosModules.hardware-hetzner-cloud - flake.modules.nixos.server - ]; - - environment.systemPackages = [ - perSystem.nixos-anywhere.default - ]; - - nixpkgs.hostPlatform = "x86_64-linux"; - - system.stateVersion = "24.05"; -} -``` - -#### `darwin-configuration.nix` - -Evaluates to a [nix-darwin](https://github.com/LnL7/nix-darwin) configuration. - -To support it, also add the following lines to the `flake.nix` file: - -```nix -{ - inputs.nix-darwin.url = "github:LnL7/nix-darwin"; -} -``` - -Additional values passed: - -* `inputs` maps to the current flake inputs. -* `flake` maps to `inputs.self`. -* `perSystem`: contains the packages of all the inputs, filtered per system. - Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. -* `hostName` as per the hosts directory name. - -Flake outputs: - -* `darwinConfiguration.` -* `checks..darwin-` - contains the system closure. - -#### `system-configuration.nix` - -Evaluates to a [system-manager](https://github.com/numtide/system-manager) -configuration. - -To support it, also add the following lines to the `flake.nix` file: - -```nix -{ - inputs.system-manager.url = "github:numtide/system-manager"; -} -``` - -Additional values passed: - -* `inputs` maps to the current flake inputs. -* `flake` maps to `inputs.self`. -* `perSystem`: contains the packages of all the inputs, filtered per system. - Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. -* `hostName` as per the hosts directory name. - -Flake outputs: - -* `systemConfiguration.` -* `checks..system-` - contains the system closure. - -#### `default.nix` - -If present, this file takes precedence over `configuration.nix` and `darwin-configuration.nix` and is designed as an -escape hatch, allowing the user complete control over `nixosSystem` or `darwinSystem` calls. - -```nix -{ flake, inputs, ... }: -{ - class = "nixos"; - - value = inputs.nixpkgs-unstable.lib.nixosSystem { - system = "x86_64-linux"; - ... - }; -} -``` - -Additional values passed: - -* `inputs` maps to the current flake inputs. -* `flake` maps to `inputs.self`. -* `hostName` as per the hosts directory name. - -Expected return value: - -* `class` - type of system. Currently "nixos" or "nix-darwin". -* `value` - the evaluated system. - -Flake outputs: - -> Depending on the system type returned, the flake outputs will be the same as detailed for NixOS or Darwin above. - -### `hosts//users/(.nix|/home-configuration.nix)` - -Defines a configuration for a Home Manager user. Users can either be defined as a nix file or directory containing -a `home-configuration.nix` file. - -Before using this mapping, add the `home-manager` input to your `flake.nix` file: - -```nix -{ - inputs = { - home-manager.url = "github:nix-community/home-manager"; - home-manager.inputs.nixpkgs.follows = "nixpkgs"; - }; -} -``` - -Additional values passed: - -* `inputs` maps to the current flake inputs. -* `flake` maps to `inputs.self`. -* `perSystem`: contains the packages of all the inputs, filtered per system. - Eg: `perSystem.nixos-anywhere.default` is a shorthand for `inputs.nixos-anywhere.packages..default`. -* other provided module arguments. - Eg: home-manager provides `osConfig`, the host nixos/nix-darwin configuration. - -> The simplest way to have a common/shared user configuration between multiple systems is to create a -> module at `modules/home/.nix` ([docs](#modulestypenamenamenix)), and import that module -> from `inputs.self.homeModules.` for each user that should inherit it. This pattern makes -> it easy to apply system-specific customizations on top of a shared, generic configuration. -> An example of this setup is shown in the following template: `templates/nixos-and-darwin-shared-homes`. - -#### NixOS and nix-darwin - -If `home-manager` is an input to the flake, each host with any users defined will have the appropriate home-manager -module imported and each user created automatically. - -The options `home-manager.useGlobalPkgs` and `home-manager.useUserPkgs` will default to true. - -#### Standalone configurations - -Users are also standalone Home Manager configurations. A user defined as `hosts/pc1/users/max.nix` can be -applied using the `home-manager` CLI as `.#max@pc1`. The output name can be elided entirely if the current username -and hostname match it, e.g. `home-manager switch --flake .` (note the lack of `#`). - -Because the username is part of the path to the configuration, the `home.username` option will default to -this username. This can be overridden manually. Likewise, `home.homeDirectory` will be set by default based -on the username and operating system (`/Users/${username}` on macOS, `/home/${username}` on Linux). - -### `lib/default.nix` - -Loaded if it exists. - -Inputs: - -* `flake` -* `inputs` - -Flake outputs: - -* `lib` - contains the return value of `lib/default.nix` - -Eg: - -```nix -{ flake, inputs }: -{ } -``` - -### `modules//(|.nix)` - -Where the type can be any folder name. - -For the following folder names, we also map them to the following outputs: - -* "darwin" → `darwinModules.` -* "home" → `homeModules.` -* "nixos" → `nixosModules.` - -These and other unrecognized types also exposed as `modules..`. - -If a module is wrapped in a function that accepts one (or more) of the following arguments: - -* `flake` -* `inputs` - -Then that function is called before exposing the module as an output. -This allows modules to refer to the flake where it is defined, while the module arguments refer to the flake where the module is consumed. Those can -be but do not need to be the same flake. - -### `package.nix`, `formatter.nix`, `packages/(.nix|/default.nix)` - -This `packages/` folder contains all your packages. - -For single-package repositories, we also allow a top-level `package.nix` that -maps to the "default" package. - -Inputs: - -The [per-system](#per-system) values, plus the `pname` attribute. - -Flake outputs: - -* `packages..` - will contain the package -* `checks..pkgs-` - also contains the package for `nix flake check`. -* `checks..pkgs--` - adds all the package `passthru.tests` - -To consume a package inside a host from the same flake, `perSystem.self.` - -#### `default.nix` or top-level `package.nix` - -Takes the "per-system" arguments. On top of this, it also takes a `pname` -argument. - -### `checks/(.nix|/default.nix)` - -The `checks/` folder can be populated by packages that will be run when `nix flake checks` is invoked. - -The flake checks are also populate by some of the other attributes, like `packages` and `hosts`. - -Inputs: - -* The [per-system](#per-system) values, plus the `pname` attribute. - -Flake outputs: - -* `checks..` - will contain the package - -#### `templates//` - -Use this if you want your project to be initializable using `nix flake init`. - -This is what is used by blueprint in the [getting started](getting-started.md) section. - -If no name is passed, it will look for the "default" folder. - -Flake outputs: - -* `templates. -> path` diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 09cf185..0000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,30 +0,0 @@ -# Getting started - -1. Install [Nix](https://nix.dev/install-nix). -2. Run `mkdir my-project && cd my-project` -3. Run `nix flake init -t github:numtide/blueprint`. - -This will give you a barebone project structure with a single `flake.nix` file containing the following content: - -```nix -{ - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - blueprint.url = "github:numtide/blueprint"; - }; - - outputs = inputs: inputs.blueprint { inherit inputs; }; -} -``` - -From that point you won't have to touch this file (unless you're adding new inputs). - -The rest happens in the [folder structure](folder-structure.md). - -## Adding a host - -TODO - -## Adding a package - -TODO diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index e69de29..0000000 diff --git a/templates/default/.envrc b/templates/default/.envrc old mode 100755 new mode 100644