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
|
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
<body class="bg-white text-black dark:bg-black dark:text-white">
|
<body class="bg-white text-black dark:bg-black dark:text-white">
|
||||||
<section class="flow md:container md:mx-auto p-4">
|
<section class="flow md:container md:mx-auto p-4">
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<p>
|
<p>
|
||||||
<a href="/">{{ sitename }}</a>
|
<a href="/">{{ sitename }}</a>
|
||||||
</p>
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ contents | safe }}
|
{{ contents | safe }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -3,16 +3,17 @@
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{{ contents | safe }}
|
{{ contents | safe }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{% 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
|
||||||
|
|
|
@ -2,20 +2,23 @@
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
{{ contents | safe }}
|
{{ contents | safe }}
|
||||||
|
|
||||||
{% if previous %}
|
{% if previous or next %}
|
||||||
<p>
|
<hr class="{{ classes.hr }}"/>
|
||||||
Previous:
|
<div class="flex flex-row justify-around">
|
||||||
<a href="/{{ previous.path }}">{{ previous.title }}</a>
|
{% if previous %}
|
||||||
</p>
|
<p>
|
||||||
{% endif %}
|
Previous: <a href="/{{ previous.path }}">{{ previous.title }}</a>
|
||||||
|
</p>
|
||||||
|
{% 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 %}
|
||||||
|
|
|
@ -1,68 +1,68 @@
|
||||||
export const classes = {
|
export const classes = {
|
||||||
blockquote: 'flow px-4 border-l-8 border-purple-900',
|
blockquote: 'flow px-4 border-l-8 border-purple-900',
|
||||||
footnote: 'list-decimal flow',
|
footnote: 'list-decimal flow',
|
||||||
footnote_section: 'mx-8 my-4',
|
footnote_section: 'mx-8 my-4',
|
||||||
bullet_list: 'list-disc mx-4',
|
bullet_list: 'list-disc mx-4',
|
||||||
h1: 'text-6xl',
|
h1: 'text-6xl',
|
||||||
h2: 'text-4xl',
|
h2: 'text-4xl',
|
||||||
h3: 'text-2xl',
|
h3: 'text-2xl',
|
||||||
h4: 'text-xl',
|
h4: 'text-xl',
|
||||||
h5: 'text-lg',
|
h5: 'text-lg',
|
||||||
h6: 'text-base',
|
h6: 'text-base',
|
||||||
hljs: 'hljs p-4 overflow-x-scroll',
|
hljs: 'hljs p-4 overflow-x-scroll',
|
||||||
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) =>
|
||||||
const defaultRenderer = (name) => md.renderer.rules[name] || proxy;
|
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 defaultBlockquoteOpenRenderer = defaultRenderer('blockquote_open');
|
||||||
const defaultHeadingOpenRenderer = defaultRenderer('heading_open');
|
const defaultHeadingOpenRenderer = defaultRenderer('heading_open');
|
||||||
const defaultCodeOpenRenderer = defaultRenderer('code_inline');
|
const defaultCodeOpenRenderer = defaultRenderer('code_inline');
|
||||||
const defaultHrRenderer = defaultRenderer('hr');
|
const defaultHrRenderer = defaultRenderer('hr');
|
||||||
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);
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
;
|
|
||||||
|
|
|
@ -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,49 +41,56 @@ Metalsmith(__dirname)
|
||||||
generatorurl: 'https://metalsmith.io/',
|
generatorurl: 'https://metalsmith.io/',
|
||||||
classes,
|
classes,
|
||||||
})
|
})
|
||||||
.use(drafts({
|
.use(
|
||||||
default: false,
|
drafts({
|
||||||
include: false,
|
default: false,
|
||||||
}))
|
include: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
.use(
|
.use(
|
||||||
markdown({
|
markdown({
|
||||||
render: function (source, options, context) {
|
render: function (source, options, context) {
|
||||||
if (!md) {
|
if (!md) {
|
||||||
md = new MarkdownIt(options);
|
md = new MarkdownIt(options);
|
||||||
md.use(MarkdownItFootnote);
|
md.use(MarkdownItFootnote);
|
||||||
md.renderer.rules = {
|
md.renderer.rules = {
|
||||||
...md.renderer.rules,
|
...md.renderer.rules,
|
||||||
...rules(md),
|
...rules(md),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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: {
|
||||||
linkify: true,
|
linkify: true,
|
||||||
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>${
|
||||||
} catch (__) {}
|
hljs.highlight(str, { language: lang, ignoreIllegals: true })
|
||||||
}
|
.value
|
||||||
|
}</code></pre>`;
|
||||||
return `<pre class="${classes.hljs}"><code>${md.utils.escapeHtml(str)}</code></pre>`;
|
} catch (__) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return `<pre class="${classes.hljs}"><code>${md.utils.escapeHtml(
|
||||||
|
str,
|
||||||
|
)}</code></pre>`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.use(
|
.use(
|
||||||
collections({
|
collections({
|
||||||
posts: {
|
posts: {
|
||||||
pattern: 'posts/**/*.html',
|
pattern: 'posts/**/*.html',
|
||||||
sortBy: 'date',
|
sortBy: 'date',
|
||||||
reverse: false,
|
reverse: false,
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.use(
|
.use(
|
||||||
|
@ -92,18 +99,18 @@ Metalsmith(__dirname)
|
||||||
date: 'YYYY',
|
date: 'YYYY',
|
||||||
relative: false,
|
relative: false,
|
||||||
slug: {
|
slug: {
|
||||||
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
42
package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -2,5 +2,3 @@
|
||||||
title: index
|
title: index
|
||||||
layout: index.njk
|
layout: index.njk
|
||||||
---
|
---
|
||||||
|
|
||||||
asdf
|
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
title: Bash powered flat-file blog
|
title: Bash powered flat-file blog
|
||||||
date: 2019-08-01
|
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
|
||||||
|
@ -21,7 +22,7 @@ Multiple times: [1](https://github.com/varl/blorgh),
|
||||||
[4](https://github.com/varl/galgen). _Spine_ actually works, and is
|
[4](https://github.com/varl/galgen). _Spine_ actually works, and is
|
||||||
kinda neat. It's what powered this site four years ago, and I've used it
|
kinda neat. It's what powered this site four years ago, and I've used it
|
||||||
to deploy static sites for clients. _Blorgh_ was meant to replace
|
to deploy static sites for clients. _Blorgh_ was meant to replace
|
||||||
_Spine_ ... You know how it goes. In the meantime my [podcast
|
_Spine_ ... You know how it goes. In the meantime my [podcast
|
||||||
downloader](https://github.com/varl/pyphoid) broke and I decided to
|
downloader](https://github.com/varl/pyphoid) broke and I decided to
|
||||||
rewrite that in a new language (Go) as an exercise in learning a new
|
rewrite that in a new language (Go) as an exercise in learning a new
|
||||||
language. Obviously that has priority over trying to fix the parser to
|
language. Obviously that has priority over trying to fix the parser to
|
||||||
|
@ -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
|
||||||
|
@ -333,7 +334,7 @@ template.innerHTML = `
|
||||||
color: #181818;
|
color: #181818;
|
||||||
background-color: #aeaeae;
|
background-color: #aeaeae;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
@ -441,7 +442,7 @@ JavaScript client: [blog/2020/06](https://vlv.io/blog/2020/06/)
|
||||||
|
|
||||||
> Couldn't you have used SSI to accomplish the exact same behaviour?
|
> Couldn't you have used SSI to accomplish the exact same behaviour?
|
||||||
|
|
||||||
Yes.
|
Yes.
|
||||||
|
|
||||||
> ES Modules? Custom elements? ShadowRoot? Templates & slots? History
|
> ES Modules? Custom elements? ShadowRoot? Templates & slots? History
|
||||||
> API? All that for _this_ tiny little thing?
|
> API? All that for _this_ tiny little thing?
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: World's best chili
|
title: World's best chili
|
||||||
date: 2019-11-01
|
date: 2019-11-01
|
||||||
pub:
|
pub:
|
||||||
year: 2019
|
year: 2019
|
||||||
---
|
---
|
||||||
|
|
||||||
_(at some point i might translate this amazing Chili recipe)_
|
_(at some point i might translate this amazing Chili recipe)_
|
||||||
|
@ -53,14 +53,14 @@ Det här är vad du behöver:
|
||||||
- En helflaska tequila
|
- En helflaska tequila
|
||||||
- Sex burkar ljust öl, helst det mexikanska Corona.
|
- Sex burkar ljust öl, helst det mexikanska Corona.
|
||||||
- 5 torkade ancho-pepparfrukter (De är stora, mörkt brunröda och finns i
|
- 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
|
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
|
blandning av mörkrött chilipulver och ett antal flådda, urkärnade röda
|
||||||
paprikor.)
|
paprikor.)
|
||||||
- 1 chipotle-peppar (Röd, rökt. Finns konserverad i latinamerikanska affärer.)
|
- 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
|
- 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
|
- 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.
|
- Baconfett (eller olja) att steka i.
|
||||||
- 10 stora vitlöksklyftor, grovt hackade.
|
- 10 stora vitlöksklyftor, grovt hackade.
|
||||||
- 5 kilo oxkött, skuret i centimeterstora tärningar.
|
- 5 kilo oxkött, skuret i centimeterstora tärningar.
|
||||||
|
@ -68,7 +68,7 @@ välsorterade livsmedelsaffärer. Ta den starka varianten.)
|
||||||
- En kopp chilipulver.
|
- En kopp chilipulver.
|
||||||
- Två koppar mörk oxbuljong.
|
- Två koppar mörk oxbuljong.
|
||||||
- 2 matskedar spiskummin. (Kännarna kan inte komma överens om kryddan ska
|
- 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 oregano.
|
||||||
- 2 matskedar malda korianderfrön.
|
- 2 matskedar malda korianderfrön.
|
||||||
- 1/2 matsked socker.
|
- 1/2 matsked socker.
|
||||||
|
@ -78,47 +78,47 @@ rostas innan den används eller inte.)
|
||||||
Så här gör du:
|
Så här gör du:
|
||||||
|
|
||||||
1. Ta dig en rejäl tequila. En platta med gamla Hank Williams-låtar bidrar
|
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
|
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å
|
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
|
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
|
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.)
|
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
|
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. 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
|
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
|
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.
|
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. 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,
|
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
|
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
|
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
|
skapelse och belönar sig själv med ytterligare en tequila, raskt åtföljd av det
|
||||||
resterande ölet.
|
resterande ölet.
|
||||||
|
|
||||||
1. Låt chilin småkoka. Rör ner spiskummin, oregano och koriander. Rör ofta så
|
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
|
1. Fortsätt kokningen tills köttet börjar falla sönder. Det bör ta två, tre
|
||||||
timmar.
|
timmar.
|
||||||
|
|
||||||
1. Tequila!!!
|
1. Tequila!!!
|
||||||
|
|
||||||
1. Det kan hända att chilin är lite lös när den närmar sig slutkokningen.
|
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).
|
1. Gör slut på den sista skvätten tequila (om du inte redan gjort det).
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ sallad, bröd, guacamole (avocadoröra) och stora mängder ljust öl till.
|
||||||
|
|
||||||
Musiken är nästan lika viktig. Satsa på någon genuint: Hank Williams, Buddy
|
Musiken är nästan lika viktig. Satsa på någon genuint: Hank Williams, Buddy
|
||||||
Holly, Joe Ely, Jerry Jeff Walker, Flaco Jiminez, Butch Hancock, Commander Cody
|
Holly, Joe Ely, Jerry Jeff Walker, Flaco Jiminez, Butch Hancock, Commander Cody
|
||||||
and his Lost Planet Airmen eller Gram Parsons. Fram mot natten passar det
|
and his Lost Planet Airmen eller Gram Parsons. Fram mot natten passar det
|
||||||
utmärkt att spela Freddy Fenders odödliga "Before the next teardrops falls",
|
utmärkt att spela Freddy Fenders odödliga "Before the next teardrops falls",
|
||||||
Doug Sahms "Wasted days and wasted nights" eller Creedende Clearwater Revivals
|
Doug Sahms "Wasted days and wasted nights" eller Creedende Clearwater Revivals
|
||||||
"Lodi".
|
"Lodi".
|
||||||
|
@ -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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: Tao Te Ching
|
title: Tao Te Ching
|
||||||
date: 2019-08-30
|
date: 2019-08-30
|
||||||
pub:
|
pub:
|
||||||
year: 2019
|
year: 2019
|
||||||
---
|
---
|
||||||
|
|
||||||
# Tao Te Ching ([Stephen Mitchell](http://taoteching.org.uk/index.php?c=30&a=Stephen+Mitchell))
|
# Tao Te Ching ([Stephen Mitchell](http://taoteching.org.uk/index.php?c=30&a=Stephen+Mitchell))
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: We did a thing
|
title: We did a thing
|
||||||
date: 2019-07-22
|
date: 2019-07-22
|
||||||
pub:
|
pub:
|
||||||
year: 2019
|
year: 2019
|
||||||
---
|
---
|
||||||
|
|
||||||
![Welcome to the world, little one.](/assets/P7240102.jpg)
|
![Welcome to the world, little one.](/assets/P7240102.jpg)
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
title: 2020 so far
|
title: 2020 so far
|
||||||
date: 2020-06-01
|
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
|
||||||
|
@ -17,15 +18,15 @@ Shoulder dislocations aplenty; when moving appliances, hastily grabbing
|
||||||
a coffee cup before our boy did, when stretching out my chest, when
|
a coffee cup before our boy did, when stretching out my chest, when
|
||||||
sleeping with my hand behind my head.
|
sleeping with my hand behind my head.
|
||||||
|
|
||||||
Funeral attendance over Zoom. I had to put my distrust of Zoom aside for
|
Funeral attendance over Zoom. I had to put my distrust of Zoom aside for
|
||||||
that, not my hill to die on. Most of our combined families are in Sweden
|
that, not my hill to die on. Most of our combined families are in Sweden
|
||||||
whereas we ourselves are in Norway. The lockdown has made the 600 km
|
whereas we ourselves are in Norway. The lockdown has made the 600 km
|
||||||
divide between us accute.
|
divide between us accute.
|
||||||
|
|
||||||
This is a personal memo to remember these 6 months, and that freedom to
|
This is a personal memo to remember these 6 months, and that freedom to
|
||||||
travel is a privilege and not a right. Throughout my life borders have
|
travel is a privilege and not a right. Throughout my life borders have
|
||||||
become looser and easier to traverse, and it has been an assumption of
|
become looser and easier to traverse, and it has been an assumption of
|
||||||
mine that it would continue in that direction. One virus is all it took
|
mine that it would continue in that direction. One virus is all it took
|
||||||
to fundamentally challenge that.
|
to fundamentally challenge that.
|
||||||
|
|
||||||
Half the year to go, I wonder what lasting changes we'll see.
|
Half the year to go, I wonder what lasting changes we'll see.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: Keep up the good work
|
title: Keep up the good work
|
||||||
date: 2020-06-30
|
date: 2020-06-30
|
||||||
pub:
|
pub:
|
||||||
year: 2020
|
year: 2020
|
||||||
---
|
---
|
||||||
|
|
||||||
"Keep up the good work", he said, fully intending it as words of
|
"Keep up the good work", he said, fully intending it as words of
|
||||||
|
@ -10,6 +10,6 @@ acknowledgement of the quality of work being done, and as encouragement
|
||||||
to carry on doing good work.
|
to carry on doing good work.
|
||||||
|
|
||||||
What is the implication though? That good things will come if you do
|
What is the implication though? That good things will come if you do
|
||||||
good work? Promotion? Money? Status? Are those good things?
|
good work? Promotion? Money? Status? Are those good things?
|
||||||
|
|
||||||
As if.
|
As if.
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
title: The Key to Success
|
title: The Key to Success
|
||||||
date: 2020-06-01
|
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:
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
title: Monitoring with IRC
|
title: Monitoring with IRC
|
||||||
date: 2020-09-01
|
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
|
||||||
|
@ -44,7 +45,7 @@ When the server "cc" successfully backups using rclone to Google Photos:
|
||||||
21:40 < cc> [OK] rclone backed up /data/media/pictures to photos:album/backups
|
21:40 < cc> [OK] rclone backed up /data/media/pictures to photos:album/backups
|
||||||
```
|
```
|
||||||
|
|
||||||
A failure message looks like:
|
A failure message looks like:
|
||||||
|
|
||||||
```
|
```
|
||||||
21:56 < cc> [ERR] rclone failed to backup /data/media/pictures to photos:album/backups:
|
21:56 < cc> [ERR] rclone failed to backup /data/media/pictures to photos:album/backups:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: varl's nixtapes vol.1
|
title: varl's nixtapes vol.1
|
||||||
date: 2023-08-08
|
date: 2023-08-08
|
||||||
pub:
|
pub:
|
||||||
year: 2023
|
year: 2023
|
||||||
collection: nixtapes
|
collection: nixtapes
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -48,16 +49,18 @@ language" concerns and dive into it. Again.
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- A supported OS:
|
- A supported OS:
|
||||||
- Linux
|
- Linux
|
||||||
- MacOS
|
- MacOS
|
||||||
- Windows with WSL2 with `systemd` enabled[^2]
|
- Windows with WSL2 with `systemd` enabled[^2]
|
||||||
- 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
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: varl's nixtapes vol.2
|
title: varl's nixtapes vol.2
|
||||||
date: 2023-08-09
|
date: 2023-08-09
|
||||||
pub:
|
pub:
|
||||||
year: 2023
|
year: 2023
|
||||||
collection: nixtapes
|
collection: nixtapes
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -5,7 +5,7 @@ keywords:
|
||||||
- bar
|
- bar
|
||||||
draft: true
|
draft: true
|
||||||
pub:
|
pub:
|
||||||
year: 2023
|
year: 2023
|
||||||
---
|
---
|
||||||
|
|
||||||
asdf
|
asdf
|
||||||
|
|
Loading…
Reference in a new issue