chore: big dump
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run

This commit is contained in:
Viktor Varland 2023-11-16 09:34:09 +01:00
parent 12173463ab
commit 9f7a77273f
Signed by: varl
GPG key ID: 7459F0B410115EE8
22 changed files with 766 additions and 176 deletions

View file

@ -0,0 +1,19 @@
name: Gitea Actions Demo
run-name: ${{ gitea.actor }} is testing out Gitea Actions
on: [push]
jobs:
Explore-Gitea-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- run: echo "This job's status is ${{ job.status }}."

View file

@ -1,5 +1,3 @@
# blog # blog
[![Build Status](https://ci.meatbag.se/api/badges/varl/blog/status.svg)](https://ci.meatbag.se/varl/blog) [![Build Status](https://ci.meatbag.se/api/badges/varl/blog/status.svg)](https://ci.meatbag.se/varl/blog)

View file

@ -7,7 +7,14 @@
@layer base { @layer base {
html { html {
font-family: IBM Plex Mono, ui-monospace, monospace; font-family:
IBM Plex Mono,
ui-monospace,
monospace;
}
a {
@apply underline underline-offset-4 decoration-slate-500 hover:decoration-indigo-500;
} }
} }

View file

@ -9,10 +9,11 @@
{% for date, posts in collections.posts | groupby('pub.year') | dictsort | reverse %} {% for date, posts in collections.posts | groupby('pub.year') | dictsort | reverse %}
<h2 class="{{classes.h2}}">{{ date }}</h2> <h2 class="{{classes.h2}}">{{ date }}</h2>
<hr class="{{ classes.hr }}"/> <hr class="{{ classes.hr }}"/>
{% for post in posts %} {% for post in posts %}
<div> <div>
<h4 class="{{ classes.h4 }}"><a href="{{ post.permalink }}">{{ post.title | safe }}</a></h4> <h4 class="{{ classes.h4 }}"><a href="{{ post.permalink }}">{{ post.title | safe }}</a></h4>
<h5>{{ post.description | safe }}</h5> <h5 class="{{ classes.post_date }}">{{post.date}}</h5>
</div> </div>
{% else %} {% else %}
No posts No posts

View file

@ -4,18 +4,21 @@
{{ contents | safe }} {{ contents | safe }}
{% if previous or next %}
<hr class="{{ classes.hr }}"/>
<div class="flex flex-row justify-around">
{% if previous %} {% if previous %}
<p> <p>
Previous: Previous: <a href="/{{ previous.path }}">{{ previous.title }}</a>
<a href="/{{ previous.path }}">{{ previous.title }}</a>
</p> </p>
{% endif %} {% endif %}
{% if next %} {% if next %}
<p> <p>
Next: Next: <a href="/{{ next.path }}">{{ next.title }}</a>
<a href="/{{ next.path }}">{{ next.title }}</a>
</p> </p>
{% endif %} {% endif %}
</div>
{% endif %}
{% endblock %} {% endblock %}

View file

@ -13,13 +13,15 @@ export const classes = {
hr: 'border-red-900', hr: 'border-red-900',
code: 'text-slate-400', code: 'text-slate-400',
image: 'grayscale', image: 'grayscale',
} post_date: 'text-base italic',
};
export const rules = (md) => { export const rules = (md) => {
const proxy = (tokens, idx, options, env, self) => self.renderToken(tokens, idx, options); const proxy = (tokens, idx, options, env, self) =>
self.renderToken(tokens, idx, options);
const defaultRenderer = (name) => md.renderer.rules[name] || proxy; const defaultRenderer = (name) => md.renderer.rules[name] || proxy;
const defaultBulletListOpenRenderer = defaultRenderer('bullet_list_open') const defaultBulletListOpenRenderer = defaultRenderer('bullet_list_open');
const defaultBlockquoteOpenRenderer = defaultRenderer('blockquote_open'); const defaultBlockquoteOpenRenderer = defaultRenderer('blockquote_open');
const defaultHeadingOpenRenderer = defaultRenderer('heading_open'); const defaultHeadingOpenRenderer = defaultRenderer('heading_open');
const defaultCodeOpenRenderer = defaultRenderer('code_inline'); const defaultCodeOpenRenderer = defaultRenderer('code_inline');
@ -27,42 +29,40 @@ export const rules = (md) => {
const defaultImageRenderer = defaultRenderer('image'); const defaultImageRenderer = defaultRenderer('image');
return { return {
footnote_block_open: () => ( footnote_block_open: () =>
`<hr class="${classes.hr}">\n` + `<hr class="${classes.hr}">\n` +
`<section class="${classes.footnote_section}">\n` + `<section class="${classes.footnote_section}">\n` +
`<ol class="${classes.footnote}">\n` `<ol class="${classes.footnote}">\n`,
),
bullet_list_open: (tokens, idx, options, env, self) => { bullet_list_open: (tokens, idx, options, env, self) => {
tokens[idx].attrJoin("class", classes.bullet_list); tokens[idx].attrJoin('class', classes.bullet_list);
return defaultBulletListOpenRenderer(tokens, idx, options, env, self); return defaultBulletListOpenRenderer(tokens, idx, options, env, self);
}, },
blockquote_open: (tokens, idx, options, env, self) => { blockquote_open: (tokens, idx, options, env, self) => {
tokens[idx].attrJoin("class", classes.blockquote); tokens[idx].attrJoin('class', classes.blockquote);
return defaultBlockquoteOpenRenderer(tokens, idx, options, env, self); return defaultBlockquoteOpenRenderer(tokens, idx, options, env, self);
}, },
heading_open: (tokens, idx, options, env, self) => { heading_open: (tokens, idx, options, env, self) => {
tokens[idx].attrJoin("class", classes[tokens[idx].tag]); tokens[idx].attrJoin('class', classes[tokens[idx].tag]);
return defaultHeadingOpenRenderer(tokens, idx, options, env, self); return defaultHeadingOpenRenderer(tokens, idx, options, env, self);
}, },
code_inline: (tokens, idx, options, env, self) => { code_inline: (tokens, idx, options, env, self) => {
tokens[idx].attrJoin("class", classes.code); tokens[idx].attrJoin('class', classes.code);
return defaultCodeOpenRenderer(tokens, idx, options, env, self); return defaultCodeOpenRenderer(tokens, idx, options, env, self);
}, },
hr: (tokens, idx, options, env, self) => { hr: (tokens, idx, options, env, self) => {
tokens[idx].attrJoin("class", classes.hr); tokens[idx].attrJoin('class', classes.hr);
return defaultHrRenderer(tokens, idx, options, env, self); return defaultHrRenderer(tokens, idx, options, env, self);
}, },
image: (tokens, idx, options, env, self) => { image: (tokens, idx, options, env, self) => {
tokens[idx].attrJoin("class", classes.image); tokens[idx].attrJoin('class', classes.image);
return defaultImageRenderer(tokens, idx, options, env, self); return defaultImageRenderer(tokens, idx, options, env, self);
}, },
} };
} };
;

View file

@ -23,7 +23,7 @@ import { rules, classes } from './lib/tailwind-renderer.js';
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
const t1 = performance.now(); const t1 = performance.now();
let md let md;
Metalsmith(__dirname) Metalsmith(__dirname)
.source('./src') .source('./src')
@ -41,10 +41,12 @@ Metalsmith(__dirname)
generatorurl: 'https://metalsmith.io/', generatorurl: 'https://metalsmith.io/',
classes, classes,
}) })
.use(drafts({ .use(
drafts({
default: false, default: false,
include: false, include: false,
})) }),
)
.use( .use(
markdown({ markdown({
render: function (source, options, context) { render: function (source, options, context) {
@ -58,9 +60,9 @@ Metalsmith(__dirname)
} }
if (context.key == 'contents') { if (context.key == 'contents') {
return md.render(source) return md.render(source);
} else { } else {
return md.renderInline(source) return md.renderInline(source);
} }
}, },
engineOptions: { engineOptions: {
@ -68,12 +70,17 @@ Metalsmith(__dirname)
highlight: function (str, lang) { highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) { if (lang && hljs.getLanguage(lang)) {
try { try {
return `<pre class="${classes.hljs}"><code>${hljs.highlight(str, { language: lang, ignoreIllegals: true }).value}</code></pre>`; return `<pre class="${classes.hljs}"><code>${
hljs.highlight(str, { language: lang, ignoreIllegals: true })
.value
}</code></pre>`;
} catch (__) {} } catch (__) {}
} }
return `<pre class="${classes.hljs}"><code>${md.utils.escapeHtml(str)}</code></pre>`; return `<pre class="${classes.hljs}"><code>${md.utils.escapeHtml(
} str,
)}</code></pre>`;
},
}, },
}), }),
) )
@ -83,7 +90,7 @@ Metalsmith(__dirname)
pattern: 'posts/**/*.html', pattern: 'posts/**/*.html',
sortBy: 'date', sortBy: 'date',
reverse: false, reverse: false,
} },
}), }),
) )
.use( .use(
@ -95,15 +102,15 @@ Metalsmith(__dirname)
remove: /[^a-z0-9- ]+/gi, remove: /[^a-z0-9- ]+/gi,
lower: true, lower: true,
extend: { extend: {
"'": '-' "'": '-',
} },
}, },
}), }),
) )
.use( .use(
inplace({ inplace({
transform: 'nunjucks' transform: 'nunjucks',
}) }),
) )
.use( .use(
layouts({ layouts({

42
package-lock.json generated
View file

@ -12,6 +12,7 @@
"@metalsmith/markdown": "1.10.0", "@metalsmith/markdown": "1.10.0",
"@metalsmith/permalinks": "2.5.1", "@metalsmith/permalinks": "2.5.1",
"@metalsmith/postcss": "5.4.1", "@metalsmith/postcss": "5.4.1",
"@metalsmith/rss": "1.0.1",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
"highlight.js": "11.8.0", "highlight.js": "11.8.0",
"jstransformer-nunjucks": "1.2.0", "jstransformer-nunjucks": "1.2.0",
@ -190,6 +191,14 @@
"postcss": "^5.0.4 || ^6.0.0 || ^7.0.0 || ^8.0.0" "postcss": "^5.0.4 || ^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/@metalsmith/rss": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@metalsmith/rss/-/rss-1.0.1.tgz",
"integrity": "sha512-27untrvM6ltnFbeSajzSU5e+7zJ4EfUsyqWR8m64S9GXw1gQqKnZbDKMhPbmHv8AMXVqtGPCayhBcYjJWKj+hQ==",
"dependencies": {
"rss": "^1.2.2"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -897,6 +906,25 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/mime-db": {
"version": "1.25.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz",
"integrity": "sha512-5k547tI4Cy+Lddr/hdjNbBEWBwSl8EBc5aSdKvedav8DReADgWJzcYiktaRIw3GtGC1jjwldXtTzvqJZmtvC7w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.13",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz",
"integrity": "sha512-ryBDp1Z/6X90UvjUK3RksH0IBPM137T7cmg4OgD5wQBojlAiUwuok0QeELkim/72EtcYuNlmbkrcGuxj3Kl0YQ==",
"dependencies": {
"mime-db": "~1.25.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -1300,6 +1328,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/rss": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz",
"integrity": "sha512-xUhRTgslHeCBeHAqaWSbOYTydN2f0tAzNXvzh3stjz7QDhQMzdgHf3pfgNIngeytQflrFPfy6axHilTETr6gDg==",
"dependencies": {
"mime-types": "2.1.13",
"xml": "1.0.1"
}
},
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@ -1553,6 +1590,11 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}, },
"node_modules/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="
},
"node_modules/yaml": { "node_modules/yaml": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",

View file

@ -2,5 +2,3 @@
title: index title: index
layout: index.njk layout: index.njk
--- ---
asdf

View file

@ -4,6 +4,7 @@ date: 2019-08-01
pub: pub:
year: 2019 year: 2019
--- ---
# Bash powered flat-file blog # Bash powered flat-file blog
I've been meaning to put something on this space for a long time, yet I I've been meaning to put something on this space for a long time, yet I
@ -32,7 +33,7 @@ extensions to it.
It is Java however and since I've reduced the specs for this virtual It is Java however and since I've reduced the specs for this virtual
server (cost optimization) and tried to outsource some of the tasks it server (cost optimization) and tried to outsource some of the tasks it
previously did, so Java had to go. It was also a chore updating and previously did, so Java had to go. It was also a chore updating and
recompiling the thing. *cough* Made up excuses. *cough* recompiling the thing. _cough_ Made up excuses. _cough_
It was hard enough to find a Wiki software that used Git as a backing It was hard enough to find a Wiki software that used Git as a backing
storage system. Eventually I settled on [Gitit]() that gives me the storage system. Eventually I settled on [Gitit]() that gives me the

View file

@ -178,4 +178,3 @@ FOTNOT 4: denna text får fritt spridas och kopieras under förutsättning att n
inte blandar bönor i chilin. inte blandar bönor i chilin.
--- ---

View file

@ -4,6 +4,7 @@ date: 2020-06-01
pub: pub:
year: 2020 year: 2020
--- ---
# 2020 so far # 2020 so far
This has been a stranger year than most, even when not taking COVID-19 This has been a stranger year than most, even when not taking COVID-19

View file

@ -4,6 +4,7 @@ date: 2020-06-01
pub: pub:
year: 2020 year: 2020
--- ---
# The Key to success # The Key to success
Wisdom from my grandparents: Wisdom from my grandparents:

View file

@ -4,6 +4,7 @@ date: 2020-09-01
pub: pub:
year: 2020 year: 2020
--- ---
# Monitoring with IRC # Monitoring with IRC
I would like to keep an eye on my servers from the comfort of my IRC I would like to keep an eye on my servers from the comfort of my IRC

View file

@ -40,7 +40,8 @@ language", and not "nix the operating system"), is quite mature[^1]
across both Linux and MacOS, so I decided to brush off my "nix the across both Linux and MacOS, so I decided to brush off my "nix the
language" concerns and dive into it. Again. language" concerns and dive into it. Again.
[^1]: Well, `nix-env` and other `nix-*` commands _are_ mature, but the [^1]:
Well, `nix-env` and other `nix-*` commands _are_ mature, but the
next-gen all-in-one `nix` CLI is definitely _not_ mature. It's not next-gen all-in-one `nix` CLI is definitely _not_ mature. It's not
even final: even final:
https://nixos.org/manual/nix/stable/command-ref/experimental-commands https://nixos.org/manual/nix/stable/command-ref/experimental-commands
@ -54,10 +55,12 @@ language" concerns and dive into it. Again.
- Comfortable using a shell - Comfortable using a shell
- A high tolerance for obtuse languages[^3] - A high tolerance for obtuse languages[^3]
[^2]: Configuration guide: [^2]:
Configuration guide:
https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/ https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/
[^3]: We will be using a bare minimum of "nix the language" to [^3]:
We will be using a bare minimum of "nix the language" to
accomplish our goals, so there are better ways to do most things I accomplish our goals, so there are better ways to do most things I
do, but I don't understand any of them so I'm sticking to what I can do, but I don't understand any of them so I'm sticking to what I can
only describe as "simple but dumb". only describe as "simple but dumb".
@ -73,7 +76,8 @@ Nix is somewhat of an overloaded term, as it can mean:
- Nix the utilities - Nix the utilities
- Nix the store - Nix the store
[^4]: Most often called NixOS but searching for "nix" often winds up in [^4]:
Most often called NixOS but searching for "nix" often winds up in
NixOS-land which is not always helpful. NixOS-land which is not always helpful.
I will use a subset of those in this guide, and do my best to I will use a subset of those in this guide, and do my best to
@ -132,7 +136,8 @@ advanced functionality (often experimental) that the official installer
leaves off by default, and goes as far as to disable or dissuade users leaves off by default, and goes as far as to disable or dissuade users
from using the stable nix utilities. from using the stable nix utilities.
[^5]: A consultancy that provides services related to Nix training for [^5]:
A consultancy that provides services related to Nix training for
teams and companies. teams and companies.
It's a great installer, but if used, some configuration options must be It's a great installer, but if used, some configuration options must be
@ -245,7 +250,8 @@ versions of the same package can exist at the same time (at the cost of
disk space) without causing conflicts across the environment. There is disk space) without causing conflicts across the environment. There is
also a long version[^6]. Probably a longer version somewhere as well. also a long version[^6]. Probably a longer version somewhere as well.
[^6]: Understanding the Nix store by looking at how Nix works: [^6]:
Understanding the Nix store by looking at how Nix works:
https://nixos.org/guides/how-nix-works.html https://nixos.org/guides/how-nix-works.html
### Installing packages into the environment ### Installing packages into the environment

View file

@ -1,15 +1,521 @@
--- ---
title: varl's nixtapes vol.3 title: varl's nixtapes vol.3
date: 2023-08-15 date: 2023-08-29
pub: pub:
year: 2023 year: 2023
collection: nixtapes collection: nixtapes
--- ---
# Context > #### TL;DR
>
> Let us cut into the meat of Nix and finally take a look at how we can
> use what we tediously went through [vol.1](../varls-nixtape-vol-1) and
> [vol.2](../varls-nixtape-vol-2) for. Declarative and isolated
> development environments.
# Prerequisites # volume 3: declarative developer environments
- Declarative development environments ## Prerequisites
- Nested nix environments.
- Setting up WARP and `app` command - Again we need a working nix installation, see
[vol.1](../varls-nixtape-vol-1)
- We also need Nix integrated with the user environment, see
[vol.2](../varls-nixtape-vol-2)
## Context
Why do we want declarative development environments ?
To be fair, once an development environment has been set up, it mostly
chugs along just fine, and will do so for some time.
Eventually the developer needs to update something, for example NodeJS
to another version. She can choose to do so globally, forcing all the
apps she works on to run on the global version provided. That might not
be ideal, so there are tools that solve this problem by allowing her to
install multiple versions of Node and either set one for a specific
project, or manually switch between them.
There are many, many, tools that do this (to list a few):
- Polyglot: [asdf](https://asdf-vm.com/)
- NodeJS: [volta](https://github.com/volta-cli/volta),
[fnm](https://github.com/Schniz/fnm), [n](https://github.com/tj/n),
[nvm](https://github.com/nvm-sh/nvm)
- Python: [pyenv](https://github.com/pyenv/pyenv),
[venv](https://docs.python.org/3/library/venv.html)
- Rust: [r](https://github.com/javanile/r),
[rustup](https://rustup.rs/),
[zrsvm](https://github.com/zcorky/zrsvm),
[rsvm](https://github.com/sdepold/rsvm)
- Go: [gvm](https://github.com/moovweb/gvm),
[g](https://github.com/stefanmaric/g)
Nix however, can replace all of these. It circumvents the problem by
changing the paradigm. What if all versions can be installed
side-by-side, making it so that there is no truly global version at all
?
There may be a _default_ version, sure, but with declarative
environments we can switch between them automatically, or at will. And
once we leave the environment where we loaded the tool, it is removed,
so it only exists in the context we want it in.
By using a declarative style, we tell the system what state we want it
to be in, and the system moves to that state. And we can do that at any
granularity: coarse (all of our development), or fine (just a single
project).
On with the show.
## Our first development environment
Having an organised folder structure will have an immense impact on how
reusable how environments will be, and allow for tricks like nesting
developer environments. I expect most developers have a folder that
contains the projects that they work on, and for now that is perfectly
fine.
If you don't have something like that though, and dump all projects in
your `$HOME` ... May I suggest to create a `~/dev` folder, at least for
the purpose of this experiment.
Jump into `~/dev` and create a new file called `shell.nix`. This is
where we will declare what our environment should be:
Starting with some basics, let's add NodeJS 18, Python 3, Go 1.20, and
Java 17.
```
with (import <nixpkgs> {});
mkShell {
buildInputs = [
nodejs-18_x
python3
python3.pkgs.pip
go_1_20
maven
jdk17
];
shellHook = ''
'';
}
```
Save and exit the file, and type `nix-shell`. After a bit of
downloading and plumbing, you will be dropped into a shell environment
that may change your prompt:
```
~/dev% nix-shell
[nix-shell:~/dev]$
```
In the shell we can see that we indeed have an environment with the
tools we declared[^1]:
```
[nix-shell:~/dev]$ node --version
v18.17.1
[nix-shell:~/dev]$ go version
go version go1.20.7 linux/amd64
[nix-shell:~/dev]$ javac -version
javac 17.0.7
[nix-shell:~/dev]$ python3 --version
Python 3.10.12
```
[^1]:
We just started `nix-shell` in an impure state, meaning it merges
our existing environment with the created shell. If we had provided
the `--pure` switch, then no system tools would be visible within
the Nix shell. See [vol.1](../varls-nixtape-vol-1) for more on this.
We can also make sure we are in fact using the versions provided by Nix:
```
[nix-shell:~/dev]$ which python3
/nix/store/wlxpsdzfvdanfzh704qmgyzb42qvy4fr-python3-3.10.12/bin/python3
[nix-shell:~/dev]$ which node
/nix/store/5my366b5ws4mnmycnynrwiwmz93zj37b-nodejs-18.17.1/bin/node
[nix-shell:~/dev]$ which go
/nix/store/54qksp8q7lwwhpjyxqbhzvrmn883gkpy-go-1.20.7/bin/go
[nix-shell:~/dev]$ which javac
/nix/store/l4plrvlxi46fiy65sz75x7850wx0nhhw-openjdk-17.0.7+7/bin/javac
```
Exit the `nix-shell` environment, and poof. The tools are either gone or
reverted to whatever I had before:
```
~/dev% which javac
javac not found
```
That's a neat trick, but we don't want to type `nix-shell`, and we
absolutely don't want to change our prompt and/or drop into bash when we
are using zsh in our main environment. We _want_ some things to blend
between environments.
## Enter: `direnv` and `nix-direnv`
> - https://github.com/direnv/direnv
> - https://github.com/nix-community/nix-direnv
These two tools will do the heavy lifting for us. `direnv` should be
installed in our user environment that we set up in [vol.2](../varls-nixtape-vol-2).
So jumping back to `~/.config/nixpkgs/config.nix`, we can add it to our
`userBasePkgs`:
```diff
pkgs : {
allowUnfree = true;
packageOverrides = pkgs: with pkgs; rec {
nixUserProfile = writeText "nix-user-profile" ''
export PATH=$HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/sbin:/bin:/usr/sbin:/usr/bin
export NIX_PATH="nixpkgs=$HOME/.nix-defexpr"
'';
userBasePkgs = pkgs.buildEnv {
name = "user-base";
paths = [
(runCommand "profile" {} ''
mkdir -p $out/etc/profile.d
cp ${nixUserProfile} $out/etc/profile.d/nix-user-profile.sh
'')
+ direnv
];
pathsToLink = [
"/share"
"/bin"
"/etc"
];
extraOutputsToInstall = [ "man" "doc" ];
};
};
}
```
And we instruct Nix to set up our user environment with `nix-env -ir user-base`.
Now `direnv` should be available:
```
$ which direnv
/nix/store/h77a0hqm3jcfqq7fgs310rf5l9w9g66y-direnv-2.32.3/bin/direnv
```
Last installation step for `direnv` is to hook it into our environment.
In `.zshrc` or `.bashrc` add the line for your shell[^2]:
```
# for zsh
eval "$(direnv hook zsh)"
# for bash
eval "$(direnv hook bash)"
```
[^2]: For more shell integrations: https://github.com/direnv/direnv/blob/master/docs/hook.md
Now, create a `.envrc` in your `~/dev` folder, making it a sibling with
`shell.nix`:
```
~/dev% tree -L 1
.
├── shell.nix
└── .envrc
```
Add this content to `.envrc`:
```
use nix
```
Save and quit, and then run `direnv allow` in the `~/dev` folder to
allow `direnv` to load the file automatically.
It never runs any `.envrc` that isn't explicitly
allowed, and if the `.envrc` file changes, you need to manually allow it
again. This is a safeguard in case someone for example sneaks an `.envrc`
file into version control.
Upon running `direnv allow`, it will immediately source the `.envrc`
file and load the environment as specified. It will spit out something
like this:
```
~/dev% direnv allow
direnv: loading ~/dev/.envrc
direnv: using nix
direnv: export +AR +AS +CC +CONFIG_SHELL +CXX +DETERMINISTIC_BUILD
+HOST_PATH +IN_NIX_SHELL +JAVA_HOME +LD +NIX_BINTOOLS
+NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu
+NIX_BUILD_CORES +NIX_BUILD_TOP +NIX_CC
+NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_CFLAGS_COMPILE
+NIX_ENFORCE_NO_NATIVE +NIX_HARDENING_ENABLE +NIX_LDFLAGS +NIX_STORE +NM
+NODE_PATH +OBJCOPY +OBJDUMP +PYTHONHASHSEED +PYTHONNOUSERSITE
+PYTHONPATH +RANLIB +READELF +SIZE +SOURCE_DATE_EPOCH +STRINGS +STRIP
+TEMP +TEMPDIR +TMP +TMPDIR +XDG_DATA_DIRS +_PYTHON_HOST_PLATFORM
+_PYTHON_SYSCONFIGDATA_NAME +__structuredAttrs +buildInputs +buildPhase
+builder +cmakeFlags +configureFlags +depsBuildBuild
+depsBuildBuildPropagated +depsBuildTarget +depsBuildTargetPropagated
+depsHostHost +depsHostHostPropagated +depsTargetTarget
+depsTargetTargetPropagated +doCheck +doInstallCheck +mesonFlags +name
+nativeBuildInputs +out +outputs +patches +phases +preferLocalBuild
+propagatedBuildInputs +propagatedNativeBuildInputs +shell +shellHook
+stdenv +strictDeps +system ~PATH
~/dev%
```
If you have new packages, it can take some time for it to download them
and put them in the `/nix` store. Hang tight. Subsequent loads will be
faster, and through the power of caching, we can make them even faster.
Any changes to `shell.nix` will be picked up and trigger `direnv` to
load our changed environment.
At this point we could call it a day, but it's a bit too slow, so let's
add in an extension to `direnv` called `nix-direnv` that caches our
development environments, making them a lot faster to load, after the
cache is populated.
There are a few ways to install `nix-direnv`, either by adding it to our
user configuration[^3], or by using a directive in `.envrc` to load it
for us (also cached, so not done each time):
[^3]: This means the file: `~/.config/nixpkgs/config.nix`.
In `.envrc`:
```diff
+if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then
+ source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8="
+fi
use nix
```
It does a SHA validation to make sure that what it downloads matches
what we think it does, which is a reasonable thing to do when loading
scripts from the internet.
On saving and quitting our editor, `direnv` will angrily state:
```
direnv: error /home/varl/dev/.envrc is blocked. Run `direnv allow` to approve its content
```
After another `direnv allow`, it will use cached developer environments,
speeding up the time it takes to `cd` into a directory and setup the
environment according to `shell.nix` by a lot.
We can verify that by looking for lines like:
```diff
direnv: loading ~/dev/.envrc
direnv: loading https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc (sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=)
direnv: using nix
+direnv: nix-direnv: renewed cache
```
And:
```diff
direnv: loading ~/dev/.envrc
direnv: loading https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc (sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=)
direnv: using nix
+direnv: nix-direnv: using cached dev shell
```
## Shell hooks
In our `shell.nix` above, notice that there is a `shellHook`[^4] directive
that was empty. Let's see what we can use it for:
```diff
with (import <nixpkgs> {});
mkShell {
buildInputs = [
nodejs-18_x
python3
python3.pkgs.pip
go_1_20
maven
jdk17
];
+ shellHook = ''
+ '';
}
```
[^4]: https://nixos.org/manual/nix/stable/command-ref/nix-shell#description
`shellHook` is run _after_ the setup, so we can use it for
initialisation that is specific for our `nix-shell`. For example, we can
export environment variables to change things according to our context.
We can use a specific browser profile to open links, a few examples I
use is that we can change the `GOPATH` and `NODE_PATH`, or autoload Python
`venvs`.
```diff
with (import <nixpkgs> {});
mkShell {
buildInputs = [
nodejs-18_x
python3
python3.pkgs.pip
go_1_20
maven
jdk17
];
shellHook = ''
+ # setup node
+ mkdir -p .nix-node
+ export NODE_PATH=$PWD/.nix-node
+ export NPM_CONFIG_PREFIX=$PWD/.nix-node
+
+ # setup python
+ mkdir -p .nix-python/venvs
+ xport SOURCE_DATE_EPOCH="$(date +%s)"
+ export VIRTUAL_ENV_DISABLE_PROMPT=1
+ export PIPENV_VERBOSITY=-1
+
+ if [ -f $PWD/.nix-python/venv/bin/activate ]; then
+ source $PWD/.nix-python/venv/bin/activate
+ fi
+
+ # setup go
+ mkdir -p .nix-go/{bin,pkg,src,cache}
+ touch .nix-go/env
+ export GOPATH=$PWD/.nix-go
+ export GOCACHE=$PWD/.nix-go/cache
+ export GOENV=$PWD/.nix-go/env
'';
}
```
We could also do these things in `.envrc` (more or less), as both
`direnv` and `nix-shell` ensures that it also unloads any environment
variables we set up when we leave a directory, so they only exist in the
context we are in.
## Going a step further
While what we have now is perfectly useful, the `~/dev/shell.nix` approach is not
what I actually use.
My setup looks more like:
```
~/dev% tree -L 2
.
├── work
│   ├── apps
│ │ ├── .envrc
│ │ └── shell.nix
│ ├── .envrc
│   └── shell.nix
├── forks
│ ├── .envrc
│   └── shell.nix
├── personal
│ ├── .envrc
│   └── shell.nix
├── .envrc
└── shell.nix
```
With our current setup, each environment would fully overwrite the
previous (so no nesting of environments, yet).
## Nested developer environments
Luckily, there is a handy feature in `direnv` that allows us to solve
this by adding a single line to our `shell.nix` that we want to instruct
to inherit from the parent `shell.nix`.
If we want our `~/dev/work` environment to inherit from the parent
`~/dev`, we put the following in `~/dev/work/.envrc`:
```diff
if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8="
fi
+source_up
use nix
```
Now we can specify tools that are valid across all contexts in
`~/dev/shell.nix`:
```
with (import <nixpkgs> {});
mkShell {
buildInputs = [
yq
jq
gh
shellcheck
mr
tokei
];
shellHook = ''
# empty so far
'';
}
```
And get more specific, so for example for `~/dev/work/shell.nix`:
```
with (import <nixpkgs> {});
mkShell {
buildInputs = [
nodejs-18_x
python3
python3.pkgs.pip
python3.pkgs.pex
kubectl
kustomize
(google-cloud-sdk.withExtraComponents [ google-cloud-sdk.components.gke-gcloud-auth-plugin ])
go_1_20
cloudflared
glibcLocales
maven
jdk17
];
shellHook = ''
# ... truncated ...
'';
}
```
For specific projects we can use nesting as an override feature, so we
can swap `nodejs-18_x` for `nodejs-20_x` in specific projects, for
example.
I think that's about it for vol.3.
/v.