chore: big dump
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
This commit is contained in:
parent
12173463ab
commit
9f7a77273f
19
.gitea/workflows/build.yaml
Normal file
19
.gitea/workflows/build.yaml
Normal 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 }}."
|
|
@ -1,5 +1,3 @@
|
|||
# blog
|
||||
|
||||
[![Build Status](https://ci.meatbag.se/api/badges/varl/blog/status.svg)](https://ci.meatbag.se/varl/blog)
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,14 @@
|
|||
|
||||
@layer base {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
{% block main %}
|
||||
|
||||
<p>
|
||||
{{ contents | safe }}
|
||||
{{ contents | safe }}
|
||||
</p>
|
||||
|
||||
{% for date, posts in collections.posts | groupby('pub.year') | dictsort | reverse %}
|
||||
<h2 class="{{classes.h2}}">{{ date }}</h2>
|
||||
<hr class="{{ classes.hr }}"/>
|
||||
|
||||
{% for post in posts %}
|
||||
<div>
|
||||
<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>
|
||||
{% else %}
|
||||
No posts
|
||||
|
|
|
@ -2,20 +2,23 @@
|
|||
|
||||
{% block main %}
|
||||
|
||||
{{ contents | safe }}
|
||||
{{ contents | safe }}
|
||||
|
||||
{% if previous %}
|
||||
<p>
|
||||
Previous:
|
||||
<a href="/{{ previous.path }}">{{ previous.title }}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if previous or next %}
|
||||
<hr class="{{ classes.hr }}"/>
|
||||
<div class="flex flex-row justify-around">
|
||||
{% if previous %}
|
||||
<p>
|
||||
Previous: <a href="/{{ previous.path }}">{{ previous.title }}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if next %}
|
||||
<p>
|
||||
Next:
|
||||
<a href="/{{ next.path }}">{{ next.title }}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if next %}
|
||||
<p>
|
||||
Next: <a href="/{{ next.path }}">{{ next.title }}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,13 +13,15 @@ export const classes = {
|
|||
hr: 'border-red-900',
|
||||
code: 'text-slate-400',
|
||||
image: 'grayscale',
|
||||
}
|
||||
post_date: 'text-base italic',
|
||||
};
|
||||
|
||||
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 defaultBulletListOpenRenderer = defaultRenderer('bullet_list_open')
|
||||
const defaultBulletListOpenRenderer = defaultRenderer('bullet_list_open');
|
||||
const defaultBlockquoteOpenRenderer = defaultRenderer('blockquote_open');
|
||||
const defaultHeadingOpenRenderer = defaultRenderer('heading_open');
|
||||
const defaultCodeOpenRenderer = defaultRenderer('code_inline');
|
||||
|
@ -27,42 +29,40 @@ export const rules = (md) => {
|
|||
const defaultImageRenderer = defaultRenderer('image');
|
||||
|
||||
return {
|
||||
footnote_block_open: () => (
|
||||
footnote_block_open: () =>
|
||||
`<hr class="${classes.hr}">\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) => {
|
||||
tokens[idx].attrJoin("class", classes.bullet_list);
|
||||
tokens[idx].attrJoin('class', classes.bullet_list);
|
||||
|
||||
return defaultBulletListOpenRenderer(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);
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@ import { rules, classes } from './lib/tailwind-renderer.js';
|
|||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const t1 = performance.now();
|
||||
|
||||
let md
|
||||
let md;
|
||||
|
||||
Metalsmith(__dirname)
|
||||
.source('./src')
|
||||
|
@ -41,10 +41,12 @@ Metalsmith(__dirname)
|
|||
generatorurl: 'https://metalsmith.io/',
|
||||
classes,
|
||||
})
|
||||
.use(drafts({
|
||||
.use(
|
||||
drafts({
|
||||
default: false,
|
||||
include: false,
|
||||
}))
|
||||
}),
|
||||
)
|
||||
.use(
|
||||
markdown({
|
||||
render: function (source, options, context) {
|
||||
|
@ -58,9 +60,9 @@ Metalsmith(__dirname)
|
|||
}
|
||||
|
||||
if (context.key == 'contents') {
|
||||
return md.render(source)
|
||||
return md.render(source);
|
||||
} else {
|
||||
return md.renderInline(source)
|
||||
return md.renderInline(source);
|
||||
}
|
||||
},
|
||||
engineOptions: {
|
||||
|
@ -68,12 +70,17 @@ Metalsmith(__dirname)
|
|||
highlight: function (str, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
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 (__) {}
|
||||
}
|
||||
|
||||
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',
|
||||
sortBy: 'date',
|
||||
reverse: false,
|
||||
}
|
||||
},
|
||||
}),
|
||||
)
|
||||
.use(
|
||||
|
@ -95,15 +102,15 @@ Metalsmith(__dirname)
|
|||
remove: /[^a-z0-9- ]+/gi,
|
||||
lower: true,
|
||||
extend: {
|
||||
"'": '-'
|
||||
}
|
||||
"'": '-',
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
.use(
|
||||
inplace({
|
||||
transform: 'nunjucks'
|
||||
})
|
||||
transform: 'nunjucks',
|
||||
}),
|
||||
)
|
||||
.use(
|
||||
layouts({
|
||||
|
|
42
package-lock.json
generated
42
package-lock.json
generated
|
@ -12,6 +12,7 @@
|
|||
"@metalsmith/markdown": "1.10.0",
|
||||
"@metalsmith/permalinks": "2.5.1",
|
||||
"@metalsmith/postcss": "5.4.1",
|
||||
"@metalsmith/rss": "1.0.1",
|
||||
"autoprefixer": "10.4.14",
|
||||
"highlight.js": "11.8.0",
|
||||
"jstransformer-nunjucks": "1.2.0",
|
||||
|
@ -190,6 +191,14 @@
|
|||
"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": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
@ -897,6 +906,25 @@
|
|||
"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": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
@ -1300,6 +1328,15 @@
|
|||
"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": {
|
||||
"version": "1.2.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
|
||||
|
|
|
@ -2,5 +2,3 @@
|
|||
title: index
|
||||
layout: index.njk
|
||||
---
|
||||
|
||||
asdf
|
||||
|
|
|
@ -4,6 +4,7 @@ date: 2019-08-01
|
|||
pub:
|
||||
year: 2019
|
||||
---
|
||||
|
||||
# Bash powered flat-file blog
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
storage system. Eventually I settled on [Gitit]() that gives me the
|
||||
|
|
|
@ -53,14 +53,14 @@ Det här är vad du behöver:
|
|||
- En helflaska tequila
|
||||
- Sex burkar ljust öl, helst det mexikanska Corona.
|
||||
- 5 torkade ancho-pepparfrukter (De är stora, mörkt brunröda och finns i
|
||||
affärer som säljer latinamerikansk mat. I nödfall kan de ersättas med en
|
||||
blandning av mörkrött chilipulver och ett antal flådda, urkärnade röda
|
||||
paprikor.)
|
||||
affärer som säljer latinamerikansk mat. I nödfall kan de ersättas med en
|
||||
blandning av mörkrött chilipulver och ett antal flådda, urkärnade röda
|
||||
paprikor.)
|
||||
- 1 chipotle-peppar (Röd, rökt. Finns konserverad i latinamerikanska affärer.)
|
||||
- 3 birdseye-pepparfrukter (Små spetsiga klarröda. Kan ersättas av torkad, mald
|
||||
piri-piri).
|
||||
piri-piri).
|
||||
- 4 jalapeno-pepparfrukter (Knubbiga, gröna. Finns på burk i de flesta
|
||||
välsorterade livsmedelsaffärer. Ta den starka varianten.)
|
||||
välsorterade livsmedelsaffärer. Ta den starka varianten.)
|
||||
- Baconfett (eller olja) att steka i.
|
||||
- 10 stora vitlöksklyftor, grovt hackade.
|
||||
- 5 kilo oxkött, skuret i centimeterstora tärningar.
|
||||
|
@ -68,7 +68,7 @@ välsorterade livsmedelsaffärer. Ta den starka varianten.)
|
|||
- En kopp chilipulver.
|
||||
- Två koppar mörk oxbuljong.
|
||||
- 2 matskedar spiskummin. (Kännarna kan inte komma överens om kryddan ska
|
||||
rostas innan den används eller inte.)
|
||||
rostas innan den används eller inte.)
|
||||
- 2 matskedar oregano.
|
||||
- 2 matskedar malda korianderfrön.
|
||||
- 1/2 matsked socker.
|
||||
|
@ -78,47 +78,47 @@ rostas innan den används eller inte.)
|
|||
Så här gör du:
|
||||
|
||||
1. Ta dig en rejäl tequila. En platta med gamla Hank Williams-låtar bidrar
|
||||
också till så att det rätta chili-perspektivet på tillvaron infinner sig.
|
||||
också till så att det rätta chili-perspektivet på tillvaron infinner sig.
|
||||
|
||||
1. Börja sedan med pepparn. Rensa bort stjälkar och frön. Koka den torkade
|
||||
pepparn 15 minuter under lock, ställ åt sidan och låtsvalna.
|
||||
pepparn 15 minuter under lock, ställ åt sidan och låtsvalna.
|
||||
|
||||
1. Rensa och hacka den övriga pepparn. Ställ åt sidan. (Här är en varning på
|
||||
plats. Peppar är starkt. Den BRÄNNS. Se upp för ångorna när du kokar och tvätta
|
||||
händerna noga efteråt. Och tänk noga på vad då gör med fingrarna det närmaste
|
||||
dygnet. Om du petar dig i näsan kan du lika gärna göra det med lödkolven.)
|
||||
plats. Peppar är starkt. Den BRÄNNS. Se upp för ångorna när du kokar och tvätta
|
||||
händerna noga efteråt. Och tänk noga på vad då gör med fingrarna det närmaste
|
||||
dygnet. Om du petar dig i näsan kan du lika gärna göra det med lödkolven.)
|
||||
|
||||
1. Ta ett glas tequila till, ett rejält glas. Det kommer att behövas. Nu börjar
|
||||
det nämligen dra ihop sig.
|
||||
det nämligen dra ihop sig.
|
||||
|
||||
1. Fräs vitlöken mjuk och brun. Lägg i grytan.
|
||||
|
||||
1. Öka värmen i stekpannan och börja stek köttet. Ta lite i sänder och rör om
|
||||
ordentligt så att bitarna steks på alla sidor. Lägg ner i grytan. Detta är ett
|
||||
varmt, osigt och tidsödande slitgöra som kräver både tålamod och tequila.
|
||||
ordentligt så att bitarna steks på alla sidor. Lägg ner i grytan. Detta är ett
|
||||
varmt, osigt och tidsödande slitgöra som kräver både tålamod och tequila.
|
||||
|
||||
1. Blanda mjöl och chilipulver. Strö över köttet i grytan.
|
||||
|
||||
1. Sila av den blötlagda pepparn, men spara vattnet. Mosa den kokta pepparn,
|
||||
tillsätt sedan all peppar till köttet.
|
||||
tillsätt sedan all peppar till köttet.
|
||||
|
||||
1. Häll på pepparvattnet, oxbuljongen och öl tills vätskan täcker köttet. Koka
|
||||
upp.
|
||||
upp.
|
||||
|
||||
1. Nu är chilin på väg. En nöjd kock kan ta ett steg tillbaka, beundra sin
|
||||
skapelse och belönar sig själv med ytterligare en tequila, raskt åtföljd av det
|
||||
resterande ölet.
|
||||
skapelse och belönar sig själv med ytterligare en tequila, raskt åtföljd av det
|
||||
resterande ölet.
|
||||
|
||||
1. Låt chilin småkoka. Rör ner spiskummin, oregano och koriander. Rör ofta så
|
||||
att mästerverket inte bränns fast i botten.
|
||||
att mästerverket inte bränns fast i botten.
|
||||
|
||||
1. Fortsätt kokningen tills köttet börjar falla sönder. Det bör ta två, tre
|
||||
timmar.
|
||||
timmar.
|
||||
|
||||
1. Tequila!!!
|
||||
|
||||
1. Det kan hända att chilin är lite lös när den närmar sig slutkokningen.
|
||||
Riktiga Texasbor reder den då med grovt majsmjöl.
|
||||
Riktiga Texasbor reder den då med grovt majsmjöl.
|
||||
|
||||
1. Gör slut på den sista skvätten tequila (om du inte redan gjort det).
|
||||
|
||||
|
@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ date: 2020-06-01
|
|||
pub:
|
||||
year: 2020
|
||||
---
|
||||
|
||||
# 2020 so far
|
||||
|
||||
This has been a stranger year than most, even when not taking COVID-19
|
||||
|
|
|
@ -4,6 +4,7 @@ date: 2020-06-01
|
|||
pub:
|
||||
year: 2020
|
||||
---
|
||||
|
||||
# The Key to success
|
||||
|
||||
Wisdom from my grandparents:
|
||||
|
|
|
@ -4,6 +4,7 @@ date: 2020-09-01
|
|||
pub:
|
||||
year: 2020
|
||||
---
|
||||
|
||||
# Monitoring with IRC
|
||||
|
||||
I would like to keep an eye on my servers from the comfort of my IRC
|
||||
|
|
|
@ -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
|
||||
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
|
||||
even final:
|
||||
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
|
||||
- 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/
|
||||
|
||||
[^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
|
||||
do, but I don't understand any of them so I'm sticking to what I can
|
||||
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 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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
### Installing packages into the environment
|
||||
|
|
|
@ -1,15 +1,521 @@
|
|||
---
|
||||
title: varl's nixtapes vol.3
|
||||
date: 2023-08-15
|
||||
date: 2023-08-29
|
||||
pub:
|
||||
year: 2023
|
||||
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
|
||||
- Nested nix environments.
|
||||
- Setting up WARP and `app` command
|
||||
## Prerequisites
|
||||
|
||||
- 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.
|
||||
|
|
Loading…
Reference in a new issue