Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/flake-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ jobs:
strategy:
matrix:
template: ${{ fromJSON(needs.find-templates.outputs.templates) }}
exclude:
- template: "unflake"
steps:
- uses: wimpysworld/nothing-but-nix@main
- uses: cachix/install-nix-action@v31
Expand All @@ -44,6 +46,20 @@ jobs:
nix flake metadata
nix run .#write-flake -L --show-trace
nix flake check -L --show-trace
unflake:
name: Check unflake
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: wimpysworld/nothing-but-nix@main
- uses: cachix/install-nix-action@v31
- uses: DeterminateSystems/magic-nix-cache-action@main
- run: |
set -e -o pipefail
cd templates/unflake
sed -i 's/# flake-file = import/flake-file = import/' default.nix
echo "{ inputs, ... }: { unflake.pkgs = import inputs.nixpkgs {}; }" | tee modules/pkgs.nix
nix-shell . -A unflake.env --run 'write-unflake --verbose'
dev:
name: Check flake dev
runs-on: ubuntu-latest
Expand Down
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,32 @@
<a href="LICENSE"> <img src="https://img.shields.io/github/license/vic/flake-file" alt="License"/> </a>
</p>

# flake-file — Generate flake.nix from flake-parts modules.
# flake-file — Generate flake.nix or unflake.nix from inputs defined as module options.

