11 |
12 | Yolk is a cross platform dotfile management tool with a unique spin on templating,
13 | sitting somewhere in between [GNU Stow](https://www.gnu.org/software/stow/) and [chezmoi](https://www.chezmoi.io/).
14 |
15 | Have a look at our [documentation](https://elkowar.github.io/yolk/book) for more information on how to get started!
16 |
17 | ## The Concept
18 |
19 | Yolk allows you to use simple templates in your configuration files without having to worry about keeping a separate template file and the generated config file in sync.
20 | This is achieved through a design that allows all templates to be included inside comments in your actual configuration file.
21 |
22 | Let's demonstrate:
23 |
24 | ```toml
25 | # Use a different font on one host
26 | # {% if SYSTEM.hostname == "epic-desktop" %}
27 | font="Fira Code"
28 | # {% else %}
29 | # font="Iosevka"
30 | # {% end %}
31 |
32 | [colors]
33 | # Load your colors from your yolk configuration
34 | background="#282828" # {< replace_color(data.colors.background) >}
35 | foreground="#ebdbb2" # {< replace_color(data.colors.foreground) >}
36 | ```
37 |
38 | Yolk will now be able to run the corresponding modifications on the file itself, allowing you to set
39 | templated values while keeping the template directly in the same file.
40 |
41 | ### User Configuration
42 |
43 | Yolk template expressions and configuration are written in the [Rhai](https://rhai.rs/) scripting language.
44 | You can provide custom data to use in your templates via the `yolk.rhai` file in your yolk directory,
45 | which allows you to fetch data dynamically from your system, or reference different static data depending on your system.
46 |
47 | ### Version Control
48 |
49 | How does this work with git?
50 | Given that the concrete files in use on your system may be different across machines,
51 | adding those to version control directly would result in a lot of merge conflicts frequently.
52 | Yolk solves this by only commiting a "canonical" version of your templates, generated right before you commit.
53 | This means that the version of your configuration seen in git will always be generated from a consistent, stable context.
54 |
--------------------------------------------------------------------------------
/dist-workspace.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = ["cargo:."]
3 |
4 | [dist]
5 | windows-archive = ".zip"
6 | fail-fast = true
7 | github-attestations = true
8 | global-artifacts-jobs = ["./build-man"]
9 | # github-build-setup = "build-setup.yml"
10 | cargo-dist-version = "0.28.4"
11 | # pr-run-mode = "upload"
12 | pr-run-mode = "plan"
13 | ci = "github"
14 | installers = [
15 | "shell",
16 | # "homebrew",
17 | "powershell",
18 | ]
19 | # tap = "elkowar/homebrew-tap"
20 | # publish-jobs = ["homebrew"]
21 | formula = "yolk"
22 | targets = [
23 | # "aarch64-apple-darwin",
24 | "x86_64-apple-darwin",
25 | "x86_64-unknown-linux-gnu",
26 | "x86_64-pc-windows-msvc",
27 | "x86_64-unknown-linux-musl",
28 | ]
29 | install-path = "CARGO_HOME"
30 | install-updater = false
31 |
32 | # include = ["yolk.man"]
33 |
34 | allow-dirty = ["ci"]
35 |
36 | [dist.github-custom-runners]
37 | # Use an `ubuntu-latest` runner for all "global" steps of the release process,
38 | # rather than cargo-dist's default of using the oldest possible Linux runner.
39 | # This includes `plan`, `build-global-artifacts`, `host`, and `announce`, none
40 | # of which actually rely on the specific Linux version.
41 | global = "ubuntu-latest"
42 | local = "ubuntu-latest"
43 | x86_64-unknown-linux-gnu = "ubuntu-latest"
44 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | book
2 |
--------------------------------------------------------------------------------
/docs/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["elkowar"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 | title = "Yolk"
7 | [output.html]
8 | theme = "theme"
9 | default-theme = "dark"
10 | preferred-dark-theme = "theme"
11 | additional-js = ["./theme/tabs.js"]
12 |
13 |
14 | smart-punctuation = true
15 | git-repository-url = "https://github.com/elkowar/yolk"
16 | edit-url-template = "https://github.com/elkowar/yolk/edit/main/docs/{path}"
17 |
--------------------------------------------------------------------------------
/docs/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | - [Getting started](./getting_started.md)
4 | - [Git concepts](./git_concepts.md)
5 | - [Eggs](./eggs.md)
6 | - [Templates](./templates.md)
7 | - [The yolk.rhai file](./yolk_rhai.md)
8 | - [Conditionals](./conditional_templates.md)
9 | - [Custom Template Functions](./custom_template_functions.md)
10 | - [Yolk on Windows](./yolk_on_windows.md)
11 | # Rhai reference
12 | - [Function Reference](./rhai_docs/index.md)
13 | - [Template functions](./rhai_docs/template.md)
14 | - [Utils](./rhai_docs/utils.md)
15 | - [IO Functions](./rhai_docs/io.md)
16 | - [Rhai stdlib](./rhai_docs/global.md)
17 |
--------------------------------------------------------------------------------
/docs/src/conditional_templates.md:
--------------------------------------------------------------------------------
1 | # Conditionals
2 |
3 | Yolk allows you to conditionally include parts of your configuration based on the state of your system.
4 | It achieves this by commenting or un-commenting blocks of your file.
5 |
6 | Conditionals use special template tags that start with the keyword `if`,
7 | which instructs Yolk to treat the following expression as a conditional,
8 | rather than a regular template tag function.
9 |
10 | ## Multiline conditionals
11 |
12 | The most common form of conditional block is using the multiline template tag syntax.
13 | Let's type out a simple example:
14 |
15 | ```toml
16 | # {% if SYSTEM.hostname == "epic-desktop" %}
17 | displays = ["DP-1", "DP-2"]
18 | # {% if SYSTEM.hostname == "business-desktop" %}
19 | displays = ["HDMI-1", "HDMI-2"]
20 | # {% else %}
21 | displays = ["eDP-1"]
22 | # {% end %}
23 | ```
24 |
25 | Now, this is of course not a valid configuration just yet, as we're setting the `displays` variable thrice.
26 |
27 | However, once you run `yolk sync`, yolk will evaluate the condition and comment out the blocks that don't apply.
28 | For example, on your laptop, this config might be turned into:
29 |
30 | ```toml
31 | # {% if SYSTEM.hostname == "epic-desktop" %}
32 | # displays = ["DP-1", "DP-2"]
33 | # {% if SYSTEM.hostname == "business-desktop" %}
34 | # displays = ["HDMI-1", "HDMI-2"]
35 | # {% else %}
36 | displays = ["eDP-1"]
37 | # {% end %}
38 | ```
39 |
40 | Note that yolk added a special `` prefix to the comments.
41 | Yolk conditionals will only ever add or remove comments with this prefix,
42 | which means that you can still have regular comments in those conditional blocks.
43 |
44 | ## Inline and Next-line conditionals
45 |
46 | A more simplified version of this is also supported in inline and next-line tags:
47 |
48 | ```kdl
49 | enable_variable_refreshrate // {< if data.is_desktop() >}
50 |
51 | // {# if data.enable_xwayland #}
52 | spawn-at-startup "xwayland-satellite"
53 | ```
54 |
--------------------------------------------------------------------------------
/docs/src/custom_template_functions.md:
--------------------------------------------------------------------------------
1 | # Custom Template Functions
2 |
3 | Given that all of the template functions are just regular Rhai code, you might ask yourself if you can define your own template tag functions.
4 | The answer is YES!
5 |
6 | ## How do template tag functions work
7 |
8 | Template tag functions are executed in a special context, in which a function called `get_yolk_text()` is available.
9 | This variable contains the text block that the template tag operates on.
10 | A template tag function then returns a string which yolk will replace the old text block with.
11 |
12 | ## Example
13 |
14 | Let's define a simple, useless template tag function in your `yolk.rhai`.
15 |
16 | ```rust,ignore
17 | fn scream_or_not(should_scream) {
18 | if should_scream {
19 | get_yolk_text().to_upper()
20 | } else {
21 | get_yolk_text().to_lower()
22 | }
23 | }
24 | ```
25 |
26 | That's it!
27 | Now, we can go into any templated file, and use our new template tag function.
28 |
29 | ```toml
30 | # {# scream_or_not(SYSTEM.hostname == "loud-host") #}
31 | screaming = "HELLO"
32 | ```
33 |
--------------------------------------------------------------------------------
/docs/src/eggs.md:
--------------------------------------------------------------------------------
1 | # Eggs
2 |
3 | An egg is one package of configuration, typically for one single application.
4 | For example, your editor configuration `~/.config/nvim` would likely be one egg called `nvim`.
5 |
6 | According to your `yolk.rhai`, these get deployed on your system, and may contain some templated files.
7 |
--------------------------------------------------------------------------------
/docs/src/getting_started.md:
--------------------------------------------------------------------------------
1 | # Getting started
2 |
3 |
4 |
5 | **Remember: Always have a good backup of your files before using any tool that modifies your files. Nothing bad should happen here, but better be careful.**
6 |
7 |
8 |
9 | ## How dotfiles are stored
10 |
11 | Yolk manages your dotfiles by storing them in a separate directory, typically inside `~/.config/yolk`.
12 | This allows you to keep your dotfiles in version control easily, and lets you manage your configuration from one central location.
13 |
14 | Yolk groups dotfiles into so-called ["eggs"](eggs.md), which are packages of configuration,
15 | typically for one single application (although you can group them however you want, or even just have one egg for all your configuration files).
16 |
17 | When an egg is "deployed", Yolk creates symlinks in the target location pointing towards the egg directory.
18 | This way, the configured applications will see the configuration files as they expect to see them.
19 |
20 | To define where a set of configuration files should be deployed to, you declare each of your eggs in your [main yolk configuration file](./yolk_rhai.md).
21 | This allows you, among other things, to define a different target directory per system.
22 |
23 | ## Initial setup
24 |
25 | To get started with Yolk, you'll first need to set up the Yolk file structure.
26 |
27 | ```bash
28 | $ yolk init
29 | ```
30 |
31 | This will create the yolk directory, with a default `yolk.rhai` file, and an `eggs` directory.
32 |
33 | ### Adding your first egg
34 |
35 | let's say we want to manage the configuration for the `alacritty` terminal emulator.
36 | To do this, we first move our alacritty configuration into the `eggs` directory:
37 |
38 | ```bash
39 | $ mv ~/.config/alacritty ~/.config/yolk/eggs/
40 | ```
41 |
42 | And then configure the corresponding [egg deployment](./yolk_rhai.md#basic-structure):
43 |
44 | ```rust,ignore
45 | export let eggs = #{
46 | alacritty: #{
47 | targets: "~/.config/alacritty",
48 | templates: ["alacritty.yml"],
49 | enabled: true,
50 | }
51 | }
52 | ```
53 |
54 | Now we can run `yolk sync`!
55 | This will set up a symlink from the target location `~/.config/alacritty`
56 | back to the alacritty egg directory `~/.config/yolk/eggs/alacritty`.
57 |
58 | ### Committing your dots to git
59 |
60 | Now, we want to make sure our dotfiles are in version control and pushed to our git host of choice.
61 | Every interaction with git should be done through the `yolk git` command.
62 | This ensures that git sees the canonical (stable) representation of your files, and automatically performs them from within the yolk directory.
63 |
64 | ```bash
65 | $ yolk safeguard # you only need to run this once.
66 | $ yolk git add --all
67 | $ yolk git commit -m "Setup alacritty"
68 | ```
69 |
70 | To understand what `yolk safeguard` does, see [safeguarding git](./git_concepts.md#safeguarding-git).
71 |
72 | You can now set up your git remote and use git as usual -- just remember to always use `yolk git`, especially when you're committing your files.
73 |
74 | ### Baby's first template
75 |
76 | Because you too are very indecisive about your terminal colors,
77 | you now decide you want to use yolk to manage your color theme for alacritty, and any other applications that you might add later.
78 | You also decide that you want to use a different color scheme on your desktop and your laptop.
79 |
80 | To achieve this, let's first add a declaration of your color theme in your `~/.config/yolk/yolk.rhai` file:
81 |
82 | ```rust,ignore
83 | // ... snip ...
84 |
85 | const themes = #{
86 | gruvbox: #{
87 | background: "#282828",
88 | foreground: "#ebdbb2",
89 | },
90 | mono: #{
91 | background: "#000000",
92 | foreground: "#ffffff",
93 | },
94 | }
95 |
96 | export const data = #{
97 | colors: if SYSTEM.hostname == "laptop" { themes.gruvbox } else { themes.mono }
98 | }
99 | ```
100 |
101 | Beautiful!
102 | What we're doing here is setting up an *exported* table called `data`, which will store any user-data we might want to refer to in our templates in the future.
103 | We set up a field `colors`, which we then set to a different color scheme depending on the hostname of the system.
104 |
105 | **Don't forget to `export` any variables you might want to reference in your template tags!**
106 |
107 | Now, let's set up a template in our alacritty config file:
108 |
109 | ```toml
110 | #...
111 | [colors.primary]
112 | background = "#ff0000" # {< replace_color(data.colors.background) >}
113 | foreground = "#ff0000" # {< replace_color(data.colors.foreground) >}
114 | # ...
115 | ```
116 |
117 | Let's break down what's happening here:
118 | Inside the comments after the color declarations, we're using "inline template tags", as indicated by the `{< ... >}` syntax.
119 | These inline tags transform whatever is before them in the line.
120 | The tag calls the built-in `replace_color` function, which looks for a hex-code and replaces it with the value from the `data.colors` table.
121 |
122 | **Let's try it**!
123 | Run
124 |
125 | ```bash
126 | $ yolk sync
127 | ```
128 |
129 | You will see that, your `alacritty.toml` has changed, and the colors from your `yolk.rhai` file have been applied, depending on your hostname.
130 |
--------------------------------------------------------------------------------
/docs/src/git_concepts.md:
--------------------------------------------------------------------------------
1 | # Git concepts
2 |
3 | Basic familiarity with git is assumed.
4 |
5 | ## Safeguarding git
6 |
7 | Yolk wraps the git CLI to ensure that git only ever interacts with your dotfiles in their canonical state.
8 | If it didn't do that, you would end up committing the local state of your dotfiles,
9 | which would conflict with their state from another machine -- which is what yolk is trying to solve.
10 |
11 | To ensure that you're not accidentally using the regular git CLI for your dotfiles, it is recommended to "safeguard" your dotfiles' git directory.
12 | To do this, simply run
13 |
14 | ```bash
15 | $ yolk safeguard
16 | ```
17 |
18 | after cloning or initializing your dotfiles.
19 |
20 | This simply renames the `.git` directory to `.yolk_git`, which means the regular git CLI won't see the repository anymore.
21 | You are now more or less forced to use the `yolk git` command instead -- which conveniently doesn't just ensure consistency of the git state,
22 | but also works from anywhere in your filesystem!
23 |
24 | ## Cloning your dotfiles
25 |
26 | To clone your dotfiles on a new machine, simply clone the repository to `.config/yolk`, and safeguard your git directory.
27 |
28 | ```bash
29 | $ git clone "$XDG_CONFIG_HOME/yolk"
30 | $ yolk safeguard
31 | ```
32 |
33 | After that, you can start `yolk sync`ing your eggs!
34 |
35 | ## Interacting with git
36 |
37 | To stage or commit changes, get the git diff or status, you can use the `yolk git` command, which behaves just like the `git` CLI.
38 | So, instead of
39 |
40 | - `git status`, you run `yolk git status`,
41 | - `git add .`, you run `yolk git add --all`,
42 | - `git commit -m "cool changes"`, you run `yolk git commit -m "cool changes`,
43 |
44 | and so on.
45 | This ensures the files are always in the correct canonical state, and makes it possible to interact with a safeguarded git repository.
46 |
--------------------------------------------------------------------------------
/docs/src/rhai_docs/index.md:
--------------------------------------------------------------------------------
1 | # Function Reference
2 |
3 | Here you can find the different functions available for use in yolk.
4 | This documentation is generated through the code,
5 | so while it should be very accurate,
6 | the type signatures might look a bit confusing in some places.
7 |
--------------------------------------------------------------------------------
/docs/src/rhai_docs/io.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # IO Functions
4 |
5 | A collection of functions that can read the environment and filesystem.
6 | These return standardized values in canonical mode.
7 |
8 | ---
9 |
10 | **namespace**: `io`
11 |
12 | ---
13 |
14 |
15 |
16 |
17 |
18 | ## command_available
19 |
20 |
21 |
22 | ```rust,ignore
23 | command_available(name: &str) -> Result
24 | ```
25 |
26 | Check if a given command is available
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ## env
37 |
38 |
39 |
40 | ```rust,ignore
41 | env(name: &str, def: &str) -> Result
42 | ```
43 |
44 | Read an environment variable, or return the given default
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | ## path_exists
55 |
56 |
57 |
58 | ```rust,ignore
59 | path_exists(p: &str) -> Result
60 | ```
61 |
62 | Check if something exists at a given path
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | ## path_is_dir
73 |
74 |
75 |
76 | ```rust,ignore
77 | path_is_dir(p: &str) -> Result
78 | ```
79 |
80 | Check if the given path is a directory
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | ## path_is_file
91 |
92 |
93 |
94 | ```rust,ignore
95 | path_is_file(p: &str) -> Result
96 | ```
97 |
98 | Check if the given path is a file
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | ## read_dir
109 |
110 |
111 |
112 | ```rust,ignore
113 | read_dir(p: &str) -> Result>
114 | ```
115 |
116 | Read the children of a given dir
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | ## read_file
127 |
128 |
129 |
130 | ```rust,ignore
131 | read_file(p: &str) -> Result
132 | ```
133 |
134 | Read the contents of a given file
135 |
136 |
2 |
3 | # Template tag functions
4 |
5 | Yolk template tags simply execute rhai functions that transform the block of text the tag operates on.
6 |
7 | Quick reminder: Yolk has three different types of tags, that differ only in what text they operate on:
8 |
9 | - Next-line tags (`{# ... #}`): These tags operate on the line following the tag.
10 | - Inline tags (`{< ... >}`): These tags operate on everything before the tag within the same line.
11 | - Block tags (`{% ... %} ... {% end %}`): These tags operate on everything between the tag and the corresponding `{% end %}` tag.
12 |
13 | Inside these tags, you can call any of Yolks template tag functions (Or, in fact, any rhai expression that returns a string).
14 |
15 | ---
16 |
17 | **namespace**: `template`
18 |
19 | ---
20 |
21 |
22 |
23 |
24 |
25 | ## replace_between
26 |
27 |
28 |
29 | ```rust,ignore
30 | replace_between(left: &str, right: &str, replacement: &str) -> Result
31 | ```
32 |
33 | **shorthand**: `rbet`.
34 |
35 | Replaces the text between two delimiters with the `replacement`.
36 |
37 | #### Example
38 |
39 | ```handlebars
40 | ui_font = (Arial) # {< replace_between(`(`, `)`, data.font.ui) >}
41 | ```
42 |
43 | Note: we don't need to include the quotes in the replacement here.
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | ## replace_color
54 |
55 |
56 |
57 | ```rust,ignore
58 | replace_color(replacement: &str) -> Result
59 | ```
60 |
61 | **shorthand**: `rcol`.
62 |
63 | Replaces a hex color value with a new hex color.
64 |
65 | #### Example
66 |
67 | ```handlebars
68 | background_color = "#282828" # {< replace_color(data.colors.bg) >}
69 | ```
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | ## replace_in
80 |
81 |
82 |
83 | ```rust,ignore
84 | replace_in(between: &str, replacement: &str) -> Result
85 | ```
86 |
87 | **shorthand**: `rin`.
88 |
89 | Replaces the text between two delimiters with the `replacement`.
90 |
91 | #### Example
92 |
93 | ```toml
94 | ui_font = "Arial" # {< replace_in(`"`, data.font.ui) >}
95 | ```
96 |
97 | Note: we don't need to include the quotes in the replacement here.
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | ## replace_number
108 |
109 |
110 |
111 | ```rust,ignore
112 | replace_number(replacement: Dynamic) -> Result
113 | ```
114 |
115 | **shorthand**: `rnum`.
116 |
117 | Replaces a number with another number.
118 |
119 | #### Example
120 |
121 | ```handlebars
122 | cursor_size = 32 # {< replace_number(data.cursor_size) >}
123 | ```
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | ## replace_quoted
134 |
135 |
136 |
137 | ```rust,ignore
138 | replace_quoted(replacement: &str) -> Result
139 | ```
140 |
141 | **shorthand**: `rq`.
142 |
143 | Replaces a value between quotes with another value
144 |
145 | #### Example
146 |
147 | ```handlebars
148 | ui_font = "Arial" # {< replace_quoted(data.font.ui) >}
149 | ```
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | ## replace_re
160 |
161 |
162 |
163 | ```rust,ignore
164 | replace_re(regex: &str, replacement: &str) -> Result
165 | ```
166 |
167 | **shorthand**: `rr`.
168 |
169 | Replaces all occurrences of a Regex `pattern` with `replacement` in the text.
170 |
171 | #### Example
172 |
173 | ```handlebars
174 | ui_font = "Arial" # {< replace_re(`".*"`, `"{data.font.ui}"`) >}
175 | ```
176 |
177 | Note that the replacement value needs to contain the quotes, as those are also matched against in the regex pattern.
178 | Otherwise, we would end up with invalid toml.
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | ## replace_value
189 |
190 |
191 |
192 | ```rust,ignore
193 | replace_value(replacement: &str) -> Result
194 | ```
195 |
196 | **shorthand**: `rv`.
197 |
198 | Replaces a value (without spaces) after a `:` or a `=` with another value
199 |
200 | #### Example
201 |
202 | ```handlebars
203 | ui_font = Arial # {< replace_value(data.font.ui) >}
204 | ```
205 |
206 |