diff --git a/.github/workflows/flakehub.yml b/.github/workflows/flakehub.yml new file mode 100644 index 0000000..6db926f --- /dev/null +++ b/.github/workflows/flakehub.yml @@ -0,0 +1,19 @@ +name: "Publish every Git push to master to FlakeHub" +on: + push: + branches: + - "main" +jobs: + flakehub-publish: + runs-on: "ubuntu-latest" + permissions: + id-token: "write" + contents: "read" + steps: + - uses: "actions/checkout@v3" + - uses: "DeterminateSystems/nix-installer-action@main" + - uses: "DeterminateSystems/flakehub-push@main" + with: + name: "tsandrini/practical-flakes-template" + rolling: true + visibility: "public" diff --git a/README.md b/README.md index e016918..a69cece 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ And you're good to go! 👍 3. [treefmt](https://github.com/numtide/treefmt) is the one and only formatter to rule them all 🙏 4. Already preconfigured [github actions](https://docs.github.com/en/actions) - and [gitlab CI](ttps://docs.gitlab.com/ee/ci/) 💪 + and [gitlab CI](https://docs.gitlab.com/ee/ci/) 💪 5. Prepared for custom `lib` overrides 🤓 - depending on what you're currently aiming to write, you might need some custom helpers or library functions, this template @@ -43,7 +43,7 @@ After a proper installation process you can enter the development environment 1. either using [direnv](https://github.com/direnv/direnv) `direnv allow` 2. or directly `nix develop .#dev --impure` -While not many, the code has some required references to the `practicalFlake` +While not many, the code has some required references to the `practicalFlakes` identifier. This can be renamed in the whole project using the script `rename-project` (which is available in the dev environment) @@ -51,4 +51,41 @@ identifier. This can be renamed in the whole project using the script rename-project . myAwesomeApp ``` -## Resources +You're also encouraged to update your flakes with + +```bash +nix flake update +``` + +## Variants + +There are also a few other different variants of the base template that may +be better suited for your needs + +- **main**: The main, default template. +- **home**: Conceptually and structurally the same as the default template, but + also includes prepared and preconfigured + [home-manager](https://github.com/nix-community/home-manager) as well as + examples of how to use it +- **minimal**: Structurally the same as the default template, but stripped of all + of the included examples and additional prepared files +- **isolated**: Centralizes all of the nix related stuff into a `nix/` folder. + This can be useful when you'd like to not pollute your root with stuff not + directly tied to the code. +- **isolated-minimal**: Isolated combined with minimal, that is, structurally the + same as minimal, however, stripped out of all the examples and unnecessary code + +You can install your desired template variant using + +```bash +nix flake init -t github:tsandrini/practical-flakes-template#myVariant +``` + +For example, +`nix flake init -t github:tsandrini/practical-flakes-template#isolated-minimal`. + +## Notes + +- `pkgs` are by default enabled to allow **unfree** licenses, if you'd prefer not + to have this enabled, simply remove the line in the `lib/modules.nix:mkNixpkgs` + helper function diff --git a/flake.nix b/flake.nix index 2ce164f..57f8b1f 100644 --- a/flake.nix +++ b/flake.nix @@ -2,11 +2,12 @@ description = "PracticalFlakesTemplate - Highly opinionated nix flakes starter template that focuses on modularity."; inputs = { + # Base dependencies nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; systems.url = "github:nix-systems/default"; - # dev and formatting + # Development treefmt-nix.url = "github:numtide/treefmt-nix"; devenv.url = "github:cachix/devenv"; mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin"; @@ -22,20 +23,49 @@ systems = import inputs.systems; flake = { - templates = { + templates = let + welcomeText = '' + Hi! You've just created a fresh new flakes project using the + practical-flakes-template. You can start by looking around or + running the development environment either via direnv (`direnv allow`) + or `nix develop .#dev --impure`. + + Furthermore don't forget to rename your project using + `rename-project . myAwesomeNewProject` + + For more info refer to + https://github.com/tsandrini/practical-flakes-template/ + ''; + in { default = inputs.self.templates.main; main = { + inherit welcomeText; path = ./templates/main; description = "Highly opinionated nix flakes starter template that focuses on modularity."; - welcomeText = '' - Hi! You've just created a fresh new flakes project using the - practical-flakes-template. You can start by looking around or - running the development environment either via direnv (`direnv allow`) - or `nix develop .#dev --impure`. - - For more info refer to - https://github.com/tsandrini/practical-flakes-template/ - ''; + }; + + minimal = { + inherit welcomeText; + path = ./templates/minimal; + description = "Minimal version of the highly opiniated nix flakes starter template."; + }; + + isolated = { + inherit welcomeText; + path = ./templates/isolated; + description = "Isolated (./nix) version of the highly opiniated nix flakes starter template."; + }; + + isolated-minimal = { + inherit welcomeText; + path = ./templates/isolated-minimal; + description = "Isolated (./nix) and minimal version of the highly opiniated nix flakes starter template."; + }; + + home = { + inherit welcomeText; + path = ./templates/home; + description = "Full version of the highly opiniated nix flakes starter template that includes prewired home-manager"; }; }; }; diff --git a/templates/home/.envrc b/templates/home/.envrc new file mode 100644 index 0000000..4bf7b2f --- /dev/null +++ b/templates/home/.envrc @@ -0,0 +1,8 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +fi + +if ! use flake .#dev --impure +then + echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 +fi diff --git a/templates/home/.github/workflows/check-on-merge.yml b/templates/home/.github/workflows/check-on-merge.yml new file mode 100644 index 0000000..01a9ff4 --- /dev/null +++ b/templates/home/.github/workflows/check-on-merge.yml @@ -0,0 +1,20 @@ +name: "Run various checks on merge (pull) request" + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + check: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + + - name: Complete nix setup + uses: DeterminateSystems/nix-installer-action@main + + - name: pre-commit checks + run: nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + + - name: Check the flake + run: nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" diff --git a/templates/home/.gitignore b/templates/home/.gitignore new file mode 100644 index 0000000..2d8df24 --- /dev/null +++ b/templates/home/.gitignore @@ -0,0 +1,28 @@ +# OS generated files # +###################### +.DS_Store +.DS_Store? +ehthumbs.db +Icon? +Thumbs.db + +# IDEA Ignores # +################ +*.iml +*.ipr +*.iws +.idea/ +out/ +local.properties + +# Project related # +################ +result/ +build/ + +result +.direnv/ +.devenv/ + +.devcontainer.json +.pre-commit-config.yaml diff --git a/templates/home/.gitlab-ci.yml b/templates/home/.gitlab-ci.yml new file mode 100644 index 0000000..4a0f4c8 --- /dev/null +++ b/templates/home/.gitlab-ci.yml @@ -0,0 +1,14 @@ +image: nixos/nix:latest + +variables: + NIX_CONF_DIR: "/etc/nix" + +before_script: + - echo 'experimental-features = nix-command flakes' > $NIX_CONF_DIR/nix.conf + +check: + script: + - nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + - nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' diff --git a/templates/home/CHANGELOG.md b/templates/home/CHANGELOG.md new file mode 100644 index 0000000..825c32f --- /dev/null +++ b/templates/home/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/templates/home/README.md b/templates/home/README.md new file mode 100644 index 0000000..2c45761 --- /dev/null +++ b/templates/home/README.md @@ -0,0 +1,16 @@ +# practicalFlakes + +## Description + +This flake has been generated from the +[tsandrini/practical-flakes-template](https://github.com/tsandrini/practical-flakes-template/) +project. The next steps for your development are + +1. **development environment** + - either use [direnv](https://github.com/direnv/direnv) and `direnv allow` + - or explicitly enter the shell via `nix develop .#dev --impure` +2. **rename the project** + - while not many, there are some places in the code that have a `practicalFlakes` + identifier, you can use the `rename-project` (available in the dev environment) + script to change these + - `rename-project . myCoolNewProject` diff --git a/templates/home/flake.lock b/templates/home/flake.lock new file mode 100644 index 0000000..3281f1b --- /dev/null +++ b/templates/home/flake.lock @@ -0,0 +1,416 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1700140236, + "narHash": "sha256-OpukFO0rRG2hJzD+pCQq+nSWuT9dBL6DSvADQaUlmFg=", + "owner": "cachix", + "repo": "devenv", + "rev": "525d60c44de848a6b2dd468f6efddff078eb2af2", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1700553346, + "narHash": "sha256-kW7uWsCv/lxuA824Ng6EYD9hlVYRyjuFn0xBbYltAeQ=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "1aabb0a31b25ad83cfaa37c3fe29053417cd9a0f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "mk-shell-bin": { + "locked": { + "lastModified": 1677004959, + "narHash": "sha256-/uEkr1UkJrh11vD02aqufCxtbF5YnhRTIKlx5kyvf+I=", + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "rev": "ff5d8bd4d68a347be5042e2f16caee391cd75887", + "type": "github" + }, + "original": { + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nix2container": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1700389764, + "narHash": "sha256-hMsZ741ri9c4ZQpB6mgLY8KErk0yXVAOUjfNkP1nbbw=", + "owner": "nlewo", + "repo": "nix2container", + "rev": "4400b77e14f3095ee3215a9a5e0f9143bc0e8f2d", + "type": "github" + }, + "original": { + "owner": "nlewo", + "repo": "nix2container", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1700390070, + "narHash": "sha256-de9KYi8rSJpqvBfNwscWdalIJXPo8NjdIZcEJum1mH0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e4ad989506ec7d71f7302cc3067abd82730a4beb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1695644571, + "narHash": "sha256-asS9dCCdlt1lPq0DLwkVBbVoEKuEuz+Zi3DG7pR/RxA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "6500b4580c2a1f3d0f980d32d285739d8e156d92", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1688056373, + "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "home-manager": "home-manager", + "mk-shell-bin": "mk-shell-bin", + "nix2container": "nix2container", + "nixpkgs": "nixpkgs_2", + "systems": "systems_3", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1699786194, + "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/templates/home/flake.nix b/templates/home/flake.nix new file mode 100644 index 0000000..78a7eab --- /dev/null +++ b/templates/home/flake.nix @@ -0,0 +1,99 @@ +{ + description = "practicalFlakes - TODO Add a description of your new project"; + + inputs = { + # Base dependencies + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + systems.url = "github:nix-systems/default"; + + # Development (devenv and treefmt dependencies) + treefmt-nix.url = "github:numtide/treefmt-nix"; + devenv.url = "github:cachix/devenv"; + mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin"; + nix2container = { + url = "github:nlewo/nix2container"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Project specific dependencies + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + # Here you can add additional binary cache substituers that you trust + nixConfig = { + extra-trusted-public-keys = [ + "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" + ]; + extra-substituters = [ + "https://devenv.cachix.org" + ]; + }; + + outputs = inputs @ {flake-parts, ...}: let + inherit (inputs) nixpkgs; + inherit (lib.practicalFlakes) mapModules mkNixpkgs flatten; + + # You should ideally use relative paths in each individual part from ./parts, + # however, if needed you can use the `projectPath` variable that is passed + # to every flakeModule to properly anchor your absolute paths. + projectPath = ./.; + + # We extend the base library with our own custom helpers as well + # as override any of the nixpkgs default functions that we'd like + # to override. This instance is then passed to every part in ./parts so that + # you can use it in your custom modules + lib = nixpkgs.lib.extend (self: _super: { + practicalFlakes = import ./lib { + inherit inputs projectPath; + pkgs = nixpkgs; + lib = self; + }; + }); + specialArgs = {inherit lib projectPath;}; + in + flake-parts.lib.mkFlake {inherit inputs specialArgs;} { + # We recursively traverse all of the flakeModules in ./parts and import only + # the final modules, meaning that you can have an arbitrary nested structure + # that suffices your needs. For example + # + # - ./parts + # - modules/ + # - nixos/ + # - myNixosModule1.nix + # - myNixosModule2.nix + # - default.nix + # - home-manager/ + # - myHomeModule1.nix + # - myHomeModule2.nix + # - default.nix + # - sharedModules.nix + # - pkgs/ + # - myPackage1.nix + # - myPackage2.nix + # - default.nix + # - mySimpleModule.nix + imports = flatten (mapModules ./parts (x: x)); + + # We use the default `systems` defined by the `nix-systems` flake, if you + # need any additional systems, simply add them in the following manner + # + # `systems = (import inputs.systems) ++ [ "armv7l-linux" ];` + systems = import inputs.systems; + flake.lib = lib.practicalFlakes; + + # Finally, we bootstrap the `pkgs` argument to use our custom nixpkgs + # instance bootstrapped with overlays, loaded system and other defaults. + # For more info refer to `lib/modules.nix:mkNixpkgs` + perSystem = { + system, + pkgs, + ... + }: { + _module.args.pkgs = mkNixpkgs nixpkgs system []; + }; + }; +} diff --git a/templates/home/lib/.gitignore b/templates/home/lib/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/home/lib/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/home/lib/_bootstrap-lib.nix b/templates/home/lib/_bootstrap-lib.nix new file mode 100644 index 0000000..93b1c61 --- /dev/null +++ b/templates/home/lib/_bootstrap-lib.nix @@ -0,0 +1,23 @@ +# --- lib/_bootstrap-lib.nix +{lib, ...}: +with lib; +with builtins; rec { + # This file should provide the bare minimum to bootstrap the lib, namely the + # mapModules' function to enable properly loading the library files and its + # functions + + mapFilterAttrs' = pred: f: attrs: filterAttrs pred (mapAttrs' f attrs); + + mapModules' = dir: fn: + mapFilterAttrs' + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules' path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); +} diff --git a/templates/home/lib/asserts.nix b/templates/home/lib/asserts.nix new file mode 100644 index 0000000..f9a50b3 --- /dev/null +++ b/templates/home/lib/asserts.nix @@ -0,0 +1,15 @@ +# --- lib/asserts.nix +{lib, ...}: +with lib; +with builtins; { + # Example assertion + # ----------------- + # assertModulesLoaded = + # cfg: { + # assertion = hasAttr "practicalFlakes" cfg; + # message = '' + # Simple example assertion that checkc if the practicalFlakes namespace + # is present + # ''; + # }; +} diff --git a/templates/home/lib/attrsets.nix b/templates/home/lib/attrsets.nix new file mode 100644 index 0000000..792a14b --- /dev/null +++ b/templates/home/lib/attrsets.nix @@ -0,0 +1,66 @@ +# --- lib/attrsets.nix +{lib, ...}: +with lib; +with builtins; rec { + /* + Apply a map to every attribute of an attrset and then filter the resulting + attrset based on a given predicate function. + + *Type*: `mapFilterAttrs :: (AttrSet b -> Bool) -> (AttrSet a -> AttrSet b) -> AttrSet a -> AttrSet b` + */ + mapFilterAttrs = + # (AttrSet b -> Bool) Predicate used for filtering + pred: + # (AttrSet a -> AttrSet b) Function used for transforming the given AttrSets + f: + # (AttrSet a) Initial attrset + attrs: + filterAttrs pred (mapAttrs' f attrs); + + /* + Recursively merges a list of attrsets. + + *Type*: `mergeAttrs :: [AttrSet] -> AttrSet` + + Example: + ```nix title="Example" linenums="1" + mergeAttrs [ + { keyA = 1; keyB = 3; } + { keyB = 10; keyC = "hey"; nestedKey = { A = null; }; } + { nestedKey = { A = 3; B = 4; }; } + ] + => { keyA = 1; keyB = 10; keyC = "hey"; nestedKey = { A = 3; B = 4; };} + ``` + */ + mergeAttrs = + # ([AttrSet]) The list of attrsets + attrs: + foldl' (acc: elem: acc // elem) {} attrs; + + /* + Recursively flattens a nested attrset into a list of just its values. + + *Type*: `flatten :: AttrSet a -> [a]` + + Example: + ```nix title="Example" linenums="1" + flatten { + keyA = 10; + keyB = "str20"; + keyC = { + keyD = false; + keyE = { + a = 10; + b = "20"; + c = false; + }; + }; + } + => [ 10 "str20" false 10 "20" false ] + ``` + */ + flatten = + # (AttrSet a) Initial nested attrset + attrs: + collect (x: !isAttrs x) attrs; +} diff --git a/templates/home/lib/default.nix b/templates/home/lib/default.nix new file mode 100644 index 0000000..0eace91 --- /dev/null +++ b/templates/home/lib/default.nix @@ -0,0 +1,21 @@ +# --- lib/default.nix +{ + pkgs, + lib, + inputs, + projectPath ? ./.., + ... +}: let + inherit (bootstrap) mapModules'; + + bootstrap = import ./_bootstrap-lib.nix {inherit lib;}; + + practicalFlakes = lib.makeExtensible (self: + with self; + mapModules' ./. (file: + import file { + inherit pkgs lib self inputs projectPath; + })); +in + practicalFlakes.extend + (_self: super: lib.foldr (a: b: a // b) {} (lib.attrValues super)) diff --git a/templates/home/lib/licenses.nix b/templates/home/lib/licenses.nix new file mode 100644 index 0000000..e88c436 --- /dev/null +++ b/templates/home/lib/licenses.nix @@ -0,0 +1,2 @@ +# --- lib/licenses.nix +_: {} diff --git a/templates/home/lib/lists.nix b/templates/home/lib/lists.nix new file mode 100644 index 0000000..9425c0b --- /dev/null +++ b/templates/home/lib/lists.nix @@ -0,0 +1,20 @@ +# --- lib/lists.nix +{lib, ...}: +with lib; +with builtins; rec { + # Example function + # ----------------- + /* + # Map function with flipped arguments. + + # *Type*: `flipMap :: (a -> b) -> [a] -> [b]` + + # Example: + # ```nix title="Example" linenums="1" + # flipmap (x: x * x) [ 1 2 3 ] + # => [ 1 4 9 ] + # ``` + # + */ + # flipMap = flip map; +} diff --git a/templates/home/lib/maintainers.nix b/templates/home/lib/maintainers.nix new file mode 100644 index 0000000..8cf4e05 --- /dev/null +++ b/templates/home/lib/maintainers.nix @@ -0,0 +1,11 @@ +# --- lib/maintainers.nix +_: { + # Example maintainer + # ----------------- + # exampleMaintainer = { + # email = "exampleMaintainer@myMail.com"; + # name = "Example Maintainer"; + # github = "exampleMaintainer"; + # githubId = 12345678; + # }; +} diff --git a/templates/home/lib/modules.nix b/templates/home/lib/modules.nix new file mode 100644 index 0000000..13ae4fe --- /dev/null +++ b/templates/home/lib/modules.nix @@ -0,0 +1,91 @@ +# --- lib/modules.nix +{ + lib, + self, + inputs, + ... +}: let + inherit (self.attrsets) mapFilterAttrs; +in + with lib; + with builtins; rec { + /* + Recursively read a directory and apply a provided function to every `.nix` + file. Returns an attrset that reflects the filenames and directory + structure of the root. + + Notes: + + 1. Files and directories starting with the `_` or `.git` prefix will be completely + ignored. + + 2. If a directory with a `myDir/default.nix` file will be encountered, + the function will be applied to the `myDir/default.nix` file + instead of recursively loading `myDir` and applying it to every file. + + *Type*: `mapModules :: Path -> (Path -> AttrSet a) -> { name :: String; value :: AttrSet a; }` + + Example: + ```nix title="Example" linenums="1" + mapModules ./modules import + => { hardware = { moduleA = { ... }; }; system = { moduleB = { ... }; }; } + + mapModules ./hosts (host: mkHostCustomFunction myArg host) + => { hostA = { ... }; hostB = { ... }; } + ``` + */ + mapModules = + # (Path) Root directory on which should the recursive mapping be applied + dir: + # (Path -> AttrSet a) Function that transforms node paths to their custom attrsets + fn: + mapFilterAttrs + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); + + /* + Custom nixpkgs constructor. Its purpose is to import provided nixpkgs + while setting the target platform and all over the needed overlays. + + *Type*: `mkNixpkgs :: AttrSet -> String -> [(AttrSet -> AttrSet -> AttrSet)] -> Attrset` + + Example: + ```nix title="Example" linenums="1" + mkNixpkgs inputs.nixpkgs "x86_64-linux" [] + => { ... } + + mkNixpkgs inputs.nixpkgs "aarch64-linux" [ (final: prev: { + customPkgs = inputs.customPkgs { pkgs = final; }; + }) ] + => { ... } + ``` + */ + mkNixpkgs = + # (AttrSet) Nixpkgs attrset + pkgs: + # (String) System string identifier (eg: "x86_64-linux", "aarch64-linux", "aarch64-darwin") + system: + # ([AttrSet -> AttrSet -> AttrSet]) Extra overlays that should be applied to the created pkgs + extraOverlays: + import pkgs { + inherit system; + config.allowUnfree = true; + hostPlatform = system; + overlays = let + pkgsOverlay = _final: _prev: { + practicalFlakes = inputs.self.packages.${system}; + }; + in + [pkgsOverlay] + ++ (attrValues inputs.self.overlays) + ++ extraOverlays; + }; + } diff --git a/templates/home/lib/options.nix b/templates/home/lib/options.nix new file mode 100644 index 0000000..29484ac --- /dev/null +++ b/templates/home/lib/options.nix @@ -0,0 +1,19 @@ +# --- lib/options.nix +{lib, ...}: +with lib; +with lib.types; +with builtins; rec { + # Example function + # ----------------- + # /* + # Creates an enableOption (ie `mkEnableOption`), however, already + # preenabled. + # + # *Type*: `String -> Option` + # */ + # mkAlreadyEnabledOption = description: + # (mkEnableOption description) + # // { + # default = true; + # }; +} diff --git a/templates/home/lib/strings.nix b/templates/home/lib/strings.nix new file mode 100644 index 0000000..c04ecea --- /dev/null +++ b/templates/home/lib/strings.nix @@ -0,0 +1,22 @@ +# --- lib/strings.nix +{lib, ...}: +with lib; +with builtins; rec { + # Example function + # ----------------- + # /* + # Given an absolute path to a file, return the dirname of that file. + + # *Type*: `dirnameFromPath :: Path -> Path` + + # Example: + # ```nix title="Example" linenums="1" + # dirnameFromPath "/etc/myDir/file.nix" + # => "/etc/myDir" + # ``` + # */ + # dirnameFromPath = + # # (Path) Absolute path to a given file + # dir: + # trivial.pipe dir [toString (strings.splitString "/") lists.last]; +} diff --git a/templates/home/lib/types.nix b/templates/home/lib/types.nix new file mode 100644 index 0000000..2340423 --- /dev/null +++ b/templates/home/lib/types.nix @@ -0,0 +1,11 @@ +# --- lib/types.nix +{lib, ...}: +with lib; +with builtins; +with types; { + # Example email type + # ------------------ + # email = + # addCheck str + # (str: match "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}" str != null); +} diff --git a/templates/home/parts/.gitignore b/templates/home/parts/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/home/parts/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/home/parts/homes/default.nix b/templates/home/parts/homes/default.nix new file mode 100644 index 0000000..ae7e59f --- /dev/null +++ b/templates/home/parts/homes/default.nix @@ -0,0 +1,43 @@ +# --- parts/homes/default.nix +# { +# lib, +# inputs, +# projectPath, +# withSystem, +# self, +# config, +# ... +# }: let +# mkHome = args: home: { +# extraSpecialArgs ? {}, +# extraModules ? [], +# }: +# inputs.home-manager.lib.homeManagerConfiguration { +# inherit (args) pkgs; +# extraSpecialArgs = +# { +# inherit (args) system; +# inherit inputs home projectPath self; +# } +# // extraSpecialArgs; +# modules = +# [ +# ./${home} +# ] +# ++ extraModules; +# }; +# in { +# options.flake.homeConfigurations = lib.mkOption { +# type = with lib.types; lazyAttrsOf unspecified; +# default = {}; +# }; +# config = { +# flake.homeConfigurations = { +# "myHost@myUser" = withSystem "x86_64-linux" (args: mkHome args "myHost@myUser" {}); +# }; +# flake.checks."x86_64-linux" = { +# "home-myHost@myUser" = config.flake.homeConfigurations."myHost@myUser".config.home.path; +# }; +# }; +# } +{} diff --git a/templates/home/parts/hosts/.gitignore b/templates/home/parts/hosts/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/home/parts/hosts/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/home/parts/hosts/default.nix b/templates/home/parts/hosts/default.nix new file mode 100644 index 0000000..5e57c26 --- /dev/null +++ b/templates/home/parts/hosts/default.nix @@ -0,0 +1,36 @@ +# --- parts/hosts/default.nix +# { +# lib, +# inputs, +# projectPath, +# withSystem, +# ... +# }: let +# mkHost = args: hostName: { +# extraSpecialArgs ? {}, +# extraModules ? [], +# }: +# lib.nixosSystem { +# inherit (args) system pkgs; +# specialArgs = +# { +# inherit (args) system; +# inherit inputs lib hostName projectPath; +# } +# // extraSpecialArgs; +# modules = +# [ +# { +# nixpkgs.pkgs = lib.mkDefault args.pkgs; +# networking.hostName = hostName; +# } +# ./${hostName} +# ] +# ++ extraModules; +# }; +# in { +# flake.nixosConfigurations = { +# # exampleHost = withSystem "x86_64-linux" (args: mkHost args "exampleHost" {}); +# }; +# } +{} diff --git a/templates/home/parts/modules/.gitignore b/templates/home/parts/modules/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/home/parts/modules/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/home/parts/modules/default.nix b/templates/home/parts/modules/default.nix new file mode 100644 index 0000000..16728d9 --- /dev/null +++ b/templates/home/parts/modules/default.nix @@ -0,0 +1,7 @@ +# --- parts/modules/default.nix +_: { + imports = [ + ./nixos + ./home-manager + ]; +} diff --git a/templates/home/parts/modules/home-manager/default.nix b/templates/home/parts/modules/home-manager/default.nix new file mode 100644 index 0000000..f330ee8 --- /dev/null +++ b/templates/home/parts/modules/home-manager/default.nix @@ -0,0 +1,11 @@ +# --- parts/modules/home-manager/default.nix +{lib, ...}: { + options.flake.homeModules = lib.mkOption { + type = with lib.types; lazyAttrsOf unspecified; + default = {}; + }; + + config.flake.homeModules = { + example-module = import ./example-module.nix; + }; +} diff --git a/templates/home/parts/modules/home-manager/example-module.nix b/templates/home/parts/modules/home-manager/example-module.nix new file mode 100644 index 0000000..63c6644 --- /dev/null +++ b/templates/home/parts/modules/home-manager/example-module.nix @@ -0,0 +1,40 @@ +# --- parts/modules/home-manager/example-module.nix +{ + config, + lib, + pkgs, + self, + ... +}: +with builtins; +with lib; let + practicalFlakes = self.lib; + inherit (practicalFlakes.modules) mkOverrideAtModuleLevel; + + cfg = config.practicalFlakes.hm.example-module; + _ = mkOverrideAtModuleLevel; +in { + options.practicalFlakes.hm.example-module = with types; + with practicalFlakes.types; { + enable = mkEnableOption (mdDoc '' + Enable the NixOS example module that enables neovim and installs git + ''); + }; + + config = mkIf cfg.enable (mkMerge [ + # |----------------------------------------------------------------------| # + { + home.packages = with pkgs; [git]; + + # By using mkOverrideAtModuleLevel we can set a sensible override + # priority that is higher than the /modules priority, however, + # it can still be easily changed by the end user in their host/home + # configurations. + programs.neovim.enable = _ true; + home.sessionVariables.MY_AWESOME_VAR = _ "Hello, everything working!"; + } + # |----------------------------------------------------------------------| # + ]); + + meta.maintainers = with practicalFlakes.maintainers; []; +} diff --git a/templates/home/parts/modules/nixos/default.nix b/templates/home/parts/modules/nixos/default.nix new file mode 100644 index 0000000..c8b8977 --- /dev/null +++ b/templates/home/parts/modules/nixos/default.nix @@ -0,0 +1,6 @@ +# --- parts/modules/nixos/default.nix +_: { + flake.nixosModules = { + example-module = import ./example-module.nix; + }; +} diff --git a/templates/home/parts/modules/nixos/example-module.nix b/templates/home/parts/modules/nixos/example-module.nix new file mode 100644 index 0000000..aa1ef4d --- /dev/null +++ b/templates/home/parts/modules/nixos/example-module.nix @@ -0,0 +1,37 @@ +# --- parts/modules/nixos/example-module.nix +{ + config, + lib, + pkgs, + ... +}: +with builtins; +with lib; let + inherit (practicalFlakes.modules) mkOverrideAtModuleLevel; + + cfg = config.practicalFlakes.example-module; + _ = mkOverrideAtModuleLevel; +in { + options.practicalFlakes.example-module = with types; + with practicalFlakes.types; { + enable = mkEnableOption (mdDoc '' + Enable the NixOS example module that enables neovim and installs git + ''); + }; + + config = mkIf cfg.enable (mkMerge [ + # |----------------------------------------------------------------------| # + { + environment.systemPackages = with pkgs; [git]; + + # By using mkOverrideAtModuleLevel we can set a sensible override + # priority that is higher than the /modules priority, however, + # it can still be easily changed by the end user in their host/home + # configurations. + programs.neovim.enable = _ true; + } + # |----------------------------------------------------------------------| # + ]); + + meta.maintainers = with practicalFlakes.maintainers; []; +} diff --git a/templates/home/parts/pkgs/.gitignore b/templates/home/parts/pkgs/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/home/parts/pkgs/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/home/parts/pkgs/default.nix b/templates/home/parts/pkgs/default.nix new file mode 100644 index 0000000..339d701 --- /dev/null +++ b/templates/home/parts/pkgs/default.nix @@ -0,0 +1,12 @@ +# --- parts/pkgs/default.nix +{lib, ...}: { + perSystem = { + pkgs, + system, + ... + }: { + packages = { + example-package = pkgs.callPackage ./example-package.nix {inherit lib system;}; + }; + }; +} diff --git a/templates/home/parts/pkgs/example-package.nix b/templates/home/parts/pkgs/example-package.nix new file mode 100644 index 0000000..1ac527c --- /dev/null +++ b/templates/home/parts/pkgs/example-package.nix @@ -0,0 +1,24 @@ +# --- parts/pkgs/example-package.nix +{ + lib, + system, + stdenv, + ... +}: +stdenv.mkDerivation rec { + name = "example-pkg"; + version = "v0.1.0"; + + src = ./.; + installPhase = '' + mkdir -p $out + echo "Hello, this is an example package" > $out/example.txt + ''; + + meta = with lib; { + description = "Example package"; + license = licenses.mit; + platforms = [system]; + maintainers = []; + }; +} diff --git a/templates/home/parts/shells/.gitignore b/templates/home/parts/shells/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/home/parts/shells/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/home/parts/shells/default.nix b/templates/home/parts/shells/default.nix new file mode 100644 index 0000000..f9398da --- /dev/null +++ b/templates/home/parts/shells/default.nix @@ -0,0 +1,18 @@ +# --- parts/shells/default.nix +{ + inputs, + projectPath, + ... +}: { + imports = with inputs; [devenv.flakeModule]; + perSystem = { + config, + pkgs, + ... + }: { + devenv.shells.dev = import ./dev.nix { + inherit pkgs projectPath; + treefmt = config.treefmt.build.wrapper; + }; + }; +} diff --git a/templates/home/parts/shells/dev.nix b/templates/home/parts/shells/dev.nix new file mode 100644 index 0000000..0cadc0b --- /dev/null +++ b/templates/home/parts/shells/dev.nix @@ -0,0 +1,55 @@ +# --- parts/shells/dev.nix +{ + pkgs, + treefmt, + ... +}: { + packages = with pkgs; [ + # -- greeting -- + cowsay + fortune + lolcat + # -- nix -- + nil # LSP + alejandra # formatting + statix # static code analysis + deadnix # find dead nix code + nix-output-monitor # readable derivation outputs + # -- misc -- + markdownlint-cli # markdown linting + nodePackages.prettier + treefmt + ]; + + languages.nix.enable = true; + difftastic.enable = true; + devcontainer.enable = true; # if anyone needs it + devenv.flakesIntegration = true; + + pre-commit = { + hooks = { + treefmt.enable = true; + # Everything below is stuff that is missing from treefmt + nil.enable = true; + markdownlint.enable = true; + actionlint.enable = true; + }; + settings = { + treefmt.package = treefmt; + }; + }; + + scripts = { + "rename-project".exec = '' + find $1 \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s/practicalFlakes/$2/g" + ''; + }; + + enterShell = '' + echo "" + echo "~~ Welcome to the practicalFlakes devshell! ~~ + + [Fortune of the Day] $(fortune)" | cowsay -W 120 -T "U " | lolcat -F 0.3 -p 10 -t + echo "" + ''; +} diff --git a/templates/home/parts/treefmt/.gitignore b/templates/home/parts/treefmt/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/home/parts/treefmt/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/home/parts/treefmt/default.nix b/templates/home/parts/treefmt/default.nix new file mode 100644 index 0000000..d3cf392 --- /dev/null +++ b/templates/home/parts/treefmt/default.nix @@ -0,0 +1,12 @@ +# --- parts/treefmt/default.nix +{ + projectPath, + inputs, + ... +}: { + imports = with inputs; [treefmt-nix.flakeModule]; + + perSystem = {pkgs, ...}: { + treefmt = import ./treefmt.nix {inherit pkgs projectPath;}; + }; +} diff --git a/templates/home/parts/treefmt/treefmt.nix b/templates/home/parts/treefmt/treefmt.nix new file mode 100644 index 0000000..d0450ca --- /dev/null +++ b/templates/home/parts/treefmt/treefmt.nix @@ -0,0 +1,18 @@ +# --- parts/treefmt/treefmt.nix +{ + pkgs, + projectPath, + ... +}: { + package = pkgs.treefmt; + flakeCheck = true; + flakeFormatter = true; + projectRootFile = projectPath + "/flake.nix"; + + programs = { + alejandra.enable = true; + deadnix.enable = true; + statix.enable = true; + prettier.enable = true; + }; +} diff --git a/templates/isolated-minimal/.envrc b/templates/isolated-minimal/.envrc new file mode 100644 index 0000000..4bf7b2f --- /dev/null +++ b/templates/isolated-minimal/.envrc @@ -0,0 +1,8 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +fi + +if ! use flake .#dev --impure +then + echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 +fi diff --git a/templates/isolated-minimal/.github/workflows/check-on-merge.yml b/templates/isolated-minimal/.github/workflows/check-on-merge.yml new file mode 100644 index 0000000..01a9ff4 --- /dev/null +++ b/templates/isolated-minimal/.github/workflows/check-on-merge.yml @@ -0,0 +1,20 @@ +name: "Run various checks on merge (pull) request" + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + check: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + + - name: Complete nix setup + uses: DeterminateSystems/nix-installer-action@main + + - name: pre-commit checks + run: nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + + - name: Check the flake + run: nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" diff --git a/templates/isolated-minimal/.gitignore b/templates/isolated-minimal/.gitignore new file mode 100644 index 0000000..2d8df24 --- /dev/null +++ b/templates/isolated-minimal/.gitignore @@ -0,0 +1,28 @@ +# OS generated files # +###################### +.DS_Store +.DS_Store? +ehthumbs.db +Icon? +Thumbs.db + +# IDEA Ignores # +################ +*.iml +*.ipr +*.iws +.idea/ +out/ +local.properties + +# Project related # +################ +result/ +build/ + +result +.direnv/ +.devenv/ + +.devcontainer.json +.pre-commit-config.yaml diff --git a/templates/isolated-minimal/.gitlab-ci.yml b/templates/isolated-minimal/.gitlab-ci.yml new file mode 100644 index 0000000..4a0f4c8 --- /dev/null +++ b/templates/isolated-minimal/.gitlab-ci.yml @@ -0,0 +1,14 @@ +image: nixos/nix:latest + +variables: + NIX_CONF_DIR: "/etc/nix" + +before_script: + - echo 'experimental-features = nix-command flakes' > $NIX_CONF_DIR/nix.conf + +check: + script: + - nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + - nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' diff --git a/templates/isolated-minimal/CHANGELOG.md b/templates/isolated-minimal/CHANGELOG.md new file mode 100644 index 0000000..825c32f --- /dev/null +++ b/templates/isolated-minimal/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/templates/isolated-minimal/README.md b/templates/isolated-minimal/README.md new file mode 100644 index 0000000..2c45761 --- /dev/null +++ b/templates/isolated-minimal/README.md @@ -0,0 +1,16 @@ +# practicalFlakes + +## Description + +This flake has been generated from the +[tsandrini/practical-flakes-template](https://github.com/tsandrini/practical-flakes-template/) +project. The next steps for your development are + +1. **development environment** + - either use [direnv](https://github.com/direnv/direnv) and `direnv allow` + - or explicitly enter the shell via `nix develop .#dev --impure` +2. **rename the project** + - while not many, there are some places in the code that have a `practicalFlakes` + identifier, you can use the `rename-project` (available in the dev environment) + script to change these + - `rename-project . myCoolNewProject` diff --git a/templates/isolated-minimal/flake.lock b/templates/isolated-minimal/flake.lock new file mode 100644 index 0000000..1f4da98 --- /dev/null +++ b/templates/isolated-minimal/flake.lock @@ -0,0 +1,395 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1700140236, + "narHash": "sha256-OpukFO0rRG2hJzD+pCQq+nSWuT9dBL6DSvADQaUlmFg=", + "owner": "cachix", + "repo": "devenv", + "rev": "525d60c44de848a6b2dd468f6efddff078eb2af2", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "mk-shell-bin": { + "locked": { + "lastModified": 1677004959, + "narHash": "sha256-/uEkr1UkJrh11vD02aqufCxtbF5YnhRTIKlx5kyvf+I=", + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "rev": "ff5d8bd4d68a347be5042e2f16caee391cd75887", + "type": "github" + }, + "original": { + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nix2container": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1700389764, + "narHash": "sha256-hMsZ741ri9c4ZQpB6mgLY8KErk0yXVAOUjfNkP1nbbw=", + "owner": "nlewo", + "repo": "nix2container", + "rev": "4400b77e14f3095ee3215a9a5e0f9143bc0e8f2d", + "type": "github" + }, + "original": { + "owner": "nlewo", + "repo": "nix2container", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1700390070, + "narHash": "sha256-de9KYi8rSJpqvBfNwscWdalIJXPo8NjdIZcEJum1mH0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e4ad989506ec7d71f7302cc3067abd82730a4beb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1695644571, + "narHash": "sha256-asS9dCCdlt1lPq0DLwkVBbVoEKuEuz+Zi3DG7pR/RxA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "6500b4580c2a1f3d0f980d32d285739d8e156d92", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1688056373, + "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "mk-shell-bin": "mk-shell-bin", + "nix2container": "nix2container", + "nixpkgs": "nixpkgs_2", + "systems": "systems_3", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1699786194, + "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/templates/isolated-minimal/flake.nix b/templates/isolated-minimal/flake.nix new file mode 100644 index 0000000..16c4059 --- /dev/null +++ b/templates/isolated-minimal/flake.nix @@ -0,0 +1,95 @@ +{ + description = "practicalFlakes - TODO Add a description of your new project"; + + inputs = { + # Base dependencies + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + systems.url = "github:nix-systems/default"; + + # Development (devenv and treefmt dependencies) + treefmt-nix.url = "github:numtide/treefmt-nix"; + devenv.url = "github:cachix/devenv"; + mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin"; + nix2container = { + url = "github:nlewo/nix2container"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Project specific dependencies + }; + + # Here you can add additional binary cache substituers that you trust + nixConfig = { + extra-trusted-public-keys = [ + "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" + ]; + extra-substituters = [ + "https://devenv.cachix.org" + ]; + }; + + outputs = inputs @ {flake-parts, ...}: let + inherit (inputs) nixpkgs; + inherit (lib.practicalFlakes) mapModules mkNixpkgs flatten; + + # You should ideally use relative paths in each individual part from ./parts, + # however, if needed you can use the `projectPath` variable that is passed + # to every flakeModule to properly anchor your absolute paths. + projectPath = ./.; + + # We extend the base library with our own custom helpers as well + # as override any of the nixpkgs default functions that we'd like + # to override. This instance is then passed to every part in ./parts so that + # you can use it in your custom modules + lib = nixpkgs.lib.extend (self: _super: { + practicalFlakes = import ./nix/lib { + inherit inputs projectPath; + pkgs = nixpkgs; + lib = self; + }; + }); + specialArgs = {inherit lib projectPath;}; + in + flake-parts.lib.mkFlake {inherit inputs specialArgs;} { + # We recursively traverse all of the flakeModules in ./parts and import only + # the final modules, meaning that you can have an arbitrary nested structure + # that suffices your needs. For example + # + # - ./nix/parts + # - modules/ + # - nixos/ + # - myNixosModule1.nix + # - myNixosModule2.nix + # - default.nix + # - home-manager/ + # - myHomeModule1.nix + # - myHomeModule2.nix + # - default.nix + # - sharedModules.nix + # - pkgs/ + # - myPackage1.nix + # - myPackage2.nix + # - default.nix + # - mySimpleModule.nix + imports = flatten (mapModules ./nix/parts (x: x)); + + # We use the default `systems` defined by the `nix-systems` flake, if you + # need any additional systems, simply add them in the following manner + # + # `systems = (import inputs.systems) ++ [ "armv7l-linux" ];` + systems = import inputs.systems; + flake.lib = lib.practicalFlakes; + + # Finally, we bootstrap the `pkgs` argument to use our custom nixpkgs + # instance bootstrapped with overlays, loaded system and other defaults. + # For more info refer to `lib/modules.nix:mkNixpkgs` + perSystem = { + system, + pkgs, + ... + }: { + _module.args.pkgs = mkNixpkgs nixpkgs system []; + }; + }; +} diff --git a/templates/isolated-minimal/nix/.gitignore b/templates/isolated-minimal/nix/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated-minimal/nix/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated-minimal/nix/lib/.gitignore b/templates/isolated-minimal/nix/lib/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated-minimal/nix/lib/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated-minimal/nix/lib/_bootstrap-lib.nix b/templates/isolated-minimal/nix/lib/_bootstrap-lib.nix new file mode 100644 index 0000000..d29ae56 --- /dev/null +++ b/templates/isolated-minimal/nix/lib/_bootstrap-lib.nix @@ -0,0 +1,23 @@ +# --- nix/lib/_bootstrap-lib.nix +{lib, ...}: +with lib; +with builtins; rec { + # This file should provide the bare minimum to bootstrap the lib, namely the + # mapModules' function to enable properly loading the library files and its + # functions + + mapFilterAttrs' = pred: f: attrs: filterAttrs pred (mapAttrs' f attrs); + + mapModules' = dir: fn: + mapFilterAttrs' + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules' path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); +} diff --git a/templates/isolated-minimal/nix/lib/attrsets.nix b/templates/isolated-minimal/nix/lib/attrsets.nix new file mode 100644 index 0000000..5c90f84 --- /dev/null +++ b/templates/isolated-minimal/nix/lib/attrsets.nix @@ -0,0 +1,66 @@ +# --- nix/lib/attrsets.nix +{lib, ...}: +with lib; +with builtins; rec { + /* + Apply a map to every attribute of an attrset and then filter the resulting + attrset based on a given predicate function. + + *Type*: `mapFilterAttrs :: (AttrSet b -> Bool) -> (AttrSet a -> AttrSet b) -> AttrSet a -> AttrSet b` + */ + mapFilterAttrs = + # (AttrSet b -> Bool) Predicate used for filtering + pred: + # (AttrSet a -> AttrSet b) Function used for transforming the given AttrSets + f: + # (AttrSet a) Initial attrset + attrs: + filterAttrs pred (mapAttrs' f attrs); + + /* + Recursively merges a list of attrsets. + + *Type*: `mergeAttrs :: [AttrSet] -> AttrSet` + + Example: + ```nix title="Example" linenums="1" + mergeAttrs [ + { keyA = 1; keyB = 3; } + { keyB = 10; keyC = "hey"; nestedKey = { A = null; }; } + { nestedKey = { A = 3; B = 4; }; } + ] + => { keyA = 1; keyB = 10; keyC = "hey"; nestedKey = { A = 3; B = 4; };} + ``` + */ + mergeAttrs = + # ([AttrSet]) The list of attrsets + attrs: + foldl' (acc: elem: acc // elem) {} attrs; + + /* + Recursively flattens a nested attrset into a list of just its values. + + *Type*: `flatten :: AttrSet a -> [a]` + + Example: + ```nix title="Example" linenums="1" + flatten { + keyA = 10; + keyB = "str20"; + keyC = { + keyD = false; + keyE = { + a = 10; + b = "20"; + c = false; + }; + }; + } + => [ 10 "str20" false 10 "20" false ] + ``` + */ + flatten = + # (AttrSet a) Initial nested attrset + attrs: + collect (x: !isAttrs x) attrs; +} diff --git a/templates/isolated-minimal/nix/lib/default.nix b/templates/isolated-minimal/nix/lib/default.nix new file mode 100644 index 0000000..e70df52 --- /dev/null +++ b/templates/isolated-minimal/nix/lib/default.nix @@ -0,0 +1,21 @@ +# --- nix/lib/default.nix +{ + pkgs, + lib, + inputs, + projectPath ? ./.., + ... +}: let + inherit (bootstrap) mapModules'; + + bootstrap = import ./_bootstrap-lib.nix {inherit lib;}; + + practicalFlakes = lib.makeExtensible (self: + with self; + mapModules' ./. (file: + import file { + inherit pkgs lib self inputs projectPath; + })); +in + practicalFlakes.extend + (_self: super: lib.foldr (a: b: a // b) {} (lib.attrValues super)) diff --git a/templates/isolated-minimal/nix/lib/modules.nix b/templates/isolated-minimal/nix/lib/modules.nix new file mode 100644 index 0000000..d899355 --- /dev/null +++ b/templates/isolated-minimal/nix/lib/modules.nix @@ -0,0 +1,91 @@ +# --- nix/lib/modules.nix +{ + lib, + self, + inputs, + ... +}: let + inherit (self.attrsets) mapFilterAttrs; +in + with lib; + with builtins; rec { + /* + Recursively read a directory and apply a provided function to every `.nix` + file. Returns an attrset that reflects the filenames and directory + structure of the root. + + Notes: + + 1. Files and directories starting with the `_` or `.git` prefix will be completely + ignored. + + 2. If a directory with a `myDir/default.nix` file will be encountered, + the function will be applied to the `myDir/default.nix` file + instead of recursively loading `myDir` and applying it to every file. + + *Type*: `mapModules :: Path -> (Path -> AttrSet a) -> { name :: String; value :: AttrSet a; }` + + Example: + ```nix title="Example" linenums="1" + mapModules ./modules import + => { hardware = { moduleA = { ... }; }; system = { moduleB = { ... }; }; } + + mapModules ./hosts (host: mkHostCustomFunction myArg host) + => { hostA = { ... }; hostB = { ... }; } + ``` + */ + mapModules = + # (Path) Root directory on which should the recursive mapping be applied + dir: + # (Path -> AttrSet a) Function that transforms node paths to their custom attrsets + fn: + mapFilterAttrs + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); + + /* + Custom nixpkgs constructor. Its purpose is to import provided nixpkgs + while setting the target platform and all over the needed overlays. + + *Type*: `mkNixpkgs :: AttrSet -> String -> [(AttrSet -> AttrSet -> AttrSet)] -> Attrset` + + Example: + ```nix title="Example" linenums="1" + mkNixpkgs inputs.nixpkgs "x86_64-linux" [] + => { ... } + + mkNixpkgs inputs.nixpkgs "aarch64-linux" [ (final: prev: { + customPkgs = inputs.customPkgs { pkgs = final; }; + }) ] + => { ... } + ``` + */ + mkNixpkgs = + # (AttrSet) Nixpkgs attrset + pkgs: + # (String) System string identifier (eg: "x86_64-linux", "aarch64-linux", "aarch64-darwin") + system: + # ([AttrSet -> AttrSet -> AttrSet]) Extra overlays that should be applied to the created pkgs + extraOverlays: + import pkgs { + inherit system; + config.allowUnfree = true; + hostPlatform = system; + overlays = let + pkgsOverlay = _final: _prev: { + practicalFlakes = inputs.self.packages.${system}; + }; + in + [pkgsOverlay] + ++ (attrValues inputs.self.overlays) + ++ extraOverlays; + }; + } diff --git a/templates/isolated-minimal/nix/parts/.gitignore b/templates/isolated-minimal/nix/parts/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated-minimal/nix/parts/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated-minimal/nix/parts/shells/.gitignore b/templates/isolated-minimal/nix/parts/shells/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated-minimal/nix/parts/shells/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated-minimal/nix/parts/shells/default.nix b/templates/isolated-minimal/nix/parts/shells/default.nix new file mode 100644 index 0000000..690fb97 --- /dev/null +++ b/templates/isolated-minimal/nix/parts/shells/default.nix @@ -0,0 +1,18 @@ +# --- nix/parts/shells/default.nix +{ + inputs, + projectPath, + ... +}: { + imports = with inputs; [devenv.flakeModule]; + perSystem = { + config, + pkgs, + ... + }: { + devenv.shells.dev = import ./dev.nix { + inherit pkgs projectPath; + treefmt = config.treefmt.build.wrapper; + }; + }; +} diff --git a/templates/isolated-minimal/nix/parts/shells/dev.nix b/templates/isolated-minimal/nix/parts/shells/dev.nix new file mode 100644 index 0000000..ca1761e --- /dev/null +++ b/templates/isolated-minimal/nix/parts/shells/dev.nix @@ -0,0 +1,55 @@ +# --- nix/parts/shells/dev.nix +{ + pkgs, + treefmt, + ... +}: { + packages = with pkgs; [ + # -- greeting -- + cowsay + fortune + lolcat + # -- nix -- + nil # LSP + alejandra # formatting + statix # static code analysis + deadnix # find dead nix code + nix-output-monitor # readable derivation outputs + # -- misc -- + markdownlint-cli # markdown linting + nodePackages.prettier + treefmt + ]; + + languages.nix.enable = true; + difftastic.enable = true; + devcontainer.enable = true; # if anyone needs it + devenv.flakesIntegration = true; + + pre-commit = { + hooks = { + treefmt.enable = true; + # Everything below is stuff that is missing from treefmt + nil.enable = true; + markdownlint.enable = true; + actionlint.enable = true; + }; + settings = { + treefmt.package = treefmt; + }; + }; + + scripts = { + "rename-project".exec = '' + find $1 \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s/practicalFlakes/$2/g" + ''; + }; + + enterShell = '' + echo "" + echo "~~ Welcome to the practicalFlakes devshell! ~~ + + [Fortune of the Day] $(fortune)" | cowsay -W 120 -T "U " | lolcat -F 0.3 -p 10 -t + echo "" + ''; +} diff --git a/templates/isolated-minimal/nix/parts/treefmt/.gitignore b/templates/isolated-minimal/nix/parts/treefmt/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated-minimal/nix/parts/treefmt/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated-minimal/nix/parts/treefmt/default.nix b/templates/isolated-minimal/nix/parts/treefmt/default.nix new file mode 100644 index 0000000..c32de90 --- /dev/null +++ b/templates/isolated-minimal/nix/parts/treefmt/default.nix @@ -0,0 +1,12 @@ +# --- nix/parts/treefmt/default.nix +{ + projectPath, + inputs, + ... +}: { + imports = with inputs; [treefmt-nix.flakeModule]; + + perSystem = {pkgs, ...}: { + treefmt = import ./treefmt.nix {inherit pkgs projectPath;}; + }; +} diff --git a/templates/isolated-minimal/nix/parts/treefmt/treefmt.nix b/templates/isolated-minimal/nix/parts/treefmt/treefmt.nix new file mode 100644 index 0000000..b04aa42 --- /dev/null +++ b/templates/isolated-minimal/nix/parts/treefmt/treefmt.nix @@ -0,0 +1,18 @@ +# --- nix/parts/treefmt/treefmt.nix +{ + pkgs, + projectPath, + ... +}: { + package = pkgs.treefmt; + flakeCheck = true; + flakeFormatter = true; + projectRootFile = projectPath + "/flake.nix"; + + programs = { + alejandra.enable = true; + deadnix.enable = true; + statix.enable = true; + prettier.enable = true; + }; +} diff --git a/templates/isolated/.envrc b/templates/isolated/.envrc new file mode 100644 index 0000000..4bf7b2f --- /dev/null +++ b/templates/isolated/.envrc @@ -0,0 +1,8 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +fi + +if ! use flake .#dev --impure +then + echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 +fi diff --git a/templates/isolated/.github/workflows/check-on-merge.yml b/templates/isolated/.github/workflows/check-on-merge.yml new file mode 100644 index 0000000..01a9ff4 --- /dev/null +++ b/templates/isolated/.github/workflows/check-on-merge.yml @@ -0,0 +1,20 @@ +name: "Run various checks on merge (pull) request" + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + check: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + + - name: Complete nix setup + uses: DeterminateSystems/nix-installer-action@main + + - name: pre-commit checks + run: nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + + - name: Check the flake + run: nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" diff --git a/templates/isolated/.gitignore b/templates/isolated/.gitignore new file mode 100644 index 0000000..2d8df24 --- /dev/null +++ b/templates/isolated/.gitignore @@ -0,0 +1,28 @@ +# OS generated files # +###################### +.DS_Store +.DS_Store? +ehthumbs.db +Icon? +Thumbs.db + +# IDEA Ignores # +################ +*.iml +*.ipr +*.iws +.idea/ +out/ +local.properties + +# Project related # +################ +result/ +build/ + +result +.direnv/ +.devenv/ + +.devcontainer.json +.pre-commit-config.yaml diff --git a/templates/isolated/.gitlab-ci.yml b/templates/isolated/.gitlab-ci.yml new file mode 100644 index 0000000..4a0f4c8 --- /dev/null +++ b/templates/isolated/.gitlab-ci.yml @@ -0,0 +1,14 @@ +image: nixos/nix:latest + +variables: + NIX_CONF_DIR: "/etc/nix" + +before_script: + - echo 'experimental-features = nix-command flakes' > $NIX_CONF_DIR/nix.conf + +check: + script: + - nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + - nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' diff --git a/templates/isolated/CHANGELOG.md b/templates/isolated/CHANGELOG.md new file mode 100644 index 0000000..825c32f --- /dev/null +++ b/templates/isolated/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/templates/isolated/README.md b/templates/isolated/README.md new file mode 100644 index 0000000..2c45761 --- /dev/null +++ b/templates/isolated/README.md @@ -0,0 +1,16 @@ +# practicalFlakes + +## Description + +This flake has been generated from the +[tsandrini/practical-flakes-template](https://github.com/tsandrini/practical-flakes-template/) +project. The next steps for your development are + +1. **development environment** + - either use [direnv](https://github.com/direnv/direnv) and `direnv allow` + - or explicitly enter the shell via `nix develop .#dev --impure` +2. **rename the project** + - while not many, there are some places in the code that have a `practicalFlakes` + identifier, you can use the `rename-project` (available in the dev environment) + script to change these + - `rename-project . myCoolNewProject` diff --git a/templates/isolated/flake.lock b/templates/isolated/flake.lock new file mode 100644 index 0000000..1f4da98 --- /dev/null +++ b/templates/isolated/flake.lock @@ -0,0 +1,395 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1700140236, + "narHash": "sha256-OpukFO0rRG2hJzD+pCQq+nSWuT9dBL6DSvADQaUlmFg=", + "owner": "cachix", + "repo": "devenv", + "rev": "525d60c44de848a6b2dd468f6efddff078eb2af2", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "mk-shell-bin": { + "locked": { + "lastModified": 1677004959, + "narHash": "sha256-/uEkr1UkJrh11vD02aqufCxtbF5YnhRTIKlx5kyvf+I=", + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "rev": "ff5d8bd4d68a347be5042e2f16caee391cd75887", + "type": "github" + }, + "original": { + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nix2container": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1700389764, + "narHash": "sha256-hMsZ741ri9c4ZQpB6mgLY8KErk0yXVAOUjfNkP1nbbw=", + "owner": "nlewo", + "repo": "nix2container", + "rev": "4400b77e14f3095ee3215a9a5e0f9143bc0e8f2d", + "type": "github" + }, + "original": { + "owner": "nlewo", + "repo": "nix2container", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1700390070, + "narHash": "sha256-de9KYi8rSJpqvBfNwscWdalIJXPo8NjdIZcEJum1mH0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e4ad989506ec7d71f7302cc3067abd82730a4beb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1695644571, + "narHash": "sha256-asS9dCCdlt1lPq0DLwkVBbVoEKuEuz+Zi3DG7pR/RxA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "6500b4580c2a1f3d0f980d32d285739d8e156d92", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1688056373, + "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "mk-shell-bin": "mk-shell-bin", + "nix2container": "nix2container", + "nixpkgs": "nixpkgs_2", + "systems": "systems_3", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1699786194, + "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/templates/isolated/flake.nix b/templates/isolated/flake.nix new file mode 100644 index 0000000..16c4059 --- /dev/null +++ b/templates/isolated/flake.nix @@ -0,0 +1,95 @@ +{ + description = "practicalFlakes - TODO Add a description of your new project"; + + inputs = { + # Base dependencies + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + systems.url = "github:nix-systems/default"; + + # Development (devenv and treefmt dependencies) + treefmt-nix.url = "github:numtide/treefmt-nix"; + devenv.url = "github:cachix/devenv"; + mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin"; + nix2container = { + url = "github:nlewo/nix2container"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Project specific dependencies + }; + + # Here you can add additional binary cache substituers that you trust + nixConfig = { + extra-trusted-public-keys = [ + "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" + ]; + extra-substituters = [ + "https://devenv.cachix.org" + ]; + }; + + outputs = inputs @ {flake-parts, ...}: let + inherit (inputs) nixpkgs; + inherit (lib.practicalFlakes) mapModules mkNixpkgs flatten; + + # You should ideally use relative paths in each individual part from ./parts, + # however, if needed you can use the `projectPath` variable that is passed + # to every flakeModule to properly anchor your absolute paths. + projectPath = ./.; + + # We extend the base library with our own custom helpers as well + # as override any of the nixpkgs default functions that we'd like + # to override. This instance is then passed to every part in ./parts so that + # you can use it in your custom modules + lib = nixpkgs.lib.extend (self: _super: { + practicalFlakes = import ./nix/lib { + inherit inputs projectPath; + pkgs = nixpkgs; + lib = self; + }; + }); + specialArgs = {inherit lib projectPath;}; + in + flake-parts.lib.mkFlake {inherit inputs specialArgs;} { + # We recursively traverse all of the flakeModules in ./parts and import only + # the final modules, meaning that you can have an arbitrary nested structure + # that suffices your needs. For example + # + # - ./nix/parts + # - modules/ + # - nixos/ + # - myNixosModule1.nix + # - myNixosModule2.nix + # - default.nix + # - home-manager/ + # - myHomeModule1.nix + # - myHomeModule2.nix + # - default.nix + # - sharedModules.nix + # - pkgs/ + # - myPackage1.nix + # - myPackage2.nix + # - default.nix + # - mySimpleModule.nix + imports = flatten (mapModules ./nix/parts (x: x)); + + # We use the default `systems` defined by the `nix-systems` flake, if you + # need any additional systems, simply add them in the following manner + # + # `systems = (import inputs.systems) ++ [ "armv7l-linux" ];` + systems = import inputs.systems; + flake.lib = lib.practicalFlakes; + + # Finally, we bootstrap the `pkgs` argument to use our custom nixpkgs + # instance bootstrapped with overlays, loaded system and other defaults. + # For more info refer to `lib/modules.nix:mkNixpkgs` + perSystem = { + system, + pkgs, + ... + }: { + _module.args.pkgs = mkNixpkgs nixpkgs system []; + }; + }; +} diff --git a/templates/isolated/nix/.gitignore b/templates/isolated/nix/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/lib/.gitignore b/templates/isolated/nix/lib/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/lib/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/lib/_bootstrap-lib.nix b/templates/isolated/nix/lib/_bootstrap-lib.nix new file mode 100644 index 0000000..d29ae56 --- /dev/null +++ b/templates/isolated/nix/lib/_bootstrap-lib.nix @@ -0,0 +1,23 @@ +# --- nix/lib/_bootstrap-lib.nix +{lib, ...}: +with lib; +with builtins; rec { + # This file should provide the bare minimum to bootstrap the lib, namely the + # mapModules' function to enable properly loading the library files and its + # functions + + mapFilterAttrs' = pred: f: attrs: filterAttrs pred (mapAttrs' f attrs); + + mapModules' = dir: fn: + mapFilterAttrs' + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules' path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); +} diff --git a/templates/isolated/nix/lib/asserts.nix b/templates/isolated/nix/lib/asserts.nix new file mode 100644 index 0000000..c4ae16a --- /dev/null +++ b/templates/isolated/nix/lib/asserts.nix @@ -0,0 +1,15 @@ +# --- nix/lib/asserts.nix +{lib, ...}: +with lib; +with builtins; { + # Example assertion + # ----------------- + # assertModulesLoaded = + # cfg: { + # assertion = hasAttr "practicalFlakes" cfg; + # message = '' + # Simple example assertion that checkc if the practicalFlakes namespace + # is present + # ''; + # }; +} diff --git a/templates/isolated/nix/lib/attrsets.nix b/templates/isolated/nix/lib/attrsets.nix new file mode 100644 index 0000000..5c90f84 --- /dev/null +++ b/templates/isolated/nix/lib/attrsets.nix @@ -0,0 +1,66 @@ +# --- nix/lib/attrsets.nix +{lib, ...}: +with lib; +with builtins; rec { + /* + Apply a map to every attribute of an attrset and then filter the resulting + attrset based on a given predicate function. + + *Type*: `mapFilterAttrs :: (AttrSet b -> Bool) -> (AttrSet a -> AttrSet b) -> AttrSet a -> AttrSet b` + */ + mapFilterAttrs = + # (AttrSet b -> Bool) Predicate used for filtering + pred: + # (AttrSet a -> AttrSet b) Function used for transforming the given AttrSets + f: + # (AttrSet a) Initial attrset + attrs: + filterAttrs pred (mapAttrs' f attrs); + + /* + Recursively merges a list of attrsets. + + *Type*: `mergeAttrs :: [AttrSet] -> AttrSet` + + Example: + ```nix title="Example" linenums="1" + mergeAttrs [ + { keyA = 1; keyB = 3; } + { keyB = 10; keyC = "hey"; nestedKey = { A = null; }; } + { nestedKey = { A = 3; B = 4; }; } + ] + => { keyA = 1; keyB = 10; keyC = "hey"; nestedKey = { A = 3; B = 4; };} + ``` + */ + mergeAttrs = + # ([AttrSet]) The list of attrsets + attrs: + foldl' (acc: elem: acc // elem) {} attrs; + + /* + Recursively flattens a nested attrset into a list of just its values. + + *Type*: `flatten :: AttrSet a -> [a]` + + Example: + ```nix title="Example" linenums="1" + flatten { + keyA = 10; + keyB = "str20"; + keyC = { + keyD = false; + keyE = { + a = 10; + b = "20"; + c = false; + }; + }; + } + => [ 10 "str20" false 10 "20" false ] + ``` + */ + flatten = + # (AttrSet a) Initial nested attrset + attrs: + collect (x: !isAttrs x) attrs; +} diff --git a/templates/isolated/nix/lib/default.nix b/templates/isolated/nix/lib/default.nix new file mode 100644 index 0000000..e70df52 --- /dev/null +++ b/templates/isolated/nix/lib/default.nix @@ -0,0 +1,21 @@ +# --- nix/lib/default.nix +{ + pkgs, + lib, + inputs, + projectPath ? ./.., + ... +}: let + inherit (bootstrap) mapModules'; + + bootstrap = import ./_bootstrap-lib.nix {inherit lib;}; + + practicalFlakes = lib.makeExtensible (self: + with self; + mapModules' ./. (file: + import file { + inherit pkgs lib self inputs projectPath; + })); +in + practicalFlakes.extend + (_self: super: lib.foldr (a: b: a // b) {} (lib.attrValues super)) diff --git a/templates/isolated/nix/lib/licenses.nix b/templates/isolated/nix/lib/licenses.nix new file mode 100644 index 0000000..1535024 --- /dev/null +++ b/templates/isolated/nix/lib/licenses.nix @@ -0,0 +1,2 @@ +# --- nix/lib/licenses.nix +_: {} diff --git a/templates/isolated/nix/lib/lists.nix b/templates/isolated/nix/lib/lists.nix new file mode 100644 index 0000000..daf065d --- /dev/null +++ b/templates/isolated/nix/lib/lists.nix @@ -0,0 +1,20 @@ +# --- nix/lib/lists.nix +{lib, ...}: +with lib; +with builtins; rec { + # Example function + # ----------------- + /* + # Map function with flipped arguments. + + # *Type*: `flipMap :: (a -> b) -> [a] -> [b]` + + # Example: + # ```nix title="Example" linenums="1" + # flipmap (x: x * x) [ 1 2 3 ] + # => [ 1 4 9 ] + # ``` + # + */ + # flipMap = flip map; +} diff --git a/templates/isolated/nix/lib/maintainers.nix b/templates/isolated/nix/lib/maintainers.nix new file mode 100644 index 0000000..8814a8f --- /dev/null +++ b/templates/isolated/nix/lib/maintainers.nix @@ -0,0 +1,11 @@ +# --- nix/lib/maintainers.nix +_: { + # Example maintainer + # ----------------- + # exampleMaintainer = { + # email = "exampleMaintainer@myMail.com"; + # name = "Example Maintainer"; + # github = "exampleMaintainer"; + # githubId = 12345678; + # }; +} diff --git a/templates/isolated/nix/lib/modules.nix b/templates/isolated/nix/lib/modules.nix new file mode 100644 index 0000000..d899355 --- /dev/null +++ b/templates/isolated/nix/lib/modules.nix @@ -0,0 +1,91 @@ +# --- nix/lib/modules.nix +{ + lib, + self, + inputs, + ... +}: let + inherit (self.attrsets) mapFilterAttrs; +in + with lib; + with builtins; rec { + /* + Recursively read a directory and apply a provided function to every `.nix` + file. Returns an attrset that reflects the filenames and directory + structure of the root. + + Notes: + + 1. Files and directories starting with the `_` or `.git` prefix will be completely + ignored. + + 2. If a directory with a `myDir/default.nix` file will be encountered, + the function will be applied to the `myDir/default.nix` file + instead of recursively loading `myDir` and applying it to every file. + + *Type*: `mapModules :: Path -> (Path -> AttrSet a) -> { name :: String; value :: AttrSet a; }` + + Example: + ```nix title="Example" linenums="1" + mapModules ./modules import + => { hardware = { moduleA = { ... }; }; system = { moduleB = { ... }; }; } + + mapModules ./hosts (host: mkHostCustomFunction myArg host) + => { hostA = { ... }; hostB = { ... }; } + ``` + */ + mapModules = + # (Path) Root directory on which should the recursive mapping be applied + dir: + # (Path -> AttrSet a) Function that transforms node paths to their custom attrsets + fn: + mapFilterAttrs + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); + + /* + Custom nixpkgs constructor. Its purpose is to import provided nixpkgs + while setting the target platform and all over the needed overlays. + + *Type*: `mkNixpkgs :: AttrSet -> String -> [(AttrSet -> AttrSet -> AttrSet)] -> Attrset` + + Example: + ```nix title="Example" linenums="1" + mkNixpkgs inputs.nixpkgs "x86_64-linux" [] + => { ... } + + mkNixpkgs inputs.nixpkgs "aarch64-linux" [ (final: prev: { + customPkgs = inputs.customPkgs { pkgs = final; }; + }) ] + => { ... } + ``` + */ + mkNixpkgs = + # (AttrSet) Nixpkgs attrset + pkgs: + # (String) System string identifier (eg: "x86_64-linux", "aarch64-linux", "aarch64-darwin") + system: + # ([AttrSet -> AttrSet -> AttrSet]) Extra overlays that should be applied to the created pkgs + extraOverlays: + import pkgs { + inherit system; + config.allowUnfree = true; + hostPlatform = system; + overlays = let + pkgsOverlay = _final: _prev: { + practicalFlakes = inputs.self.packages.${system}; + }; + in + [pkgsOverlay] + ++ (attrValues inputs.self.overlays) + ++ extraOverlays; + }; + } diff --git a/templates/isolated/nix/lib/options.nix b/templates/isolated/nix/lib/options.nix new file mode 100644 index 0000000..6529de3 --- /dev/null +++ b/templates/isolated/nix/lib/options.nix @@ -0,0 +1,19 @@ +# --- nix/lib/options.nix +{lib, ...}: +with lib; +with lib.types; +with builtins; rec { + # Example function + # ----------------- + # /* + # Creates an enableOption (ie `mkEnableOption`), however, already + # preenabled. + # + # *Type*: `String -> Option` + # */ + # mkAlreadyEnabledOption = description: + # (mkEnableOption description) + # // { + # default = true; + # }; +} diff --git a/templates/isolated/nix/lib/strings.nix b/templates/isolated/nix/lib/strings.nix new file mode 100644 index 0000000..b147465 --- /dev/null +++ b/templates/isolated/nix/lib/strings.nix @@ -0,0 +1,22 @@ +# --- nix/lib/strings.nix +{lib, ...}: +with lib; +with builtins; rec { + # Example function + # ----------------- + # /* + # Given an absolute path to a file, return the dirname of that file. + + # *Type*: `dirnameFromPath :: Path -> Path` + + # Example: + # ```nix title="Example" linenums="1" + # dirnameFromPath "/etc/myDir/file.nix" + # => "/etc/myDir" + # ``` + # */ + # dirnameFromPath = + # # (Path) Absolute path to a given file + # dir: + # trivial.pipe dir [toString (strings.splitString "/") lists.last]; +} diff --git a/templates/isolated/nix/lib/types.nix b/templates/isolated/nix/lib/types.nix new file mode 100644 index 0000000..e7c7c98 --- /dev/null +++ b/templates/isolated/nix/lib/types.nix @@ -0,0 +1,11 @@ +# --- nix/lib/types.nix +{lib, ...}: +with lib; +with builtins; +with types; { + # Example email type + # ------------------ + # email = + # addCheck str + # (str: match "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}" str != null); +} diff --git a/templates/isolated/nix/parts/.gitignore b/templates/isolated/nix/parts/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/parts/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/parts/hosts/.gitignore b/templates/isolated/nix/parts/hosts/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/parts/hosts/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/parts/hosts/default.nix b/templates/isolated/nix/parts/hosts/default.nix new file mode 100644 index 0000000..92241a7 --- /dev/null +++ b/templates/isolated/nix/parts/hosts/default.nix @@ -0,0 +1,36 @@ +# --- nix/parts/hosts/default.nix +# { +# lib, +# inputs, +# projectPath, +# withSystem, +# ... +# }: let +# mkHost = args: hostName: { +# extraSpecialArgs ? {}, +# extraModules ? [], +# }: +# lib.nixosSystem { +# inherit (args) system pkgs; +# specialArgs = +# { +# inherit (args) system; +# inherit inputs lib hostName projectPath; +# } +# // extraSpecialArgs; +# modules = +# [ +# { +# nixpkgs.pkgs = lib.mkDefault args.pkgs; +# networking.hostName = hostName; +# } +# ./${hostName} +# ] +# ++ extraModules; +# }; +# in { +# flake.nixosConfigurations = { +# # exampleHost = withSystem "x86_64-linux" (args: mkHost args "exampleHost" {}); +# }; +# } +{} diff --git a/templates/isolated/nix/parts/modules/.gitignore b/templates/isolated/nix/parts/modules/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/parts/modules/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/parts/modules/default.nix b/templates/isolated/nix/parts/modules/default.nix new file mode 100644 index 0000000..baded2d --- /dev/null +++ b/templates/isolated/nix/parts/modules/default.nix @@ -0,0 +1,6 @@ +# --- nix/parts/modules/default.nix +_: { + flake.nixosModules = { + example-module = import ./example-module.nix; + }; +} diff --git a/templates/isolated/nix/parts/modules/example-module.nix b/templates/isolated/nix/parts/modules/example-module.nix new file mode 100644 index 0000000..973837f --- /dev/null +++ b/templates/isolated/nix/parts/modules/example-module.nix @@ -0,0 +1,37 @@ +# --- nix/parts/modules/example-module.nix +{ + config, + lib, + pkgs, + ... +}: +with builtins; +with lib; let + inherit (practicalFlakes.modules) mkOverrideAtModuleLevel; + + cfg = config.practicalFlakes.example-module; + _ = mkOverrideAtModuleLevel; +in { + options.practicalFlakes.example-module = with types; + with practicalFlakes.types; { + enable = mkEnableOption (mdDoc '' + Enable the NixOS example module that enables neovim and installs git + ''); + }; + + config = mkIf cfg.enable (mkMerge [ + # |----------------------------------------------------------------------| # + { + environment.systemPackages = with pkgs; [git]; + + # By using mkOverrideAtModuleLevel we can set a sensible override + # priority that is higher than the /modules priority, however, + # it can still be easily changed by the end user in their host/home + # configurations. + programs.neovim.enable = _ true; + } + # |----------------------------------------------------------------------| # + ]); + + meta.maintainers = with practicalFlakes.maintainers; []; +} diff --git a/templates/isolated/nix/parts/pkgs/.gitignore b/templates/isolated/nix/parts/pkgs/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/parts/pkgs/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/parts/pkgs/default.nix b/templates/isolated/nix/parts/pkgs/default.nix new file mode 100644 index 0000000..e92306e --- /dev/null +++ b/templates/isolated/nix/parts/pkgs/default.nix @@ -0,0 +1,12 @@ +# --- nix/parts/pkgs/default.nix +{lib, ...}: { + perSystem = { + pkgs, + system, + ... + }: { + packages = { + example-package = pkgs.callPackage ./example-package.nix {inherit lib system;}; + }; + }; +} diff --git a/templates/isolated/nix/parts/pkgs/example-package.nix b/templates/isolated/nix/parts/pkgs/example-package.nix new file mode 100644 index 0000000..d5414e1 --- /dev/null +++ b/templates/isolated/nix/parts/pkgs/example-package.nix @@ -0,0 +1,24 @@ +# --- nix/parts/pkgs/example-package.nix +{ + lib, + system, + stdenv, + ... +}: +stdenv.mkDerivation rec { + name = "example-pkg"; + version = "v0.1.0"; + + src = ./.; + installPhase = '' + mkdir -p $out + echo "Hello, this is an example package" > $out/example.txt + ''; + + meta = with lib; { + description = "Example package"; + license = licenses.mit; + platforms = [system]; + maintainers = []; + }; +} diff --git a/templates/isolated/nix/parts/shells/.gitignore b/templates/isolated/nix/parts/shells/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/parts/shells/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/parts/shells/default.nix b/templates/isolated/nix/parts/shells/default.nix new file mode 100644 index 0000000..690fb97 --- /dev/null +++ b/templates/isolated/nix/parts/shells/default.nix @@ -0,0 +1,18 @@ +# --- nix/parts/shells/default.nix +{ + inputs, + projectPath, + ... +}: { + imports = with inputs; [devenv.flakeModule]; + perSystem = { + config, + pkgs, + ... + }: { + devenv.shells.dev = import ./dev.nix { + inherit pkgs projectPath; + treefmt = config.treefmt.build.wrapper; + }; + }; +} diff --git a/templates/isolated/nix/parts/shells/dev.nix b/templates/isolated/nix/parts/shells/dev.nix new file mode 100644 index 0000000..ca1761e --- /dev/null +++ b/templates/isolated/nix/parts/shells/dev.nix @@ -0,0 +1,55 @@ +# --- nix/parts/shells/dev.nix +{ + pkgs, + treefmt, + ... +}: { + packages = with pkgs; [ + # -- greeting -- + cowsay + fortune + lolcat + # -- nix -- + nil # LSP + alejandra # formatting + statix # static code analysis + deadnix # find dead nix code + nix-output-monitor # readable derivation outputs + # -- misc -- + markdownlint-cli # markdown linting + nodePackages.prettier + treefmt + ]; + + languages.nix.enable = true; + difftastic.enable = true; + devcontainer.enable = true; # if anyone needs it + devenv.flakesIntegration = true; + + pre-commit = { + hooks = { + treefmt.enable = true; + # Everything below is stuff that is missing from treefmt + nil.enable = true; + markdownlint.enable = true; + actionlint.enable = true; + }; + settings = { + treefmt.package = treefmt; + }; + }; + + scripts = { + "rename-project".exec = '' + find $1 \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s/practicalFlakes/$2/g" + ''; + }; + + enterShell = '' + echo "" + echo "~~ Welcome to the practicalFlakes devshell! ~~ + + [Fortune of the Day] $(fortune)" | cowsay -W 120 -T "U " | lolcat -F 0.3 -p 10 -t + echo "" + ''; +} diff --git a/templates/isolated/nix/parts/treefmt/.gitignore b/templates/isolated/nix/parts/treefmt/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/isolated/nix/parts/treefmt/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/isolated/nix/parts/treefmt/default.nix b/templates/isolated/nix/parts/treefmt/default.nix new file mode 100644 index 0000000..c32de90 --- /dev/null +++ b/templates/isolated/nix/parts/treefmt/default.nix @@ -0,0 +1,12 @@ +# --- nix/parts/treefmt/default.nix +{ + projectPath, + inputs, + ... +}: { + imports = with inputs; [treefmt-nix.flakeModule]; + + perSystem = {pkgs, ...}: { + treefmt = import ./treefmt.nix {inherit pkgs projectPath;}; + }; +} diff --git a/templates/isolated/nix/parts/treefmt/treefmt.nix b/templates/isolated/nix/parts/treefmt/treefmt.nix new file mode 100644 index 0000000..b04aa42 --- /dev/null +++ b/templates/isolated/nix/parts/treefmt/treefmt.nix @@ -0,0 +1,18 @@ +# --- nix/parts/treefmt/treefmt.nix +{ + pkgs, + projectPath, + ... +}: { + package = pkgs.treefmt; + flakeCheck = true; + flakeFormatter = true; + projectRootFile = projectPath + "/flake.nix"; + + programs = { + alejandra.enable = true; + deadnix.enable = true; + statix.enable = true; + prettier.enable = true; + }; +} diff --git a/templates/main/flake.nix b/templates/main/flake.nix index 25cd3d2..4421310 100644 --- a/templates/main/flake.nix +++ b/templates/main/flake.nix @@ -1,12 +1,13 @@ { - description = "practicalFlakes - TODO Add description :sunglasses:"; + description = "practicalFlakes - TODO Add a description of your new project"; inputs = { + # Base dependencies nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; systems.url = "github:nix-systems/default"; - # devenv.sh and treefmt + # Development (devenv and treefmt dependencies) treefmt-nix.url = "github:numtide/treefmt-nix"; devenv.url = "github:cachix/devenv"; mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin"; @@ -14,8 +15,11 @@ url = "github:nlewo/nix2container"; inputs.nixpkgs.follows = "nixpkgs"; }; + + # Project specific dependencies }; + # Here you can add additional binary cache substituers that you trust nixConfig = { extra-trusted-public-keys = [ "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" @@ -29,9 +33,15 @@ inherit (inputs) nixpkgs; inherit (lib.practicalFlakes) mapModules mkNixpkgs flatten; + # You should ideally use relative paths in each individual part from ./parts, + # however, if needed you can use the `projectPath` variable that is passed + # to every flakeModule to properly anchor your absolute paths. projectPath = ./.; - # TODO change name if needed + # We extend the base library with our own custom helpers as well + # as override any of the nixpkgs default functions that we'd like + # to override. This instance is then passed to every part in ./parts so that + # you can use it in your custom modules lib = nixpkgs.lib.extend (self: _super: { practicalFlakes = import ./lib { inherit inputs projectPath; @@ -42,11 +52,38 @@ specialArgs = {inherit lib projectPath;}; in flake-parts.lib.mkFlake {inherit inputs specialArgs;} { + # We recursively traverse all of the flakeModules in ./parts and import only + # the final modules, meaning that you can have an arbitrary nested structure + # that suffices your needs. For example + # + # - ./parts + # - modules/ + # - nixos/ + # - myNixosModule1.nix + # - myNixosModule2.nix + # - default.nix + # - home-manager/ + # - myHomeModule1.nix + # - myHomeModule2.nix + # - default.nix + # - sharedModules.nix + # - pkgs/ + # - myPackage1.nix + # - myPackage2.nix + # - default.nix + # - mySimpleModule.nix imports = flatten (mapModules ./parts (x: x)); + # We use the default `systems` defined by the `nix-systems` flake, if you + # need any additional systems, simply add them in the following manner + # + # `systems = (import inputs.systems) ++ [ "armv7l-linux" ];` systems = import inputs.systems; flake.lib = lib.practicalFlakes; + # Finally, we bootstrap the `pkgs` argument to use our custom nixpkgs + # instance bootstrapped with overlays, loaded system and other defaults. + # For more info refer to `lib/modules.nix:mkNixpkgs` perSystem = { system, pkgs, diff --git a/templates/main/lib/lists.nix b/templates/main/lib/lists.nix index 3d3e614..9425c0b 100644 --- a/templates/main/lib/lists.nix +++ b/templates/main/lib/lists.nix @@ -2,16 +2,19 @@ {lib, ...}: with lib; with builtins; rec { + # Example function + # ----------------- /* - Map function with flipped arguments. + # Map function with flipped arguments. - *Type*: `flipMap :: (a -> b) -> [a] -> [b]` + # *Type*: `flipMap :: (a -> b) -> [a] -> [b]` - Example: - ```nix title="Example" linenums="1" - flipmap (x: x * x) [ 1 2 3 ] - => [ 1 4 9 ] - ``` + # Example: + # ```nix title="Example" linenums="1" + # flipmap (x: x * x) [ 1 2 3 ] + # => [ 1 4 9 ] + # ``` + # */ - flipMap = flip map; + # flipMap = flip map; } diff --git a/templates/main/lib/maintainers.nix b/templates/main/lib/maintainers.nix index 2a1335f..8cf4e05 100644 --- a/templates/main/lib/maintainers.nix +++ b/templates/main/lib/maintainers.nix @@ -1,5 +1,7 @@ # --- lib/maintainers.nix _: { + # Example maintainer + # ----------------- # exampleMaintainer = { # email = "exampleMaintainer@myMail.com"; # name = "Example Maintainer"; diff --git a/templates/main/lib/options.nix b/templates/main/lib/options.nix index bb379c5..29484ac 100644 --- a/templates/main/lib/options.nix +++ b/templates/main/lib/options.nix @@ -3,15 +3,17 @@ with lib; with lib.types; with builtins; rec { - /* - Creates an enableOption (ie `mkEnableOption`), however, already - preenabled. - - *Type*: `String -> Option` - */ - mkAlreadyEnabledOption = description: - (mkEnableOption description) - // { - default = true; - }; + # Example function + # ----------------- + # /* + # Creates an enableOption (ie `mkEnableOption`), however, already + # preenabled. + # + # *Type*: `String -> Option` + # */ + # mkAlreadyEnabledOption = description: + # (mkEnableOption description) + # // { + # default = true; + # }; } diff --git a/templates/main/lib/strings.nix b/templates/main/lib/strings.nix index faf0f56..c04ecea 100644 --- a/templates/main/lib/strings.nix +++ b/templates/main/lib/strings.nix @@ -2,19 +2,21 @@ {lib, ...}: with lib; with builtins; rec { - /* - Given an absolute path to a file, return the dirname of that file. + # Example function + # ----------------- + # /* + # Given an absolute path to a file, return the dirname of that file. - *Type*: `dirnameFromPath :: Path -> Path` + # *Type*: `dirnameFromPath :: Path -> Path` - Example: - ```nix title="Example" linenums="1" - dirnameFromPath "/etc/myDir/file.nix" - => "/etc/myDir" - ``` - */ - dirnameFromPath = - # (Path) Absolute path to a given file - dir: - trivial.pipe dir [toString (strings.splitString "/") lists.last]; + # Example: + # ```nix title="Example" linenums="1" + # dirnameFromPath "/etc/myDir/file.nix" + # => "/etc/myDir" + # ``` + # */ + # dirnameFromPath = + # # (Path) Absolute path to a given file + # dir: + # trivial.pipe dir [toString (strings.splitString "/") lists.last]; } diff --git a/templates/main/parts/pkgs/example-package.nix b/templates/main/parts/pkgs/example-package.nix index 26c97e5..1ac527c 100644 --- a/templates/main/parts/pkgs/example-package.nix +++ b/templates/main/parts/pkgs/example-package.nix @@ -10,7 +10,6 @@ stdenv.mkDerivation rec { version = "v0.1.0"; src = ./.; - dontBuilt = true; installPhase = '' mkdir -p $out echo "Hello, this is an example package" > $out/example.txt diff --git a/templates/minimal/.envrc b/templates/minimal/.envrc new file mode 100644 index 0000000..4bf7b2f --- /dev/null +++ b/templates/minimal/.envrc @@ -0,0 +1,8 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +fi + +if ! use flake .#dev --impure +then + echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 +fi diff --git a/templates/minimal/.github/workflows/check-on-merge.yml b/templates/minimal/.github/workflows/check-on-merge.yml new file mode 100644 index 0000000..01a9ff4 --- /dev/null +++ b/templates/minimal/.github/workflows/check-on-merge.yml @@ -0,0 +1,20 @@ +name: "Run various checks on merge (pull) request" + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + check: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + + - name: Complete nix setup + uses: DeterminateSystems/nix-installer-action@main + + - name: pre-commit checks + run: nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + + - name: Check the flake + run: nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" diff --git a/templates/minimal/.gitignore b/templates/minimal/.gitignore new file mode 100644 index 0000000..2d8df24 --- /dev/null +++ b/templates/minimal/.gitignore @@ -0,0 +1,28 @@ +# OS generated files # +###################### +.DS_Store +.DS_Store? +ehthumbs.db +Icon? +Thumbs.db + +# IDEA Ignores # +################ +*.iml +*.ipr +*.iws +.idea/ +out/ +local.properties + +# Project related # +################ +result/ +build/ + +result +.direnv/ +.devenv/ + +.devcontainer.json +.pre-commit-config.yaml diff --git a/templates/minimal/.gitlab-ci.yml b/templates/minimal/.gitlab-ci.yml new file mode 100644 index 0000000..4a0f4c8 --- /dev/null +++ b/templates/minimal/.gitlab-ci.yml @@ -0,0 +1,14 @@ +image: nixos/nix:latest + +variables: + NIX_CONF_DIR: "/etc/nix" + +before_script: + - echo 'experimental-features = nix-command flakes' > $NIX_CONF_DIR/nix.conf + +check: + script: + - nix develop .#dev --impure --command bash -c "pre-commit run --all-files" + - nix develop .#dev --impure --command bash -c "nix flake check --impure --show-trace" + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' diff --git a/templates/minimal/CHANGELOG.md b/templates/minimal/CHANGELOG.md new file mode 100644 index 0000000..825c32f --- /dev/null +++ b/templates/minimal/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/templates/minimal/README.md b/templates/minimal/README.md new file mode 100644 index 0000000..2c45761 --- /dev/null +++ b/templates/minimal/README.md @@ -0,0 +1,16 @@ +# practicalFlakes + +## Description + +This flake has been generated from the +[tsandrini/practical-flakes-template](https://github.com/tsandrini/practical-flakes-template/) +project. The next steps for your development are + +1. **development environment** + - either use [direnv](https://github.com/direnv/direnv) and `direnv allow` + - or explicitly enter the shell via `nix develop .#dev --impure` +2. **rename the project** + - while not many, there are some places in the code that have a `practicalFlakes` + identifier, you can use the `rename-project` (available in the dev environment) + script to change these + - `rename-project . myCoolNewProject` diff --git a/templates/minimal/flake.lock b/templates/minimal/flake.lock new file mode 100644 index 0000000..1f4da98 --- /dev/null +++ b/templates/minimal/flake.lock @@ -0,0 +1,395 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1700140236, + "narHash": "sha256-OpukFO0rRG2hJzD+pCQq+nSWuT9dBL6DSvADQaUlmFg=", + "owner": "cachix", + "repo": "devenv", + "rev": "525d60c44de848a6b2dd468f6efddff078eb2af2", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "mk-shell-bin": { + "locked": { + "lastModified": 1677004959, + "narHash": "sha256-/uEkr1UkJrh11vD02aqufCxtbF5YnhRTIKlx5kyvf+I=", + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "rev": "ff5d8bd4d68a347be5042e2f16caee391cd75887", + "type": "github" + }, + "original": { + "owner": "rrbutani", + "repo": "nix-mk-shell-bin", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nix2container": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1700389764, + "narHash": "sha256-hMsZ741ri9c4ZQpB6mgLY8KErk0yXVAOUjfNkP1nbbw=", + "owner": "nlewo", + "repo": "nix2container", + "rev": "4400b77e14f3095ee3215a9a5e0f9143bc0e8f2d", + "type": "github" + }, + "original": { + "owner": "nlewo", + "repo": "nix2container", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1700390070, + "narHash": "sha256-de9KYi8rSJpqvBfNwscWdalIJXPo8NjdIZcEJum1mH0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e4ad989506ec7d71f7302cc3067abd82730a4beb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1695644571, + "narHash": "sha256-asS9dCCdlt1lPq0DLwkVBbVoEKuEuz+Zi3DG7pR/RxA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "6500b4580c2a1f3d0f980d32d285739d8e156d92", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1688056373, + "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "mk-shell-bin": "mk-shell-bin", + "nix2container": "nix2container", + "nixpkgs": "nixpkgs_2", + "systems": "systems_3", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1699786194, + "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/templates/minimal/flake.nix b/templates/minimal/flake.nix new file mode 100644 index 0000000..4421310 --- /dev/null +++ b/templates/minimal/flake.nix @@ -0,0 +1,95 @@ +{ + description = "practicalFlakes - TODO Add a description of your new project"; + + inputs = { + # Base dependencies + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + systems.url = "github:nix-systems/default"; + + # Development (devenv and treefmt dependencies) + treefmt-nix.url = "github:numtide/treefmt-nix"; + devenv.url = "github:cachix/devenv"; + mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin"; + nix2container = { + url = "github:nlewo/nix2container"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Project specific dependencies + }; + + # Here you can add additional binary cache substituers that you trust + nixConfig = { + extra-trusted-public-keys = [ + "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" + ]; + extra-substituters = [ + "https://devenv.cachix.org" + ]; + }; + + outputs = inputs @ {flake-parts, ...}: let + inherit (inputs) nixpkgs; + inherit (lib.practicalFlakes) mapModules mkNixpkgs flatten; + + # You should ideally use relative paths in each individual part from ./parts, + # however, if needed you can use the `projectPath` variable that is passed + # to every flakeModule to properly anchor your absolute paths. + projectPath = ./.; + + # We extend the base library with our own custom helpers as well + # as override any of the nixpkgs default functions that we'd like + # to override. This instance is then passed to every part in ./parts so that + # you can use it in your custom modules + lib = nixpkgs.lib.extend (self: _super: { + practicalFlakes = import ./lib { + inherit inputs projectPath; + pkgs = nixpkgs; + lib = self; + }; + }); + specialArgs = {inherit lib projectPath;}; + in + flake-parts.lib.mkFlake {inherit inputs specialArgs;} { + # We recursively traverse all of the flakeModules in ./parts and import only + # the final modules, meaning that you can have an arbitrary nested structure + # that suffices your needs. For example + # + # - ./parts + # - modules/ + # - nixos/ + # - myNixosModule1.nix + # - myNixosModule2.nix + # - default.nix + # - home-manager/ + # - myHomeModule1.nix + # - myHomeModule2.nix + # - default.nix + # - sharedModules.nix + # - pkgs/ + # - myPackage1.nix + # - myPackage2.nix + # - default.nix + # - mySimpleModule.nix + imports = flatten (mapModules ./parts (x: x)); + + # We use the default `systems` defined by the `nix-systems` flake, if you + # need any additional systems, simply add them in the following manner + # + # `systems = (import inputs.systems) ++ [ "armv7l-linux" ];` + systems = import inputs.systems; + flake.lib = lib.practicalFlakes; + + # Finally, we bootstrap the `pkgs` argument to use our custom nixpkgs + # instance bootstrapped with overlays, loaded system and other defaults. + # For more info refer to `lib/modules.nix:mkNixpkgs` + perSystem = { + system, + pkgs, + ... + }: { + _module.args.pkgs = mkNixpkgs nixpkgs system []; + }; + }; +} diff --git a/templates/minimal/lib/.gitignore b/templates/minimal/lib/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/minimal/lib/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/minimal/lib/_bootstrap-lib.nix b/templates/minimal/lib/_bootstrap-lib.nix new file mode 100644 index 0000000..93b1c61 --- /dev/null +++ b/templates/minimal/lib/_bootstrap-lib.nix @@ -0,0 +1,23 @@ +# --- lib/_bootstrap-lib.nix +{lib, ...}: +with lib; +with builtins; rec { + # This file should provide the bare minimum to bootstrap the lib, namely the + # mapModules' function to enable properly loading the library files and its + # functions + + mapFilterAttrs' = pred: f: attrs: filterAttrs pred (mapAttrs' f attrs); + + mapModules' = dir: fn: + mapFilterAttrs' + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules' path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); +} diff --git a/templates/minimal/lib/attrsets.nix b/templates/minimal/lib/attrsets.nix new file mode 100644 index 0000000..792a14b --- /dev/null +++ b/templates/minimal/lib/attrsets.nix @@ -0,0 +1,66 @@ +# --- lib/attrsets.nix +{lib, ...}: +with lib; +with builtins; rec { + /* + Apply a map to every attribute of an attrset and then filter the resulting + attrset based on a given predicate function. + + *Type*: `mapFilterAttrs :: (AttrSet b -> Bool) -> (AttrSet a -> AttrSet b) -> AttrSet a -> AttrSet b` + */ + mapFilterAttrs = + # (AttrSet b -> Bool) Predicate used for filtering + pred: + # (AttrSet a -> AttrSet b) Function used for transforming the given AttrSets + f: + # (AttrSet a) Initial attrset + attrs: + filterAttrs pred (mapAttrs' f attrs); + + /* + Recursively merges a list of attrsets. + + *Type*: `mergeAttrs :: [AttrSet] -> AttrSet` + + Example: + ```nix title="Example" linenums="1" + mergeAttrs [ + { keyA = 1; keyB = 3; } + { keyB = 10; keyC = "hey"; nestedKey = { A = null; }; } + { nestedKey = { A = 3; B = 4; }; } + ] + => { keyA = 1; keyB = 10; keyC = "hey"; nestedKey = { A = 3; B = 4; };} + ``` + */ + mergeAttrs = + # ([AttrSet]) The list of attrsets + attrs: + foldl' (acc: elem: acc // elem) {} attrs; + + /* + Recursively flattens a nested attrset into a list of just its values. + + *Type*: `flatten :: AttrSet a -> [a]` + + Example: + ```nix title="Example" linenums="1" + flatten { + keyA = 10; + keyB = "str20"; + keyC = { + keyD = false; + keyE = { + a = 10; + b = "20"; + c = false; + }; + }; + } + => [ 10 "str20" false 10 "20" false ] + ``` + */ + flatten = + # (AttrSet a) Initial nested attrset + attrs: + collect (x: !isAttrs x) attrs; +} diff --git a/templates/minimal/lib/default.nix b/templates/minimal/lib/default.nix new file mode 100644 index 0000000..0eace91 --- /dev/null +++ b/templates/minimal/lib/default.nix @@ -0,0 +1,21 @@ +# --- lib/default.nix +{ + pkgs, + lib, + inputs, + projectPath ? ./.., + ... +}: let + inherit (bootstrap) mapModules'; + + bootstrap = import ./_bootstrap-lib.nix {inherit lib;}; + + practicalFlakes = lib.makeExtensible (self: + with self; + mapModules' ./. (file: + import file { + inherit pkgs lib self inputs projectPath; + })); +in + practicalFlakes.extend + (_self: super: lib.foldr (a: b: a // b) {} (lib.attrValues super)) diff --git a/templates/minimal/lib/modules.nix b/templates/minimal/lib/modules.nix new file mode 100644 index 0000000..13ae4fe --- /dev/null +++ b/templates/minimal/lib/modules.nix @@ -0,0 +1,91 @@ +# --- lib/modules.nix +{ + lib, + self, + inputs, + ... +}: let + inherit (self.attrsets) mapFilterAttrs; +in + with lib; + with builtins; rec { + /* + Recursively read a directory and apply a provided function to every `.nix` + file. Returns an attrset that reflects the filenames and directory + structure of the root. + + Notes: + + 1. Files and directories starting with the `_` or `.git` prefix will be completely + ignored. + + 2. If a directory with a `myDir/default.nix` file will be encountered, + the function will be applied to the `myDir/default.nix` file + instead of recursively loading `myDir` and applying it to every file. + + *Type*: `mapModules :: Path -> (Path -> AttrSet a) -> { name :: String; value :: AttrSet a; }` + + Example: + ```nix title="Example" linenums="1" + mapModules ./modules import + => { hardware = { moduleA = { ... }; }; system = { moduleB = { ... }; }; } + + mapModules ./hosts (host: mkHostCustomFunction myArg host) + => { hostA = { ... }; hostB = { ... }; } + ``` + */ + mapModules = + # (Path) Root directory on which should the recursive mapping be applied + dir: + # (Path -> AttrSet a) Function that transforms node paths to their custom attrsets + fn: + mapFilterAttrs + (n: v: v != null && !(hasPrefix "_" n) && !(hasPrefix ".git" n)) (n: v: let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "directory" + then nameValuePair n (mapModules path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) (readDir dir); + + /* + Custom nixpkgs constructor. Its purpose is to import provided nixpkgs + while setting the target platform and all over the needed overlays. + + *Type*: `mkNixpkgs :: AttrSet -> String -> [(AttrSet -> AttrSet -> AttrSet)] -> Attrset` + + Example: + ```nix title="Example" linenums="1" + mkNixpkgs inputs.nixpkgs "x86_64-linux" [] + => { ... } + + mkNixpkgs inputs.nixpkgs "aarch64-linux" [ (final: prev: { + customPkgs = inputs.customPkgs { pkgs = final; }; + }) ] + => { ... } + ``` + */ + mkNixpkgs = + # (AttrSet) Nixpkgs attrset + pkgs: + # (String) System string identifier (eg: "x86_64-linux", "aarch64-linux", "aarch64-darwin") + system: + # ([AttrSet -> AttrSet -> AttrSet]) Extra overlays that should be applied to the created pkgs + extraOverlays: + import pkgs { + inherit system; + config.allowUnfree = true; + hostPlatform = system; + overlays = let + pkgsOverlay = _final: _prev: { + practicalFlakes = inputs.self.packages.${system}; + }; + in + [pkgsOverlay] + ++ (attrValues inputs.self.overlays) + ++ extraOverlays; + }; + } diff --git a/templates/minimal/parts/.gitignore b/templates/minimal/parts/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/minimal/parts/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/minimal/parts/shells/.gitignore b/templates/minimal/parts/shells/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/minimal/parts/shells/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/minimal/parts/shells/default.nix b/templates/minimal/parts/shells/default.nix new file mode 100644 index 0000000..f9398da --- /dev/null +++ b/templates/minimal/parts/shells/default.nix @@ -0,0 +1,18 @@ +# --- parts/shells/default.nix +{ + inputs, + projectPath, + ... +}: { + imports = with inputs; [devenv.flakeModule]; + perSystem = { + config, + pkgs, + ... + }: { + devenv.shells.dev = import ./dev.nix { + inherit pkgs projectPath; + treefmt = config.treefmt.build.wrapper; + }; + }; +} diff --git a/templates/minimal/parts/shells/dev.nix b/templates/minimal/parts/shells/dev.nix new file mode 100644 index 0000000..0cadc0b --- /dev/null +++ b/templates/minimal/parts/shells/dev.nix @@ -0,0 +1,55 @@ +# --- parts/shells/dev.nix +{ + pkgs, + treefmt, + ... +}: { + packages = with pkgs; [ + # -- greeting -- + cowsay + fortune + lolcat + # -- nix -- + nil # LSP + alejandra # formatting + statix # static code analysis + deadnix # find dead nix code + nix-output-monitor # readable derivation outputs + # -- misc -- + markdownlint-cli # markdown linting + nodePackages.prettier + treefmt + ]; + + languages.nix.enable = true; + difftastic.enable = true; + devcontainer.enable = true; # if anyone needs it + devenv.flakesIntegration = true; + + pre-commit = { + hooks = { + treefmt.enable = true; + # Everything below is stuff that is missing from treefmt + nil.enable = true; + markdownlint.enable = true; + actionlint.enable = true; + }; + settings = { + treefmt.package = treefmt; + }; + }; + + scripts = { + "rename-project".exec = '' + find $1 \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s/practicalFlakes/$2/g" + ''; + }; + + enterShell = '' + echo "" + echo "~~ Welcome to the practicalFlakes devshell! ~~ + + [Fortune of the Day] $(fortune)" | cowsay -W 120 -T "U " | lolcat -F 0.3 -p 10 -t + echo "" + ''; +} diff --git a/templates/minimal/parts/treefmt/.gitignore b/templates/minimal/parts/treefmt/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/templates/minimal/parts/treefmt/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/templates/minimal/parts/treefmt/default.nix b/templates/minimal/parts/treefmt/default.nix new file mode 100644 index 0000000..d3cf392 --- /dev/null +++ b/templates/minimal/parts/treefmt/default.nix @@ -0,0 +1,12 @@ +# --- parts/treefmt/default.nix +{ + projectPath, + inputs, + ... +}: { + imports = with inputs; [treefmt-nix.flakeModule]; + + perSystem = {pkgs, ...}: { + treefmt = import ./treefmt.nix {inherit pkgs projectPath;}; + }; +} diff --git a/templates/minimal/parts/treefmt/treefmt.nix b/templates/minimal/parts/treefmt/treefmt.nix new file mode 100644 index 0000000..d0450ca --- /dev/null +++ b/templates/minimal/parts/treefmt/treefmt.nix @@ -0,0 +1,18 @@ +# --- parts/treefmt/treefmt.nix +{ + pkgs, + projectPath, + ... +}: { + package = pkgs.treefmt; + flakeCheck = true; + flakeFormatter = true; + projectRootFile = projectPath + "/flake.nix"; + + programs = { + alejandra.enable = true; + deadnix.enable = true; + statix.enable = true; + prettier.enable = true; + }; +}