From a7e20e203c851f5298ee5ff98b79fe457f6d02d7 Mon Sep 17 00:00:00 2001 From: technofab Date: Sat, 20 Dec 2025 21:27:10 +0100 Subject: [PATCH] docs: write docs, add README --- README.md | 44 ++++++++++++++++++ cells/repo/docs.nix | 6 +++ docs/api.md | 96 +++++++++++++++++++++++++++++++++++++++ docs/concepts.md | 75 ++++++++++++++++++++++++++++++ docs/direnv.md | 37 +++++++++++++++ docs/index.md | 29 ++++++++++++ docs/libraries.md | 47 +++++++++++++++++++ docs/tutorial.md | 108 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 442 insertions(+) create mode 100644 README.md create mode 100644 docs/api.md create mode 100644 docs/concepts.md create mode 100644 docs/direnv.md create mode 100644 docs/libraries.md create mode 100644 docs/tutorial.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..3196c05 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# Rensa + +Rensa is a powerful and flexible Nix flake framework designed to organize your Nix projects into logical units called "Cells". +It is heavily inspired by [divnix/std](https://github.com/divnix/std) but aims to provide a streamlined experience. +Feel free to check out std's awesome [docs](https://std.divnix.com/reference/std.html) too. + +## Features + +- **Cells**: Organize your code into functional domains (e.g., `backend`, `frontend`, `devops`). +- **Blocks**: Define types of outputs within a cell (e.g., `packages`, `devShells`, `nixosConfigurations`). +- **Actions**: Integrate with the CLI to run commands defined in your cells. +- **Cell Flakes**: Each cell can be its own flake, allowing for modular dependency management. + +## Quick Start + +1. **Initialize a Flake**: Create a `flake.nix` that uses `rensa`. +1. **Define Cells**: Create a `cells/` directory. +1. **Add Blocks**: Add `.nix` files in your cells to define outputs. + +### Example `flake.nix` + +```nix +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + ren.url = "gitlab:rensa-nix/core"; + }; + + outputs = {ren, ...}@inputs: ren.buildWith { + inherit inputs; + cellsFrom = ./cells; + cellBlocks = with ren.blocks; [ + (simple "packages") + (simple "devShells") + ]; + } { + packages = ren.select inputs.self [ + ["my-cell" "packages"] + ]; + }; +} +``` + +See the [docs](https://rensa.projects.tf) for more. diff --git a/cells/repo/docs.nix b/cells/repo/docs.nix index ada65e8..eda2205 100644 --- a/cells/repo/docs.nix +++ b/cells/repo/docs.nix @@ -31,6 +31,11 @@ in }; nav = [ {"Introduction" = "index.md";} + {"Concepts" = "concepts.md";} + {"Tutorial" = "tutorial.md";} + {"API Reference" = "api.md";} + {"Related Libraries" = "libraries.md";} + {"Direnv Integration" = "direnv.md";} ]; markdown_extensions = [ { @@ -41,6 +46,7 @@ in "pymdownx.superfences" "pymdownx.escapeall" "fenced_code" + "admonition" ]; }; }; diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..2500ca3 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,96 @@ +# API Reference + +## `rensa.buildWith` + +The main entry point for creating a Rensa flake. + +### Arguments + +- `inputs`: The flake inputs. +- `cellsFrom`: Path to the directory containing your cells. +- `cellBlocks`: A list of blocks to load for each cell. +- `transformInputs` (optional): A function to transform inputs before they are passed to cells. + +### Returns + +A standard Nix flake output set. + +## `rensa.build` + +The underlying builder function used by `buildWith`. It returns the raw Rensa output structure without the recursive update functor. + +### Arguments + +- `inputs`: The flake inputs. +- `cellsFrom`: Path to the directory containing your cells. +- `cellBlocks`: A list of blocks to load for each cell. +- `transformInputs` (optional): A function to transform inputs. + +### Returns + +An attribute set containing: + +- `output`: The generated flake outputs. +- `__ren`: Internal metadata about the cells and blocks. + +## `rensa.blocks` + +Helper functions to define blocks. + +### `simple` + +```nix +simple "name" +``` + +Creates a block definition where the type matches the name. + +### `dynamic` + +```nix +dynamic "name" +``` + +Creates a block definition for dynamic content. This allows integration of the `std` cli +by passing through actions etc. from a cell. + +Currently not really used. + +## `rensa.select` + +Helper to select specific outputs from the generated flake. + +```nix +select inputs.self [ + ["cellName" "blockName" "outputName"] +] +``` + +## `rensa.filter` + +Filters the generated flake outputs based on a predicate and paths. + +```nix +filter predicate inputs.self [ + ["cellName" "blockName" "outputName"] +] +``` + +### Arguments + +- `predicate`: A function or boolean. If `true`, returns the attribute as is. If a function, it's used to filter the attributes of the target. +- `target`: The flake outputs (usually `inputs.self`). +- `paths`: A list of paths to select. Supports wildcards (`*`). + +## `rensa.get` + +Retrieves a single attribute from the flake outputs. + +```nix +get inputs.self ["cellName" "blockName" "outputName"] +``` + +### Arguments + +- `target`: The flake outputs. +- `path`: The path to the attribute to retrieve. diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 0000000..4740c50 --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,75 @@ +# Concepts + +Rensa is built around a few core concepts that help structure your Nix projects. + +## Cells + +A **Cell** is the fundamental unit of organization in Rensa. +It represents a functional domain or a component of your system. +For example, you might have cells for: + +- `backend`: Your backend services. +- `frontend`: Your frontend applications. + +Cells are directories located in the `cellsFrom` directory (usually `cells/` or `nix/`). + +### Cell Flakes + +A unique feature of Rensa is that each cell can be a self-contained Flake. +If a cell directory contains a `flake.nix`, Rensa will treat it as a flake and pass its outputs to the cell's blocks. +This allows you to manage dependencies at the cell level, keeping your top-level `flake.nix` clean. + +!!! success "Advantage" + + These nested flakes are also evaluated lazily, so inputs are only fetched if they are actually used. + +## Blocks + +A **Block** defines a specific type of output within a cell. Blocks are typically defined as `.nix` files within a cell directory. +Rensa provides different block types to handle various use cases. + +### Simple Blocks + +`rensa.blocks.simple` is the most common block type. +Use it to tell Rensa which blocks you have/it should load. + +**Example**: + +```nix +blocks = with ren.blocks; [ + (simple "hello") +] +``` + +This will tell Rensa to load any `hello.nix` or `hello/default.nix` in the cells. +It's required to be able to map these to flake outputs (eg. using `ren.select`). + +### Dynamic Blocks + +`rensa.blocks.dynamic` allows for more complex logic and can generate actions. + +## Actions + +**Actions** are commands that can be executed via the Std CLI. +They are defined within blocks and allow you to operationalize your Nix code. +For example, a `deploy` action in an `infra` cell could run the necessary commands to deploy your infrastructure. + +!!! warning + + Actions are also heavily inspired by Std, I added them to Rensa too, but nowadays + I don't see any real use cases for them. If you do, feel free to use and/or extend them :) + +## Performance & Best Practices + +### Single Instantiation of Nixpkgs + +Instantiating `nixpkgs` (e.g., `import inputs.nixpkgs { inherit system; }`) is a computationally expensive operation. In a large project with many cells, doing this repeatedly for every cell or block can significantly slow down evaluation. + +The recommended pattern in Rensa is to instantiate `nixpkgs` **once** in your top-level `flake.nix` and pass it down to your cells. + +1. **Instantiate in `transformInputs`**: In your `flake.nix`, use the `transformInputs` argument of `buildWith` to add the instantiated `pkgs` to the inputs passed to cells. +1. **Use `i.parent.pkgs`**: In your cell flakes, access this pre-instantiated package set via `inputs.parent.pkgs`. + +This ensures that `nixpkgs` is evaluated only once per system, drastically improving performance. + +See [Related Libraries](./libraries.md) for examples of this pattern. diff --git a/docs/direnv.md b/docs/direnv.md new file mode 100644 index 0000000..d4a54ad --- /dev/null +++ b/docs/direnv.md @@ -0,0 +1,37 @@ +# Direnv Integration + +Rensa provides a custom `direnv` integration to make your development experience smoother. +It allows you to automatically load development shells defined in your cells. + +!!! note + + This integration is based on [nix-direnv](https://github.com/nix-community/nix-direnv) + but has been improved and optimized specifically for Rensa usage. + +## Setup + +1. **Create `.envrc`**: Add the following to your `.envrc` file: + + ```bash + source $(fetchurl https://gitlab.com/rensa-nix/direnv/-/raw/v0.3.0/direnvrc "sha256-u7+KEz684NnIZ+Vh5x5qLrt8rKdnUNexewBoeTcEVHQ=") + use ren //backend/devShells/default + ``` + + This will automatically load the devShell `default` defined in `cells/backend/devShells.nix`. + +1. **Allow Direnv**: Run `direnv allow` to trust the configuration. + +## Updating + +To update the `direnvrc` to the latest version (or a specific version), you can use `direnv fetchurl`. + +1. **Run fetchurl**: + + ```bash + direnv fetchurl https://gitlab.com/rensa-nix/direnv/-/raw/v0.3.0/direnvrc + ``` + + *Note: Replace `v0.3.0` with the desired version tag.* + +1. **Update `.envrc`**: The command above will output a new hash. + Replace the previous version and hash in your `.envrc` with the new values. diff --git a/docs/index.md b/docs/index.md index fc63877..04ae320 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1 +1,30 @@ # Rensa Core + +Welcome to the documentation for **Rensa**, a powerful and flexible Nix flake framework. + +Rensa is designed to help you organize your Nix projects into logical units called **Cells**, +making your code more modular, maintainable, and scalable. + +## Getting Started + +If you are new to Rensa, we recommend starting with the [Tutorial](tutorial.md) to build your first project. + +## Core Concepts + +Understand the philosophy behind Rensa by reading about [Concepts](concepts.md), including: + +- **Cells**: Functional domains of your project. +- **Blocks**: Typed outputs within cells. +- **Actions**: CLI commands for your cells. + +## API Reference + +For detailed information on Rensa's functions and helpers, check the [API Reference](api.md). + +## Ecosystem + +See [Related Libraries](libraries.md) to see how to extend Rensa with pre-built solutions for development shells, documentation, CI, and more. + +## Further Reading + +Rensa is inspired by `std`. For more in-depth information on the underlying concepts, you can refer to the [std Reference](https://std.divnix.com/reference/std.html). diff --git a/docs/libraries.md b/docs/libraries.md new file mode 100644 index 0000000..347afe3 --- /dev/null +++ b/docs/libraries.md @@ -0,0 +1,47 @@ +# Related Libraries + +Rensa has a growing ecosystem of compatible libraries designed to work seamlessly with its cell structure. +These libraries typically expose a `lib` output that accepts `pkgs` as an argument. + +## Available Libraries + +Here are some common libraries you might want to use: + +- [**Rensa Devshell**](https://devshell.rensa.projects.tf): _Minimal devshell implementation using Modules._ +- [**Rensa Devtools**](https://devtools.rensa.projects.tf): _Utils and dev tools for the Rensa ecosystem._ +- [**Rensa Utils**](https://utils.rensa.projects.tf): _Utilities for NixOS/darwin systems, home & disko configurations etc._ +- [**Nix-GitLab-CI**](https://nix-gitlab-ci.projects.tf): _Allows (advanced) configuration of GitLab CI using Nix._ +- [**Nixible**](https://nixible.projects.tf): _Ansible but with Nix._ +- [**Tofunix**](https://tofunix.projects.tf): _Combining Nix and Terraform for reproducibility and developer experience._ +- [**Nixlets**](https://nixlets.projects.tf): _Nixlets - like Helm Charts or Grafana Tanka but instead using Nix._ +- [**Nixtest**](https://nixtest.projects.tf): _Test runner for Nix code._ +- [**Soonix**](https://soonix.projects.tf): _Auto generated project files from Nix code, with gitignore handling and many generators._ +- [**NixMkDocs**](https://nix-mkdocs.projects.tf): _Nix library for easy mkdocs integration into projects._ +- [**Torikae**](https://torikae.projects.tf): _Simple CLI to replace versions in files._ + +## Usage Pattern + +When using these libraries within a Cell Flake, you can instantiate them using the `pkgs` passed down from the parent flake. +This follows the "Single Instantiation" best practice. + +### Example: `cells/mycell/flake.nix` + +```nix +{ + inputs = { + # Add the library to your inputs + devshell.url = "gitlab:rensa-nix/devshell?dir=lib"; + + # ... other inputs + }; + + outputs = i: + i + // { + # Instantiate the library using the parent's pkgs + dslib = i.devshell.lib { inherit (i.parent) pkgs; }; + }; +} +``` + +Now you can use `inputs.dslib` in your blocks! diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 0000000..5748b5e --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,108 @@ +# Tutorial + +This tutorial will guide you through creating a new project using Rensa. + +## Prerequisites + +- Nix installed with flakes enabled. +- Optionally `direnv`. + +## Step 1: Initialize the Flake + +Create a new directory for your project and initialize a `flake.nix`: + +```nix title="flake.nix" +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + ren.url = "gitlab:rensa-nix/core"; + }; + + outputs = {ren, self, ...}@inputs: ren.buildWith { + inherit inputs; + cellsFrom = ./nix; + cellBlocks = with ren.blocks; [ + (simple "packages") + ]; + transformInputs = system: inputs: inputs // { + pkgs = import inputs.nixpkgs { inherit system; }; + }; + } { + packages = ren.select self [ + ["backend" "packages"] + ]; + }; +} +``` + +## Step 2: Create a Cell + +Create a directory named `nix` and inside it, create a directory for your first cell, e.g., `backend`. + +```bash +mkdir -p nix/backend +``` + +## Step 3: Add a Block + +Inside `nix/backend`, create a file named `packages.nix`. +This will correspond to the `packages` block we defined in `flake.nix`. + +Since we instantiated `pkgs` in the top-level `flake.nix`, we can access it via `inputs.pkgs` +(because we added it to inputs in `transformInputs`). + +```nix +{ + inputs, + cell, +}: { + default = inputs.pkgs.hello; +} +``` + +### Using Cell Flakes (Advanced) + +If your cell is a flake (has a `flake.nix`), you can access the parent inputs via `inputs.parent`. + +**`nix/backend/flake.nix`**: + +```nix +{ + inputs.common.url = "github:some/common-lib"; + outputs = inputs: inputs // { + # inputs just for the current cell, for example: + common = inputs.common.lib {inherit (inputs.parent) pkgs;}; + }; +} +``` + +See [here](./libraries.md) for more information about this syntax. + +**`cells/backend/packages.nix`**: + +```nix +{ + inputs, + cell, +}: { + default = inputs.common.mkPackage { ... }; +} +``` + +## Step 4: Build + +Now you can build your package using the standard Nix commands: + +```bash +nix build .#backend.packages.default +``` + +Or, if you mapped it in `flake.nix`: + +```bash +nix build .#default +``` + +## Step 5: Setting up Direnv + +Rensa provides a custom `direnv` integration. See the [Direnv Integration](direnv.md) guide for setup instructions.