About
Helps static file/configuration creation with Nix and devshell.
There is a bunch of ways static file/configuration are hard, this will help you generate, validate and distribute JSON, YAML, TOML or TXT.
Generate
Your content will be defined in Nix Language, it means you can use variables, functions, imports, read files, etc.
The modular system helps layering configurations, hiding complexity and making it easier for OPS teams.
Validate
Your content modules could optionally be well defined and type checked in build proccess with this same tool.
Or you could use Nix as package manager and install any tool to validate your configuration (ie integrating it with existing JSON Schema).
Distribute
Nix integrates well with git and http, it could be also used to read JSON, YAML, TOML, zip and gz files.
In fact Nix isn't a configuration tool but a package manger, we are only using it as configuration tool because the language is simple and flexible.
You can recreate files of a repository directly to your local machine by running nix develop <flake-uri> --build
, example:
# copy all my dogfood to your current folder
nix develop github:cruel-intentions/devshell-files --build
With help of Nix and devshell you could install any development or deployment tool of its 80 000 packages.
Instructions
Installing Nix
curl -sSf -L https://install.determinate.systems/nix | sh -s -- install
Configuring new projects:
nix flake new -t github:cruel-intentions/devshell-files my-project
cd my-project
git init
git add *.nix flake.lock
Configuring existing projects:
nix flake new -t github:cruel-intentions/devshell-files ./
git add *.nix
git add flake.lock
Generating files:
nix develop --build
or entering in shell with all commands and alias
nix develop -c $SHELL
# to list commands and alias
# now run: menu
Examples
Creating JSON, TEXT, TOML or YAML files
# examples/hello.nix
#
# this is one nix file
{
files.json."/generated/hello.json".hello = "world";
files.toml."/generated/hello.toml".hello = "world";
files.yaml."/generated/hello.yaml".hello = "world";
files.hcl."/generated/hello.hcl".hello = "world";
files.text."/generated/hello.txt" = "world";
}
Your file can be complemented with another module
# examples/world.nix
# almost same as previous example
# but show some language feature
let
name = "hello"; # a variable
in
{
files = {
json."/generated/${name}.json".baz = ["foo" "bar" name];
toml."/generated/${name}.toml".baz = ["foo" "bar" name];
yaml = {
"/generated/${name}.yaml" = {
baz = [
"foo"
"bar"
name
];
};
};
};
}
Content generated by those examples are in generated
# ie ./generated/hello.yaml
baz:
- foo
- bar
- hello
hello: world
Dogfooding
This project is configured by module project.nix
# ./project.nix
{
# import other modules
imports = [
./examples/hello.nix
./examples/world.nix
./examples/readme.nix
./examples/gitignore.nix
./examples/license.nix
./examples/interpolation.nix
./examples/docs.nix
./examples/book.nix
./examples/services.nix
./examples/nim.nix
./examples/nushell.nix
./examples/watch.nix
];
# My shell name
devshell.name = "devshell-files";
# install development or deployment tools
packages = [
"convco"
# now we can use 'convco' command https://convco.github.io
# but could be:
# "awscli"
# "azure-cli"
# "cargo"
# "conda"
# "go"
# "nim"
# "nodejs"
# "nodejs-18_x"
# "nushell"
# "pipenv"
# "python39"
# "ruby"
# "rustc"
# "terraform"
# "yarn"
# look at https://search.nixos.org for more packages
];
# create alias
files.alias.feat = ''convco commit --feat $@'';
files.alias.fix = ''convco commit --fix $@'';
files.alias.docs = ''convco commit --docs $@'';
files.alias.alou = ''
#!/usr/bin/env python
print("Alo!") # is hello in portuguese
'';
# now we can use feat, fix, docs and alou commands
# create .envrc for direnv
files.direnv.enable = true;
# disabe file creation when entering in the shell
# call devshell-files instead
# files.on-call = true;
}
This README.md is also a module defined as above
# There is a lot things we could use to write static file
# Basic intro to nix language https://github.com/tazjin/nix-1p
# Some nix functions https://teu5us.github.io/nix-lib.html
{lib, ...}:
{
files.text."/README.md" = builtins.concatStringsSep "\n" [
"# Devshell Files Maker"
(builtins.readFile ./readme/toc.md)
(builtins.readFile ./readme/about.md)
(builtins.readFile ./readme/installation.md)
(builtins.import ./readme/examples.nix)
((builtins.import ./readme/modules.nix) lib)
(builtins.readFile ./readme/todo.md)
(builtins.readFile ./readme/issues.md)
(builtins.readFile ./readme/seeAlso.md)
];
}
Our .gitignore is defined like this
# ./examples/gitignore.nix
{
# create my .gitignore copying ignore patterns from
# github.com/github/gitignore
files.gitignore.enable = true;
files.gitignore.template."Global/Archives" = true;
files.gitignore.template."Global/Backup" = true;
files.gitignore.template."Global/Diff" = true;
files.gitignore.pattern."**/.data" = true;
files.gitignore.pattern."**/.direnv" = true;
files.gitignore.pattern."**/.envrc" = true;
files.gitignore.pattern."**/.gitignore" = true;
files.gitignore.pattern."**/flake.lock" = true;
}
And our LICENSE file is
# ./examples/license.nix
{
# LICENSE file creation
# using templates from https://github.com/spdx/license-list-data
files.license.enable = true;
files.license.spdx.name = "MIT";
files.license.spdx.vars.year = "2023";
files.license.spdx.vars."copyright holders" = "Cruel Intentions";
}
Writing new modules
Nix lang
Jump this part if aready know Nix Lang, if don't there is a small concise content of Nix Lang.
If one page is too much to you, the basic is:
:
defines a new function,arg: "Hello ${arg}"
- that's why we use
=
instaed of:
,{ attr-key = "value"; }
;
instead of,
and they aren't optional- array aren't separated by
,
ie.[ "some" "value" ]
JSON as NIX
name | JSON | NIX |
---|---|---|
null | null | null |
bool | true | true |
int | 123 | 123 |
float | 12.3 | 12.3 |
string | "string" | "string" |
array | ["some","array"] | ["some" "array"] |
object | {"some":"value"} | { some = "value"; } |
multiline-string | ''... multiline string ... '' | |
variables | let my-var = 1; other-var = 2; in my-var + other-var | |
function | my-arg: "Hello ${my-arg}!" | |
variable-function | let my-function = my-arg: "Hello ${my-arg}!"; in ... | |
calling-a-function | ... in my-function "World" |
Module
Modules can be defined in two formats:
As attrset, aka. object (JSON), dict (Python):
{ # <|
imports = []; # |
config = {}; # | module info
options = {}; # |
} # <|
All those attributes are optional
- imports: array with paths to other modules
- config: object with actual configurations
- options: object with our config type definition
As function:
Functions has following arguments:
config
with all evaluated configs values,pkgs
with all nixpkgs available.lib
library of useful functions.- And may receive others (we use
...
to ignore them)
{ config, pkgs, lib, ... }: # <| function args
{ # <|
imports = []; # |
config = {}; # | module info
options = {}; # |
} # <|
Imports
Points to other modules files to be imported in this module
{
imports = [
./gh-actions-options.nix
./gh-actions-impl.nix
];
}
Hint, split modules in two files:
- One mostly with options, where your definition goes
- Other with config, where your information goes
It has two advantages, let share options definitions across projects more easily.
And it hides complexity, hiding complexity is what abstraction is all about, we didn't share options definitions across projects to type less, but because we could reuse an abstraction that helps hiding complexity.
Config
Are values to our options
We can set value by ourself, or use lib functions to import json/toml/text files.
{ lib, ...}:
{
config.files.text."/HW.txt" = "Hello World!";
config.files.text."/EO.txt" = lib.concatStringsSep "" ["48" "65" "6c" "6c" "6f"];
config.files.text."/LR.txt" = (lib.importJSON ./hello.json).msg; # { "msg": "Hello World!" }
config.files.text."/LL.txt" = (lib.importTOML ./hello.toml).msg; # msg = Hello World!
config.files.text."/OD.txt" = lib.readFile ./hello.txt; # Hello World!
}
If file has no options.
, config.
can be ommited.
And this file produce the same result
{ lib, ...}:
{
files.text."/HW.txt" = "Hello World!";
files.text."/EO.txt" = lib.concatStringsSep "" ["48" "65" "6c" "6c" "6f"];
files.text."/LR.txt" = (lib.importJSON ./hello.json).msg; # { "msg": "Hello World!" }
files.text."/LL.txt" = (lib.importTOML ./hello.toml).msg; # msg = Hello World!
files.text."/OD.txt" = lib.readFile ./hello.txt; # Hello World!
}
Options
Options are schema definition for configs values.
Example, to create a github action file, it could be done like this:
{
config.files.yaml."/.github/workflows/ci-cd.yaml" = {
on = "push";
jobs.ci-cd.runs-on = "ubuntu-latest";
jobs.ci-cd.steps = [
{ uses = "actions/checkout@v2.4.0"; }
{ run = "npm i"; }
{ run = "npm run build"; }
{ run = "npm run test"; }
{ run = "aws s3 sync ./build s3://some-s3-bucket"; }
];
};
}
This only works because this project has another module with:
{lib, ...}:
{
options.files = submodule {
options.yaml.type = lib.types.attrsOf lib.types.anything;
};
}
But if we always set ci-cd.yaml like that, no complexity has been hidden, and requires copy and past it in every project.
Since most CI/CD are just: 'Pre Build', 'Build', 'Test', 'Deploy'
What most projects really need is something like:
# any module file (maybe project.nix)
{
# our build steps
config.gh-actions.setup = "npm i";
config.gh-actions.build = "npm run build";
config.gh-actions.test = "npm run test";
config.gh-actions.deploy = "aws s3 sync ./build s3://some-s3-bucket";
}
Adding this to project.nix, throws an error undefined config.gh-actions
, and command fails.
It doesn't knows these options.
To make aware of it, we had to add options
schema of that.
# gh-actions-options.nix
{ lib, ...}:
{
# a property 'gh-actions.setup'
options.gh-actions.setup = lib.mkOption {
default = "echo setup";
description = "Command to run before build";
example = "npm i";
type = lib.types.str;
};
# a property 'gh-actions.build'
options.gh-actions.build = lib.mkOption {
default = "echo build";
description = "Command to run as build step";
example = "npm run build";
type = lib.types.str;
};
# a property 'gh-actions.test'
options.gh-actions.test = lib.mkOption {
default = "echo test";
description = "Command to run as test step";
example = "npm test";
type = lib.types.str;
};
# a property 'gh-actions.deploy'
options.gh-actions.deploy = lib.mkOption {
default = "echo deploy";
description = "Command to run as deploy step";
example = "aws s3 sync ./build s3://my-bucket";
type = lib.types.lines;
};
}
Or using lib.types.fluent
# gh-actions-options.nix
{ lib, ...}:
lib.types.fluent {
options.gh-actions.options = {
# defines a property 'gh-actions.setup'
setup.default = "echo setup"; #default is string
setup.mdDoc = "Command to run before build";
setup.example = "npm i";
# defines a property 'gh-actions.build'
build.default = "echo build";
build.mdDoc = "Command to run as build step";
build.example = "npm run build";
# defines a property 'gh-actions.test'
test.default = "echo test";
test.mdDoc = "Command to run as test step";
test.example = "npm test";
# defines a property 'gh-actions.deploy'
deploy.default = "echo deploy";
deploy.mdDoc = "Command to run as deploy step";
deploy.example = "aws s3 sync ./build s3://my-bucket";
deploy.type = lib.types.lines;
};
}
Now, previous config can be used, but it does nothing, it doesn't create yaml.
It knowns what options can be accepted as config
, but not what to do with it.
The following code uses parameter config
that has all evaluated config
values.
# gh-actions.nix
{ config, lib, ... }:
{
imports = [ ./gh-actions-options.nix ];
# use other module that simplify file creation to create config file
files.yaml."/.github/workflows/ci-cd.yaml".jobs.ci-cd.steps = [
{ uses = "actions/checkout@v2.4.0"; }
{ run = config.gh-actions.setup; } #
{ run = config.gh-actions.build; } # Read step scripts from
{ run = config.gh-actions.test; } # config.gh-actions
{ run = config.gh-actions.deploy"; } #
];
files.yaml."/.github/workflows/ci-cd.yaml".on = "push";
files.yaml."/.github/workflows/ci-cd.yaml".jobs.ci-cd.runs-on = "ubuntu-latest";
}
Now it can be imported and set 'setup', 'build', 'test' and 'deploy' configs
# any other module file, maybe project.nix
{
imports = [ ./gh-actions.nix ];
gh-actions.setup = "echo 'paranaue'";
gh-actions.build = "echo 'paranaue parana'";
gh-actions.build = "echo 'paranaue'";
gh-actions.deploy = ''
echo "paranaue
parana"
'';
}
If something that is not a string is set, an error will raise, cheking it against the options schema.
There are other types that can be used (some of them):
- lib.types.bool
- lib.types.path
- lib.types.package
- lib.types.int
- lib.types.ints.unsigned
- lib.types.ints.positive
- lib.types.ints.port
- lib.types.ints.between
- lib.types.str
- lib.types.lines
- lib.types.enum
- lib.types.submodule
- lib.types.nullOr (typed nullable)
- lib.types.listOf (typed array)
- lib.types.attrsOf (typed hash map)
- lib.types.uniq (typed set)
And lib has some modules helpers functions like:
- lib.mkIf : to only set a property if some informaiton is true
- lib.optionals : to return an array or an empty array
- lib.optionalString: to return an string or an empty string
Sharing our module
Now to not just copy and past it everywhere, we could create a git repository, ie. gh-actions
Then we could let nix manage it for us adding it to flake.nix file like
{
description = "Dev Environment";
inputs.dsf.url = "github:cruel-intentions/devshell-files";
inputs.gha.url = "github:cruel-intentions/gh-actions";
# for private repository use git url
# inputs.gha.url = "git+ssh://git@github.com/cruel-intentions/gh-actions.git";
outputs = inputs: inputs.dsf.lib.mkShell [
"${inputs.gha}/gh-actions.nix"
./project.nix
];
}
Or manage version adding it directly to project.nix (or any other module file)
{
imports =
let gh-actions = builtins.fetchGit {
url = "git+ssh://git@github.com/cruel-intentions/gh-actions.git";
ref = "master";
rev = "46eead778911b5786d299ecf1a95c9ed4c130844";
};
in [
"${gh-actions}/gh-actions.nix"
];
}
Document our module
To document our modules is simple, we just need to use config.files.docs
as follow
# examples/docs.nix
{lib, pkgs, ...}:
{
files.docs."/gh-pages/src/modules/alias.md".modules = [ ../modules/alias.nix ../modules/alias-complete.nix ];
files.docs."/gh-pages/src/modules/cmds.md".modules = [ ../modules/cmds.nix ];
files.docs."/gh-pages/src/modules/files.md".modules = [ ../modules/files.nix ];
files.docs."/gh-pages/src/modules/git.md".modules = [ ../modules/git.nix ];
files.docs."/gh-pages/src/modules/on-call.md".modules = [ ../modules/startup.nix ];
files.docs."/gh-pages/src/modules/gitignore.md".modules = [ ../modules/gitignore.nix ];
files.docs."/gh-pages/src/modules/hcl.md".modules = [ ../modules/hcl.nix ];
files.docs."/gh-pages/src/modules/json.md".modules = [ ../modules/json.nix ];
files.docs."/gh-pages/src/modules/mdbook.md".modules = [ ../modules/mdbook.nix ];
files.docs."/gh-pages/src/modules/nim.md".modules = [ ../modules/nim.nix ];
files.docs."/gh-pages/src/modules/nushell.md".modules = [ ../modules/nushell.nix ];
files.docs."/gh-pages/src/modules/nush.md".modules = [ ../modules/nush.nix ../modules/nuon.nix ];
files.docs."/gh-pages/src/modules/rc.md".modules = [ ../modules/services/rc-devshell.nix ];
files.docs."/gh-pages/src/modules/services.md".modules = [ ../modules/services.nix ];
files.docs."/gh-pages/src/modules/spdx.md".modules = [ ../modules/spdx.nix ];
files.docs."/gh-pages/src/modules/text.md".modules = [ ../modules/text.nix ];
files.docs."/gh-pages/src/modules/toml.md".modules = [ ../modules/toml.nix ];
files.docs."/gh-pages/src/modules/watch.md".modules = [ ../modules/watch ];
files.docs."/gh-pages/src/modules/yaml.md".modules = [ ../modules/yaml.nix ];
}
We could also generate a mdbook with it
# examples/book.nix
{lib, ...}:
let
project = "devshell-files";
author = "cruel-intentions";
org-url = "https://github.com/${author}";
edit-path = "${org-url}/${project}/edit/master/examples/{path}";
in
{
files.mdbook.authors = ["Cruel Intentions <${org-url}>"];
files.mdbook.enable = true;
files.mdbook.gh-author = author;
files.mdbook.gh-project = project;
files.mdbook.language = "en";
files.mdbook.multilingual = false;
files.mdbook.summary = builtins.readFile ./summary.md;
files.mdbook.title = "Nix DevShell Files Maker";
files.mdbook.output.html.edit-url-template = edit-path;
files.mdbook.output.html.fold.enable = true;
files.mdbook.output.html.git-repository-url = "${org-url}/${project}/tree/master";
files.mdbook.output.html.no-section-label = true;
files.mdbook.output.html.site-url = "/${project}/";
files.gitignore.pattern.gh-pages = true;
files.text."/gh-pages/src/introduction.md" = builtins.readFile ./readme/about.md;
files.text."/gh-pages/src/installation.md" = builtins.readFile ./readme/installation.md;
files.text."/gh-pages/src/examples.md" = builtins.import ./readme/examples.nix;
files.text."/gh-pages/src/modules.md" = "## Writing new modules";
files.text."/gh-pages/src/nix-lang.md" = builtins.readFile ./readme/modules/nix-lang.md;
files.text."/gh-pages/src/json-nix.md" = builtins.import ./readme/modules/json-vs-nix.nix lib;
files.text."/gh-pages/src/module-spec.md" = builtins.readFile ./readme/modules/modules.md;
files.text."/gh-pages/src/share.md" = builtins.readFile ./readme/modules/share.md;
files.text."/gh-pages/src/document.md" = builtins.import ./readme/modules/document.nix;
files.text."/gh-pages/src/builtins.md" = builtins.readFile ./readme/modules/builtins.md;
files.text."/gh-pages/src/todo.md" = builtins.readFile ./readme/todo.md;
files.text."/gh-pages/src/issues.md" = builtins.readFile ./readme/issues.md;
files.text."/gh-pages/src/seeAlso.md" = builtins.readFile ./readme/seeAlso.md;
files.alias.publish-as-gh-pages-from-local = ''
# same as publish-as-gh-pages but works local
cd $PRJ_ROOT
ORIGIN=`git remote get-url origin`
cd gh-pages
mdbook build
cd book
git init .
git add .
git checkout -b gh-pages
git commit -m "docs(gh-pages): update gh-pages" .
git remote add origin $ORIGIN
git push -u origin gh-pages --force
'';
}
And publish this mdbook to github pages with book-as-gh-pages
alias.
Builtin Modules
Builtin Modules are modules defined with this same package.
They are already included when we use this package.
files.alias
, create bash script aliasfiles.cmds
, install packages from nix repositoryfiles.docs
, convert our modules file into markdown using nmdfiles.git
, configure git with file creationfiles.on-call
, connfigure file to created only when devshell-files command is called, not on shell startfiles.gitignore
, copy .gitignore from templatesfiles.hcl
, create HCL files with nix syntaxfiles.json
, create JSON files with nix syntaxfiles.mdbook
, convert your markdown files to HTML using mdbookfiles.nim
, similar tofiles.alias
, but compiles Nim codefiles.nus
, similar tofiles.alias
, but runs in Nushellfiles.nush
, similar tofiles.nus
, but for subcommandsfiles.services
, process supervisor for development services using s6files.rc
, WIP, process supervisor for development services using s6-rcfiles.spdx
, copy LICENSE from templatesfiles.text
, create free text files with nix syntaxfiles.toml
, create TOML files with nix syntaxfiles.watch
, create an alias and service to run command when file changes using inotify-toolsfiles.yaml
, create YAML files with nix syntax
Our documentation is generated by files.text
, files.docs
and files.mdbook
files
type
submodule
files.alias
type
attribute set of Concatenated string
example
{
files.alias = {
hello = "echo hello";
world = ''
#!/usr/bin/env python
print("world")
'';
};
}
default
{
files.alias = { };
}
files.complete
type
lazy attribute set of (submodule)
example
{
files.complete = {
svcCtl = {
bash = "complete -W \"$PRJ_SVCS\" svcCtl";
fish = "complete -c svcCtl -a \"$PRJ_SVCS\"";
};
};
}
default
{
files.complete = { };
}
files.complete.<name>.bash
type
null or string
default
{
files.complete.<name>.bash = null;
}
files.complete.<name>.fish
type
null or string
default
{
files.complete.<name>.fish = null;
}
files.cmds
https://search.nixos.org for more tools
type
attribute set of boolean
example
{
files.cmds = {
awscli = true;
azure-cli = true;
cargo = true;
conda = true;
convco = true;
go_1_17 = true;
nodejs-16_x = true;
pipenv = true;
python39 = true;
ruby_3_0 = true;
rustc = true;
terraform = true;
yarn = true;
};
}
default
{
files.cmds = { };
}
file
type
attribute set of (submodule)
default
{
file = { };
}
file.<name>.executable
type
null or boolean
default
{
file.<name>.executable = null;
}
file.<name>.git-add
type
null or boolean
default
{
file.<name>.git-add = null;
}
file.<name>.interpolate
type
boolean
default
{
file.<name>.interpolate = false;
}
file.<name>.on-enter
type
null or boolean
default
{
file.<name>.on-enter = true;
}
file.<name>.source
type
path
file.<name>.target
type
string
default
{
file.<name>.target = <name>;
}
file.<name>.text
type
null or strings concatenated with "\n"
default
{
file.<name>.text = null;
}
files.git.auto-add
type
boolean
example
{
files.git.auto-add = true;
}
default
{
files.git.auto-add = false;
}
files.gitignore.enable
type
boolean
example
{
files.gitignore.enable = true;
}
default
{
files.gitignore.enable = false;
}
files.gitignore.pattern
type
attribute set of boolean
example
{
files.gitignore.pattern = {
"ignore-this-file.txt" = true;
};
}
default
{
files.gitignore.pattern = { };
}
files.gitignore.template
type
attribute set of boolean
example
{
files.gitignore.template = {
Android = true;
"community/AWS/SAM" = true;
};
}
default
{
files.gitignore.template = { };
}
files.hcl
type
attribute set of (JSON value)
example
{
files.hcl = {
"/hellaos.hcl" = {
greeting = "hello World";
};
"/hellows.hcl" = {
greetings = [
[
"Hello World"
]
[
"Ola Mundo"
[
"Holla Que Tal"
]
]
];
};
};
}
default
{
files.hcl = { };
}
files.json
type
attribute set of (JSON value)
example
{
files.json = {
"/hellaos.json" = {
greeting = "hello World";
};
"/hellows.json" = {
greetings = [
[
"Hello World"
]
[
"Ola Mundo"
[
"Holla Que Tal"
]
]
];
};
};
}
default
{
files.json = { };
}
files.mdbook
by default it creates gh-pages/mdbook.toml and gh-pages/src/SUMMARY.md
It also create publish-as-gh-pages helper
Full configuration example
#examples/book.nix
{lib, ...}:
let
project = "devshell-files";
author = "cruel-intentions";
org-url = "https://github.com/${author}";
edit-path = "${org-url}/${project}/edit/master/examples/{path}";
in
{
files.mdbook.authors = ["Cruel Intentions <${org-url}>"];
files.mdbook.enable = true;
files.mdbook.gh-author = author;
files.mdbook.gh-project = project;
files.mdbook.language = "en";
files.mdbook.multilingual = false;
files.mdbook.summary = builtins.readFile ./summary.md;
files.mdbook.title = "Nix DevShell Files Maker";
files.mdbook.output.html.edit-url-template = edit-path;
files.mdbook.output.html.fold.enable = true;
files.mdbook.output.html.git-repository-url = "${org-url}/${project}/tree/master";
files.mdbook.output.html.no-section-label = true;
files.mdbook.output.html.site-url = "/${project}/";
files.gitignore.pattern.gh-pages = true;
files.text."/gh-pages/src/introduction.md" = builtins.readFile ./readme/about.md;
files.text."/gh-pages/src/installation.md" = builtins.readFile ./readme/installation.md;
files.text."/gh-pages/src/examples.md" = builtins.import ./readme/examples.nix;
files.text."/gh-pages/src/modules.md" = "## Writing new modules";
files.text."/gh-pages/src/nix-lang.md" = builtins.readFile ./readme/modules/nix-lang.md;
files.text."/gh-pages/src/json-nix.md" = builtins.import ./readme/modules/json-vs-nix.nix lib;
files.text."/gh-pages/src/module-spec.md" = builtins.readFile ./readme/modules/modules.md;
files.text."/gh-pages/src/share.md" = builtins.readFile ./readme/modules/share.md;
files.text."/gh-pages/src/document.md" = builtins.import ./readme/modules/document.nix;
files.text."/gh-pages/src/builtins.md" = builtins.readFile ./readme/modules/builtins.md;
files.text."/gh-pages/src/todo.md" = builtins.readFile ./readme/todo.md;
files.text."/gh-pages/src/issues.md" = builtins.readFile ./readme/issues.md;
files.text."/gh-pages/src/seeAlso.md" = builtins.readFile ./readme/seeAlso.md;
files.alias.publish-as-gh-pages-from-local = ''
# same as publish-as-gh-pages but works local
cd $PRJ_ROOT
ORIGIN=`git remote get-url origin`
cd gh-pages
mdbook build
cd book
git init .
git add .
git checkout -b gh-pages
git commit -m "docs(gh-pages): update gh-pages" .
git remote add origin $ORIGIN
git push -u origin gh-pages --force
'';
}
type
submodule
default
{
files.mdbook = { };
}
files.mdbook.enable
type
boolean
example
{
files.mdbook.enable = true;
}
default
{
files.mdbook.enable = false;
}
files.mdbook.authors
type
null or (non-empty (list of string))
example
{
files.mdbook.authors = [
"Cruel Intentions"
];
}
default
{
files.mdbook.authors = null;
}
files.mdbook.build
type
JSON value
example
{
files.mdbook.build = {
build-dir = "book";
};
}
default
{
files.mdbook.build = { };
}
files.mdbook.description
type
null or string
example
{
files.mdbook.description = "Modules Docummentation";
}
default
{
files.mdbook.description = null;
}
files.mdbook.gh-author
type
null or string
example
{
files.mdbook.gh-author = "cruel-intentions";
}
default
{
files.mdbook.gh-author = null;
}
files.mdbook.gh-project
type
null or string
example
{
files.mdbook.gh-project = "devshell-files";
}
default
{
files.mdbook.gh-project = null;
}
files.mdbook.language
type
null or non-empty string
example
{
files.mdbook.language = "en";
}
default
{
files.mdbook.language = null;
}
files.mdbook.multilingual
type
boolean
example
{
files.mdbook.multilingual = true;
}
default
{
files.mdbook.multilingual = false;
}
files.mdbook.output
type
JSON value
example
{
files.mdbook.output = {
html = {
fold = {
enable = true;
};
};
};
}
default
{
files.mdbook.output = { };
}
files.mdbook.preprocessor
type
JSON value
example
{
files.mdbook.preprocessor = {
mathjax = {
renderers = [
"html"
];
};
};
}
default
{
files.mdbook.preprocessor = { };
}
files.mdbook.root-dir
type
non-empty string
example
{
files.mdbook.root-dir = "/gh-pages";
}
default
{
files.mdbook.root-dir = "/gh-pages";
}
files.mdbook.rust
type
JSON value
example
{
files.mdbook.rust = {
edition = "2018";
};
}
default
{
files.mdbook.rust = { };
}
files.mdbook.summary
type
strings concatenated with "\n"
example
{
files.mdbook.summary = ''
# SUMMARY
- [Intro](./intro.md)
'';
}
default
{
files.mdbook.summary = ''
# SUMMARY
'';
}
files.mdbook.title
type
null or string
example
{
files.mdbook.title = "Devshell Files Modules";
}
default
{
files.mdbook.title = null;
}
files.mdbook.use-default-preprocessor
type
boolean
example
{
files.mdbook.use-default-preprocessor = false;
}
default
{
files.mdbook.use-default-preprocessor = true;
}
files.nim
It includes some helpers and libraries for laziness, so it better fit prototyping and simples commands/script.
Vars:
- PRJ_ROOT : devshell PRJ_ROOT env information
- ARGS : Arguments command arguments
- NO_ARGS : empty arguments
- PWD : DirPath "."
- All devshell like:
let PRJ_DATA_DIR = env "PRJ_DATA_DIR"
Procs:
- arg : get arg n, default="", ie.
1.arg
- env : get env name, default="", ie.
"PRJ_DATA_DIR".env
- cd : set current dir
- exec : execute {cmd}, args=NO_ARGS, dir=".".dirPath
- jPath: creates a path to access json ie:
"foo/0/^1/0..1/0..^1/bar".jPath
- / : concat paths, ie:
myPath / "blerg" / 0 / ^1 / 0 .. 1 / 0 .. ^1/baz
- get : get JsonNode in path of object,
myPath.get myObj
- [] : get JsonNode in pat of object,
myObj[myPath]
- set : set JsonNode in path of object,
myPath.set myObj, myVal
- []= : set JsonNode in path of object,
myObj[myPath] = myVal
- / : concat paths, ie:
- sep : string
- dir : string
- path: JsonPath
- obj : JsonNode
Imports:
- Import almost all std libraries
Flags:
-w:off -d:ssl --threads:on --mm:orc --hints:off --parallelBuild:4 --tlsEmulation:on
Todo:
- Add an option to configure flags
- Add an option for static linking
Examples:
# examples/nim.nix
{pkgs, ...}:{
# compile nim files and them to shell
files.nim.helloNim = ''echo ARGS'';
# keep an /tmp file with last result for n seconds
files.nim.memoise = builtins.readFile ./nim/memoise.nim;
# clustersSTS is a good candidate to be used with services
files.nim.clustersSTS = builtins.readFile ./nim/clustersSTS.nim;
# our poor man jq alternative
files.nim.jsonildo = builtins.readFile ./nim/jsonildo.nim;
files.nim.helloNimAgain.deps = [ pkgs.termbox pkgs.nimPackages.nimbox ];
files.nim.helloNimAgain.src = ''
import nimbox
proc main() =
var nb = newNimbox()
defer: nb.shutdown()
nb.print(0, 0, "Hello, world!")
nb.present()
sleep(1000)
when isMainModule:
main()
'';
}
```</para>
#### type
attribute set of ((submodule) or Concatenated string)
#### example
```nix
{
files.nim = {
dokr = ''
// create an docker compose alias
// cd $PRJ_ROOT/subdir; docker "compose" $@
exec "docker", "compose" + ARGS, PRJ_ROOT / "subdir"
'';
hello = ''
#compile-at-mkshell
# by default nim commands were compiled at first run to reduce
# shell activation time, add comment #compile-at-mkshell to
# compile at shell activation
echo "hello"
'';
manage = ''
// create an pipenv alias
// pipenv run python ./manage.py
exec "pipenv", args("run python ./manage.py") + ARGS
'';
};
}
default
{
files.nim = { };
}
files.nus
type
attribute set of (Concatenated string or list of Concatenated string)
example
{
files.nus = {
hello = [
"name"
"{hello: $name}"
];
world = ''
# it could use previous commands
hello world
'';
};
}
default
{
files.nus = { };
}
files.nuon.enable
type
boolean
example
{
files.nuon.enable = true;
}
default
{
files.nuon.enable = false;
}
files.nush
type
attribute set of attribute set of (Concatenated string or list of Concatenated string)
example
{
files.nush = {
hello = {
en = [
"arg"
"{hello: $arg}"
];
pt = [
"arg"
"{ola: $arg}"
];
};
world = {
hello = ''
# it could use previous commands
hello en "World"
hello pt "Mundo"
'';
};
};
}
default
{
files.nush = { };
}
files.rc
- {name}.bundle for bundles
- {name}.oneshot for one shot activations scripts
- {name}.longrun for deamons services
All services are disabled by default.
It will fail at activation time if:
- More then onde type (bundle, oneshot, longrun) were defined for the same {name}
- One dependency service ins't defined but required
type
attribute set of (submodule)
example
{
files.rc = {
hello = {
enable = true;
longrun = {
run = "mySquirelD";
};
};
hola = {
bundle = {
contents = [
"hello"
"world"
];
};
enable = true;
};
world = {
enable = true;
oneshot = {
setup = "echo Hello World > $PRJ_DATA_DIR/hello.txt";
};
};
};
}
default
{
files.rc = { };
}
files.rc.<name>.enable
type
boolean
example
{
files.rc.<name>.enable = true;
}
default
{
files.rc.<name>.enable = false;
}
files.rc.<name>.bundle
bundle
is a service aggregator
For example we can bundle every system (db, http, config) by microservice.
Like:
- Commerce Bundle:
- commerce database service
- commerce api service
- commerce cache service
- BPMN Bundle
- BPMN database service
- BPMN api service
- BPMN cache service
type
null or (submodule)
example
{
files.rc.<name>.bundle = {
contents = [
"myFancyServiceName"
];
essential = true;
};
}
default
{
files.rc.<name>.bundle = null;
}
files.rc.<name>.bundle.deps
type
non-empty (list of non-empty string)
example
{
files.rc.<name>.bundle.deps = [
"myDep"
"myOtherDep"
];
}
files.rc.<name>.bundle.essential
It means some commands may work to stop these services
type
boolean
example
{
files.rc.<name>.bundle.essential = true;
}
default
{
files.rc.<name>.bundle.essential = false;
}
files.rc.<name>.longrun
longrun
are commands that run as deamon (in a infinity loop).
Applications:
- static site service
- test watcher
- some script to warn that your coffe bottle is empty
type
null or (submodule)
example
{
files.rc.<name>.longrun = {
agreement = {
description = "Check to confirm";
label = "Are you sure?";
};
};
}
default
{
files.rc.<name>.longrun = null;
}
files.rc.<name>.longrun.consumer-for
Dependency hierarchy are calculated at activation time.
See S6 documentation for more info
type
list of non-empty string
example
{
files.rc.<name>.longrun.consumer-for = [
"myService"
];
}
default
{
files.rc.<name>.longrun.consumer-for = [ ];
}
files.rc.<name>.longrun.data
type
null or path
example
{
files.rc.<name>.longrun.data = "./.data";
}
default
{
files.rc.<name>.longrun.data = null;
}
files.rc.<name>.longrun.deps
ready
to this
service work.
Dependency hierarchy are calculated at activation time.
See S6 documentation for more info
type
list of non-empty string
example
{
files.rc.<name>.longrun.deps = [
"myDependency"
];
}
default
{
files.rc.<name>.longrun.deps = [ ];
}
files.rc.<name>.longrun.env
type
null or path
example
{
files.rc.<name>.longrun.env = "./.direnv";
}
default
{
files.rc.<name>.longrun.env = null;
}
files.rc.<name>.longrun.essential
It means some commands may work to stop this service
type
boolean
example
{
files.rc.<name>.longrun.essential = true;
}
default
{
files.rc.<name>.longrun.essential = false;
}
files.rc.<name>.longrun.kill-after
- 0 means never
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.kill-after = 2005;
}
default
{
files.rc.<name>.longrun.kill-after = 0;
}
files.rc.<name>.longrun.kill-finish-after
- 0 means never
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.kill-finish-after = 2005;
}
default
{
files.rc.<name>.longrun.kill-finish-after = 0;
}
files.rc.<name>.longrun.lock-descriptor
running
If value any value other then 0, 1 or 2, this service uses file-descriptor lock
type
null or unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.lock-descriptor = 5;
}
default
{
files.rc.<name>.longrun.lock-descriptor = 0;
}
files.rc.<name>.longrun.max-restarts
- Could not be more than 4096
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.max-restarts = 2005;
}
default
{
files.rc.<name>.longrun.max-restarts = 100;
}
files.rc.<name>.longrun.pipeline-name
Dependency hierarchy are calculated at activation time.
See S6 documentation for more info
type
null or non-empty string
example
{
files.rc.<name>.longrun.pipeline-name = "logMyService";
}
default
{
files.rc.<name>.longrun.pipeline-name = null;
}
files.rc.<name>.longrun.producer-for
Dependency hierarchy are calculated at activation time.
See S6 documentation for more info
type
null or non-empty string
example
{
files.rc.<name>.longrun.producer-for = "willReadMyLog";
}
default
{
files.rc.<name>.longrun.producer-for = null;
}
files.rc.<name>.longrun.readiness
It make your service start script be called with s6-notifyoncheck.
See also ready-descriptor.
type
null or (submodule)
example
{
files.rc.<name>.longrun.readiness = {
command = "curl https://localhost";
};
}
default
{
files.rc.<name>.longrun.readiness = null;
}
files.rc.<name>.longrun.readiness.attempts
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.readiness.attempts = 30;
}
default
{
files.rc.<name>.longrun.readiness.attempts = 7;
}
files.rc.<name>.longrun.readiness.command
If not defined {name}-ready is assumed to be command to run
type
null or non-empty string
example
{
files.rc.<name>.longrun.readiness.command = "curl -sSf http://localhost:8080";
}
default
{
files.rc.<name>.longrun.readiness.command = null;
}
files.rc.<name>.longrun.readiness.fork-fork
type
boolean
example
{
files.rc.<name>.longrun.readiness.fork-fork = true;
}
default
{
files.rc.<name>.longrun.readiness.fork-fork = false;
}
files.rc.<name>.longrun.readiness.initial-delay
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.readiness.initial-delay = 10;
}
default
{
files.rc.<name>.longrun.readiness.initial-delay = 50;
}
files.rc.<name>.longrun.readiness.interval
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.readiness.interval = 1000;
}
default
{
files.rc.<name>.longrun.readiness.interval = 100;
}
files.rc.<name>.longrun.readiness.timeout
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.readiness.timeout = 3000;
}
default
{
files.rc.<name>.longrun.readiness.timeout = 2000;
}
files.rc.<name>.longrun.ready-descriptor
ready
notification method
If value any value other then 0, 1 or 2, this service uses file-descriptor notification method
If attr readiness
is defined, use pooling as notification method
type
null or unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.ready-descriptor = 3;
}
default
{
files.rc.<name>.longrun.ready-descriptor = null;
}
files.rc.<name>.longrun.signal
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.signal = 3;
}
default
{
files.rc.<name>.longrun.signal = 15;
}
files.rc.<name>.longrun.start
If not defined {name}-start is assumed to be command to run
Example for non execline scripts:
```nix
files.rc.alo.oneshot.start =
# creates a python script to run when srv start
# and save it at nix store
let start = pkgs.writeScript "mySrvStartScript"
''
#!/usr/bin/env -S python
print("Alô!") # is hello in portuguese
'';
# use the nix store address
in "${start}";
```</para>
type
null or non-empty string
example
{
files.rc.<name>.longrun.start = "mysqld";
}
default
{
files.rc.<name>.longrun.start = null;
}
files.rc.<name>.longrun.stop
If not defined {name}-stop is assumed to be command to run
Example for non execline scripts:
files.rc.alo.oneshot.stop =
# creates a python script to run when srv stop
# and save it at nix store
let stop = pkgs.writeScript "mySrvStopScript"
''
#!/usr/bin/env -S bash
echo "Tchau!" # is bye in portuguese
'';
# use the nix store address
in "${stop}";
```</para>
#### type
null or non-empty string
#### example
```nix
{
files.rc.<name>.longrun.stop = "abSUrDO kill -9 1 1";
}
default
{
files.rc.<name>.longrun.stop = null;
}
files.rc.<name>.longrun.timeout
ready
in X milliseconds
or it will
considered a failure
transition.
If null or 0 system will wait indefinitely
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.timeout = 10000;
}
default
{
files.rc.<name>.longrun.timeout = 0;
}
files.rc.<name>.longrun.timeout-down
stoped
in X milliseconds
or it will
considered a failure
transition.
If null or 0 system will wait indefinitely
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.longrun.timeout-down = 10000;
}
default
{
files.rc.<name>.longrun.timeout-down = 0;
}
files.rc.<name>.oneshot
oneshot
is a change in the system state
Applications examples:
- start other service that aren't controlled by this environment
- mount files
- sync sources
type
null or (submodule)
example
{
files.rc.<name>.oneshot = {
dependencies = [
"holyMoses"
];
setup = {
example = "echo Hello World > $PRJ_DATA_DIR/hello.txt";
};
timeout = 2000;
};
}
default
{
files.rc.<name>.oneshot = null;
}
files.rc.<name>.oneshot.deps
ready
to this
service work.
Dependency hierarchy are calculated at activation time.
type
list of non-empty string
example
{
files.rc.<name>.oneshot.deps = [
"myDependency"
];
}
default
{
files.rc.<name>.oneshot.deps = [ ];
}
files.rc.<name>.oneshot.essential
It means some commands may work to stop these services
type
boolean
example
{
files.rc.<name>.oneshot.essential = true;
}
default
{
files.rc.<name>.oneshot.essential = false;
}
files.rc.<name>.oneshot.start
If not defined {name}-start is assumed to be command to run
Example for non execline scripts:
files.rc.alo.oneshot.start =
# creates a python script to run when srv start
# and save it at nix store
let start = pkgs.writeScript "mySrvStartScript"
''
#!/usr/bin/env -S python
print("Alô!") # is hello in portuguese
'';
# use the nix store address
in "${start}";
```</para>
#### type
null or non-empty string
#### example
```nix
{
files.rc.<name>.oneshot.start = "echo Hello World > $PRJ_DATA_DIR/hello.txt";
}
default
{
files.rc.<name>.oneshot.start = null;
}
files.rc.<name>.oneshot.stop
If not defined {name}-stop is assumed to be command to run
Example for non execline scripts:
files.rc.alo.oneshot.stop =
# creates a python script to run when srv stop
# and save it at nix store
let stop = pkgs.writeScript "mySrvStopScript"
''
#!/usr/bin/env -S bash
echo "Tchau!" # is bye in portuguese
'';
# use the nix store address
in "${stop}";
```</para>
#### type
null or non-empty string
#### example
```nix
{
files.rc.<name>.oneshot.stop = "rm $PRJ_DATA_DIR/hello.txt";
}
default
{
files.rc.<name>.oneshot.stop = null;
}
files.rc.<name>.oneshot.timeout
ready
in X milliseconds
or it will
considered be in failure
status.
If null or 0 system will wait indefinitely
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.oneshot.timeout = 10000;
}
default
{
files.rc.<name>.oneshot.timeout = 0;
}
files.rc.<name>.oneshot.timeout-down
milliseconds
or it will
considered be in failure
status.
If null or 0 system will wait indefinitely
type
unsigned integer, meaning >=0
example
{
files.rc.<name>.oneshot.timeout-down = 10000;
}
default
{
files.rc.<name>.oneshot.timeout-down = 0;
}
files.services
{name} must be a executable command that runs forever.
Optionally could exist a {name}-finish command to stop it properly.
Optionally could exist a {name}-log command to log it properly.
Default log informations goes to $PRJ_SVCS_LOG/{name}/current .
initSvcs
is a special name to auto start the process supervisor
s6, it control all other services.
If we don't set initSvcs service, we can start it running initSvcs
.
stopSvcsd
is a special name to auto stop the process supervisor
If S6 wont stop by itself, we could run stopSvcs
when it's done.
It defines two env vars:
- PRJ_SVCS_DIR: $PRJ_DATA_DIR/services
- PRJ_SVCS_LOG: $PRJ_DATA_DIR/log
See S6 documentation.
We can use config.files.alias to help create your services scripts.
examples:
Use some program as service
Configure httplz (http static server) as service
{
# # Make services start when you enter in shell
# files.services.initSvcs = true;
# static http service
files.cmds.httplz = true; # Install httplz
files.services.httpd = true; # Use cmd 'httpd' as service
files.alias.httpd = ''
# This is an alias of httplz
cd $PRJ_ROOT/gh-pages
mdbook build
# IMPORTANT NOTE:
# if we don't use `exec` it may not stop with stopSvcs
exec httplz --port 8022 $PRJ_ROOT/gh-pages/book/
'';
# greeting service
files.cmds.notify-desktop = true; # Install notify-desktop
files.services.greet = true; # Use cmd 'greet' as service
files.alias.greet = ''
# Greet people
set +e # ignore errors bacause this service isn't critical
notify-desktop -i network-server "Welcome"
# our stdout goes to .data/log/greet
echo Dornröschen
# if service ends, it will restarted
# we sleep emulating some actual service
exec sleep infinity
'';
files.alias.greet-finish = ''
notify-desktop -i network-server "See you later"
'';
# This is a service to speedup direnv
files.direnv.auto-build.enable = true;
# special service to auto start
files.services.initSvcs = true;
# special service to auto stopSvcs
files.services.stopSvcsd = true;
# files.services-opts.tini.enable = true;
# files.services-opts.namespace.enable = true;
# # RC (unstable) is another interface it uses s6-rc
# # http://skarnet.org/software/s6-rc/why.html
#
# files.rc.hello.enable = true;
# files.rc.hello.oneshot.start = ''wait 1000000 "hello world"'';
# files.rc.hello.oneshot.stop = ''echo "bye bye world"'';
# files.rc.hello.oneshot.timeout = 1200;
#
# files.rc.olla.enable = true;
# files.rc.olla.longrun.start = ''echo "hello world"'';
# files.rc.olla.longrun.stop = ''echo "bye bye world"'';
# files.rc.olla.longrun.deps = ["hello"];
#
# files.rc.hellos.enable = true;
# files.rc.hellos.bundle.deps = ["hello" "olla"];
}
Create your own service with bash
{
# Make all services start when you enter in shell
files.services.initSvcs = true;
# Use hello configured below as service
files.services.hello = true;
# Creates an hello command in terminal
files.alias.hello = ''
while :
do
echo "Hello World!"
sleep 60
done
'';
}
See also:
files.services-opts.tini.enable
to use Tini as supervisor supervisorfiles.services-opts.namespace.enable
to use namespace
Know bugs:
If we don't use exec
in our alias, some necromancer could
form an army of undead process, and start a war against our
system, since they both may require the most scarse resource
of our lands.
You can enable namespace if it is an problem
type
attribute set of boolean
example
{
files.services = {
hello = true;
};
}
default
{
files.services = { };
}
files.services-opts.namespace.enable
type
boolean
example
{
files.services-opts.namespace.enable = true;
}
default
{
files.services-opts.namespace.enable = false;
}
files.services-opts.namespace.options
type
string
default
{
files.services-opts.namespace.options = "--pid --fork --map-root-user";
}
files.services-opts.tini.enable
type
boolean
example
{
files.services-opts.tini.enable = true;
}
default
{
files.services-opts.tini.enable = false;
}
files.services-opts.tini.options
type
string
default
{
files.services-opts.tini.options = "-sp SIGTERM";
}
files.license.enable
type
boolean
example
{
files.license.enable = true;
}
default
{
files.license.enable = false;
}
files.license.spdx
type
submodule
example
{
files.license.spdx = {
name = "MIT";
vars = {
"copyright holders" = "Cruel Intentions";
year = "2021";
};
};
}
default
{
files.license.spdx = { };
}
files.license.spdx.name
type
string
example
{
files.license.spdx.name = "MIT";
}
files.license.spdx.vars
type
attribute set of string
example
{
files.license.spdx.vars = {
OWNER = "cruel-intentions";
"URL for Development Group/Institution" = "https://github.com/cruel-intentions";
year = "2021";
"yyyy, yyyy" = "2021, 2099";
};
}
default
{
files.license.spdx.vars = { };
}
files.text
type
attribute set of strings concatenated with "\n"
example
{
files.text = {
"/hello.txt" = ''
((((( ********* ****
((((((( ********* *******
((((((((( .********* *********
((((((((( ********* *********
((((((((( *****************
((((((((( **************
(((((((((((((((((((((((((((((((( *********** (
(((((((((((((((((((((((((((((((((((( ********* (((((
(((((((((((((((((((((((((((((((((((((( ********* (((((((
********* ********* /((((((((
********* ********* (((((((((
********* ******* ((((((((/
********* **** (((((((((
***********************. ,* (((((((((((((((((((((.
*********************** ((((((((((((((((((((((((
*********************.(( ((((((((((((((((((((((((
********* (((((( (((((((((
********* (((((((( (((((((((
********* ((((((((( (((((((((
********* (((((((((
.***** /((((((((**************************************
*** *((((((((( **********************************
(((((((((((( *******************************,
((((((((((((((( ,********
(((((((((((((((((( *********
((((((((( ((((((((( *********
((((((((( ((((((((( *********
(((((( ((((((((( ******
'';
};
}
default
{
files.text = { };
}
files.toml
type
attribute set of (JSON value)
example
{
files.toml = {
"/hellaos.toml" = {
greeting = "hello World";
};
"/hellows.toml" = {
greetings = [
[
"Hello World"
]
[
"Ola Mundo"
[
"Holla Que Tal"
]
]
];
};
};
}
default
{
files.toml = { };
}
files
type
submodule
files.watch
type
lazy attribute set of (submodule)
example
{
files.watch = {
FOO = {
cmd = "echo $file $dir $time $events";
event = {
modify = true;
};
exclude = ".+\\.png$";
files = "$PRJ_ROOT/src";
include = ".+\\.js$";
recursive = true;
};
};
}
default
{
files.watch = { };
}
files.watch.<name>.enable
type
boolean
default
{
files.watch.<name>.enable = true;
}
files.watch.<name>.cmd
type
string
files.watch.<name>.event
type
submodule
default
{
files.watch.<name>.event = { };
}
files.watch.<name>.event.access
type
boolean
default
{
files.watch.<name>.event.access = false;
}
files.watch.<name>.event.attrib
type
boolean
default
{
files.watch.<name>.event.attrib = false;
}
files.watch.<name>.event.close
type
boolean
default
{
files.watch.<name>.event.close = false;
}
files.watch.<name>.event.close_nonwrite
type
boolean
default
{
files.watch.<name>.event.close_nonwrite = false;
}
files.watch.<name>.event.close_write
type
boolean
default
{
files.watch.<name>.event.close_write = false;
}
files.watch.<name>.event.create
type
boolean
default
{
files.watch.<name>.event.create = false;
}
files.watch.<name>.event.delete
type
boolean
default
{
files.watch.<name>.event.delete = false;
}
files.watch.<name>.event.modify
type
boolean
default
{
files.watch.<name>.event.modify = true;
}
files.watch.<name>.event.move
type
boolean
default
{
files.watch.<name>.event.move = false;
}
files.watch.<name>.event.move_from
type
boolean
default
{
files.watch.<name>.event.move_from = false;
}
files.watch.<name>.event.move_to
type
boolean
default
{
files.watch.<name>.event.move_to = false;
}
files.watch.<name>.event.open
type
boolean
default
{
files.watch.<name>.event.open = false;
}
files.watch.<name>.event.unmount
type
boolean
default
{
files.watch.<name>.event.unmount = false;
}
files.watch.<name>.exclude
type
string
default
{
files.watch.<name>.exclude = "(.*/\\.|.+~$)";
}
files.watch.<name>.excludei
type
string
default
{
files.watch.<name>.excludei = "";
}
files.watch.<name>.files
type
strings concatenated with "\n"
default
{
files.watch.<name>.files = "$PRJ_ROOT";
}
files.watch.<name>.follow
type
boolean
default
{
files.watch.<name>.follow = false;
}
files.watch.<name>.include
type
string
default
{
files.watch.<name>.include = "";
}
files.watch.<name>.includei
type
string
default
{
files.watch.<name>.includei = "";
}
files.watch.<name>.recursive
type
boolean
default
{
files.watch.<name>.recursive = true;
}
files.watch.<name>.timefmt
type
string
default
{
files.watch.<name>.timefmt = "%FT%T.000%zZ";
}
files.yaml
type
attribute set of (JSON value)
example
{
files.yaml = {
"/hellaos.yaml" = {
greeting = "hello World";
};
"/hellows.yaml" = {
greetings = [
[
"Hello World"
]
[
"Ola Mundo"
[
"Holla Que Tal"
]
]
];
};
};
}
default
{
files.yaml = { };
}
TODO
- Add modules for especific cases:
- ini files
- most common ci/cd configuration
- Verify if devshell could add it as default
Issues
This project uses git as version control, if your are using other version control system it may not work.
See also
- Nix the tool
- DevShell the framework
- Digga similar project
- Makes similar project
- Nixago similar project
- Home Manager similar project (for user home configs)
- NixOS similar project (for system configs)
- Nix Ecosystem more projects using same tool
- Nix Mindmap of how nix ecosystem works
- Nix.Dev
- Nixology
Don't look at
*nix
general definition of Unix/Linux- Nixos.com NSFW
- Nixos.com.br furniture