> `flake-file` and [vic](https://bsky.app/profile/oeiuwq.bsky.social)'s [dendritic libs](https://vic.github.io/dendrix/Dendritic-Ecosystem.html#vics-dendritic-libraries) made for you with Love++ and AI--. If you like my work, consider [sponsoring](https://github.com/sponsors/vic)

**flake-file** lets you generate a clean, maintainable `flake.nix` from modular options, using [flake-parts](https://flake.parts/).
**flake-file** lets you generate a clean, maintainable `flake.nix` from modular options. It works on both flakes and non-flakes environments.

It makes your flake configuration modular and based on the Nix module system. This means you can use
`lib.mkDefault` or anything you normally do with Nix modules, and have them reflected in flake schema values.

> Despite the original flake-oriented name, it NOW also works on _stable Nix_, [_non flakes_](templates/unflake) environments.

<table><tr><td>

## Features

- Flake definition aggregated from all flake-parts modules.
- Flake definition aggregated from Nix modules.
- Schema as [options](https://github.com/vic/flake-file/blob/main/modules/options/default.nix).
- Syntax for nixConfig and follows is the same as in flakes.
- `flake check` ensures files are up to date.
- App for generator: `nix run .#write-flake`
- App for `flake.nix` generator: `nix run .#write-flake`
- Custom do-not-edit header.
- Automatic flake.lock [flattening](#automatic-flakelock-flattening).
- Incrementally add [flake-parts-builder](#parts_templates) templates.
- Pick flakeModules for different feature sets.
- [Dendritic](https://vic.github.io/dendrix/Dendritic.html) flake template.
- Works on stable Nix, [unflake](templates/unflake) environments.

</td><td>

Expand Down Expand Up @@ -60,7 +63,7 @@ It makes your flake configuration modular and based on the Nix module system. Th
## Who is this for?

- Nix users who want to keep their `flake.nix` modular and maintainable
- Anyone using [flake-parts](https://flake.parts/) and looking to automate or simplify flake input management
- Anyone using Nix modules and looking to automate or simplify flake input management
- Teams or individuals who want to share and reuse flake modules across projects

---
Expand Down Expand Up @@ -148,9 +151,14 @@ The following is a complete example from our [`templates/dendritic`](https://git

> Previously, this module included `flake-aspects` and `den` as dependencies. It now provides a pure flake-parts Dendritic setup. If you need the complete [den](https://github.com/vic/den) functionality, use den's `flakeModules.dendritic` instead.

#### [`flakeModules.unflake`](https://github.com/vic/flake-file/tree/main/modules/unflake.nix)

- Defines `flake-file` options.
- Exposes `write-unflake` to generate `unflake.nix` or `npins`. See [templates/unflake](templates/unflake) for usage.

### Flake Templates

#### `default` template
#### [`default`](templates/default) template

A more basic, explicit setup.

Expand Down Expand Up @@ -178,14 +186,18 @@ A more basic, explicit setup.
> [!TIP]
> You can use the `write-flake` app as part of a devshell or git hook.

#### `dendritic` template
#### [`dendritic`](templates/dendritic) template

A template for dendritic setups; includes `flakeModules.dendritic`.

#### `parts` template
#### [`parts`](templates/parts) template

A template that uses `lib.flakeModules.flake-parts-builder`.

#### [`unflake`](templates/unflake) template

Uses [goldstein/unflake](https://codeberg.org/goldstein/unflake) to pin and fetch inputs that were defined as options for non-flakes stable Nix environments.

---

## Available Options
Expand Down
43 changes: 42 additions & 1 deletion dev/modules/_lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,48 @@ let
}
);

nixAttr =
name: value:
let
childIsAttr = builtins.isAttrs value;
childIsOne = builtins.length (builtins.attrNames value) == 1;
nested = lib.head (lib.mapAttrsToList nixAttr value);
in
if childIsAttr && childIsOne then
{
name = "${name}.${nested.name}";
value = nested.value;
}
else
{
inherit name;
value = value;
};

# expr to code
nixCode =
x:
if lib.isStringLike x then
lib.strings.escapeNixString x
else if lib.isAttrs x then
lib.pipe x [
(lib.mapAttrsToList nixAttr)
(map ({ name, value }: "${name} = ${nixCode value}; "))
(values: "{ ${lib.concatStringsSep " " values} }")
]
else if lib.isList x then
lib.pipe x [
(lib.map nixCode)
(values: "[ ${lib.concatStringsSep " " values} ]")
]
else if x == true then
"true"
else if x == false then
"false"
else
toString x;

in
{
inherit inputsExpr isNonEmptyString;
inherit inputsExpr isNonEmptyString nixCode;
}
2 changes: 2 additions & 0 deletions dev/modules/formatter.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
".envrc"
".direnv/*"
"*/.gitignore"
"**/unflake.nix" # generated by: nix-shell . -A unflake.env --run write-unflake
"**/inputs.nix" # generated by: nix-shell . -A unflake.env --run write-inputs
];
};
};
Expand Down
11 changes: 11 additions & 0 deletions modules/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ let
nix-auto-follow
dendritic
import-tree
unflake
;
};

unflake.imports = [
./options
./unflake.nix
];

default.imports = [
./options
./write-flake.nix
Expand Down Expand Up @@ -36,6 +42,11 @@ let
path = ./../templates/default;
};

templates.unflake = {
description = "unflake template";
path = ./../templates/unflake;
};

templates.dendritic = {
description = "dendritic template";
path = ./../templates/dendritic;
Expand Down
64 changes: 64 additions & 0 deletions modules/unflake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{ lib, config, ... }:
let
inherit (config.unflake) pkgs;
inherit (import ./../dev/modules/_lib lib) inputsExpr nixCode;

inputsString = ''
# DO-NOT-EDIT: Generated by github:vic/flake-file for unflake.
# To re-generate use: nix-shell . -A unflake.env --run write-inputs
''
+ (nixCode (inputsExpr config.flake-file.inputs));

inputsFile = pkgs.stdenvNoCC.mkDerivation {
name = "inputs";
passAsFile = [ "inputsString" ];
inherit inputsString;
phases = [ "copy" ];
copy = ''
cp $inputsStringPath $out
'';
};

write-inputs = pkgs.writeShellApplication {
name = "write-inputs";
text = ''
cp ${inputsFile} "''${1:-inputs.nix}"
'';
};

write-unflake = pkgs.writeShellApplication {
name = "write-unflake";
text = ''
nix-shell "${config.unflake.url}" -A unflake-shell --run "unflake -i ${inputsFile} $*"
'';
};

env = pkgs.mkShell {
buildInputs = [
write-inputs
write-unflake
];
};
in
{
options.unflake = {
url = lib.mkOption {
type = lib.types.str;
description = "unflake archive url";
default = "https://codeberg.org/goldstein/unflake/archive/main.tar.gz";
};

pkgs = lib.mkOption {
type = lib.types.raw;
description = "nixpkgs instance for unflake generator";
default = import <nixpkgs> { };
};

env = lib.mkOption {
type = lib.types.raw;
readOnly = true;
internal = true;
default = env;
};
};
}
43 changes: 1 addition & 42 deletions modules/write-flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
...
}:
let
inherit (import ./../dev/modules/_lib lib) inputsExpr isNonEmptyString;
inherit (import ./../dev/modules/_lib lib) inputsExpr isNonEmptyString nixCode;

flake-file = config.flake-file;

Expand All @@ -27,47 +27,6 @@ let
addHeader
];

nixAttr =
name: value:
let
childIsAttr = builtins.isAttrs value;
childIsOne = builtins.length (builtins.attrNames value) == 1;
nested = lib.head (lib.mapAttrsToList nixAttr value);
in
if childIsAttr && childIsOne then
{
name = "${name}.${nested.name}";
value = nested.value;
}
else
{
inherit name;
value = value;
};

# expr to code
nixCode =
x:
if lib.isStringLike x then
lib.strings.escapeNixString x
else if lib.isAttrs x then
lib.pipe x [
(lib.mapAttrsToList nixAttr)
(map ({ name, value }: "${name} = ${nixCode value}; "))
(values: "{ ${lib.concatStringsSep " " values} }")
]
else if lib.isList x then
lib.pipe x [
(lib.map nixCode)
(values: "[ ${lib.concatStringsSep " " values} ]")
]
else if x == true then
"true"
else if x == false then
"false"
else
toString x;

description =
if isNonEmptyString flake-file.description then
''
Expand Down
40 changes: 40 additions & 0 deletions templates/unflake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Unflake

This template is an example of using `flake-file.inputs` in a non-flakes project.

It uses [unflake](https://codeberg.org/goldstein/unflake) to pin and fetch inputs defined as options inside `./modules`.

## Generate `unflake.nix`

The following command is a convenience for generating `unflake.nix` by
first producing a temporary `inputs.nix` from your config and then
running unflake on it.

```shell
nix-shell . -A unflake.env --run write-unflake
```

You can also pass any unflake option:

```shell
nix-shell . -A unflake.env --run 'write-unflake --verbose --backend nix'
```

If you need to see the file that is being passed as `--inputs inputs.nix`
to the unflake command, you can generate it with:

```shell
# (only recommended for debugging)
nix-shell . -A unflake.env --run write-inputs

# then, you can run unflake yourself:
nix-shell https://ln-s.sh/unflake -A unflake-shell --run unflake
```

## Using with [npins](https://github.com/andir/npins)

Unflake has an npins backend to use it run:

```shell
nix-shell . -A unflake.env --run 'write-unflake --backend npins'
```
21 changes: 21 additions & 0 deletions templates/unflake/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
let

outputs =
inputs:
(inputs.nixpkgs.lib.evalModules {
modules = [ (inputs.import-tree ./modules) ];
specialArgs = { inherit inputs; };
}).config;

withInputs =
inputs: outputs:
outputs (
inputs
// {
# uncomment to use local checkout on CI
# flake-file = import ./../../modules;
}
);

in
(import ./unflake.nix withInputs) outputs
10 changes: 10 additions & 0 deletions templates/unflake/modules/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{ inputs, ... }:
{
imports = [ inputs.flake-file.flakeModules.unflake ];

flake-file.inputs = {
flake-file.url = "github:vic/flake-file";
import-tree.url = "github:vic/import-tree";
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
};
}
Loading