<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://gohugo.io/" version="0.155.3">Hugo</generator><title type="html">Sebastian Hoß</title><link href="https://seb.xn--ho-hia.de/" rel="alternate" type="text/html" title="html"/><link href="https://seb.xn--ho-hia.de/index.xml" rel="alternate" type="application/rss+xml" title="rss"/><link href="https://seb.xn--ho-hia.de/atom.xml" rel="self" type="application/atom+xml" title="atom"/><link href="https://seb.xn--ho-hia.de/humans.txt" rel="alternate" type="text/plain" title="humans"/><link href="https://seb.xn--ho-hia.de/foaf.rdf" rel="alternate" type="application/rdf+xml" title="foaf"/><link href="https://seb.xn--ho-hia.de/.well-known/security.txt" rel="alternate" type="text/plain" title="security"/><updated>2026-02-16T04:12:58+00:00</updated><id>https://seb.xn--ho-hia.de/</id><entry><title type="html">jspecify</title><link href="https://seb.xn--ho-hia.de/posts/jspecify/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/clojure-java-interoperability/?utm_source=atom_feed" rel="related" type="text/html" title="Clojure Java Interoperability"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Java version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/jspecify/</id><published>2023-01-09T00:00:00+00:00</published><updated>2023-01-09T07:05:35+01:00</updated><content type="html"><![CDATA[<p>Every <a href="https://www.java.com/">Java</a> developer has probably encountered a <code>NullPointerException</code> at least once in their life. The exception is thrown every time you try to dereference and use some object before initializing it. The following snippet shows a simple example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">String</span><span class="w"> </span><span class="n">someName</span><span class="p">;</span><span class="w">         </span><span class="c1">// value is &#39;null&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">someName</span><span class="p">.</span><span class="na">toUpperCase</span><span class="p">();</span><span class="w"> </span><span class="c1">// throws NullPointerException</span><span class="w">
</span></span></span></code></pre></div><p>Modern <a href="https://en.wikipedia.org/wiki/Integrated_development_environment">IDEs</a> have some sort of detection for this kind of problem and warn developers while they are writing code like this. Those IDEs typically rely on static code analysis to determine if a value is <code>null</code> and therefore a potential for a <code>NullPointerException</code> is present in your code. To improve the result of such an analysis, annotations can be placed on your code which signal that a parameter can or can not be <code>null</code>. Multiple approaches have existed in the past to define a standard set of annotations for such a task, however none of them succeeded.</p>
<p><a href="https://jspecify.dev/">jspecify</a> is the latest approach that tries to establish a <a href="https://xkcd.com/927/">standard</a>. It has gained wide <a href="https://jspecify.dev/about">community support</a> and recently celebrated their first public release (<code>0.3.0</code>).</p>
<p>The following snippet shows the dependency declaration for <a href="https://maven.apache.org/">Maven</a> projects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;dependencies&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;dependency&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;groupId&gt;</span>org.jspecify<span class="nt">&lt;/groupId&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;artifactId&gt;</span>jspecify<span class="nt">&lt;/artifactId&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;version&gt;</span>0.3.0<span class="nt">&lt;/version&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/dependency&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependencies&gt;</span>
</span></span></code></pre></div><p>In case you want to declare that nothing in your module can ever be <code>null</code>, place the <code>@NullMarked</code> on your <code>module-info.java</code> like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@org.jspecify.annotations.NullMarked</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">module</span><span class="w"> </span><span class="n">your</span><span class="p">.</span><span class="na">module</span><span class="p">.</span><span class="na">here</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">requires</span><span class="w"> </span><span class="n">org</span><span class="p">.</span><span class="na">jspecify</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// ...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The tooling support is not quiet clear yet, however if you are developing a library there is <a href="https://github.com/jspecify/jspecify/wiki/adoption#should-my-library-adopt-these-annotations-now">no harm</a> in adding these annotations now and let your users enjoy their null-free life once tools have caught up.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/java" term="java" label="java"/><category scheme="https://seb.xn--ho-hia.de/tags/nullness" term="nullness" label="nullness"/><category scheme="https://seb.xn--ho-hia.de/tags/jspecify" term="jspecify" label="jspecify"/></entry><entry><title type="html">Using chezmoi with age</title><link href="https://seb.xn--ho-hia.de/posts/chezmoi-age/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-gpg/?utm_source=atom_feed" rel="related" type="text/html" title="Encrypt dotfiles with chezmoi"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-update/?utm_source=atom_feed" rel="related" type="text/html" title="chezmoi auto-update"/><link href="https://seb.xn--ho-hia.de/posts/shell-init/?utm_source=atom_feed" rel="related" type="text/html" title="chezmoi &amp; shell init scripts"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-maintenance/?utm_source=atom_feed" rel="related" type="text/html" title="Maintaining dotfiles with chezmoi"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="related" type="text/html" title="Manage dotfiles with chezmoi and git"/><id>https://seb.xn--ho-hia.de/posts/chezmoi-age/</id><published>2023-01-08T00:00:00+00:00</published><updated>2023-01-08T09:25:44+01:00</updated><content type="html"><![CDATA[<p><a href="https://age-encryption.org/">age</a> is another tool supported by <a href="https://www.chezmoi.io/">chezmoi</a> to <a href="https://www.chezmoi.io/docs/how-to/#keep-data-private">keep data private</a>. Compared to <code>gpg</code> it is much simpler by focusing on the encryption parts only.</p>
<p>Add the following snippet to your <code>.chezmoi.toml</code> to configure <code>chezmoi</code> to use <code>age</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">encryption</span> <span class="p">=</span> <span class="s2">&#34;age&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">age</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">identity</span> <span class="p">=</span> <span class="s2">&#34;path/to/age/private-key&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">recipient</span> <span class="p">=</span> <span class="s2">&#34;age...public...key...&#34;</span>
</span></span></code></pre></div><p>Adding files to your <code>chezmoi</code> source directory remains the same as compared to using <code>gpg</code> - just call <code>chezmoi add --encrypt path/to/file</code>.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/dotfiles" term="dotfiles" label="dotfiles"/><category scheme="https://seb.xn--ho-hia.de/tags/chezmoi" term="chezmoi" label="chezmoi"/><category scheme="https://seb.xn--ho-hia.de/tags/age" term="age" label="age"/><category scheme="https://seb.xn--ho-hia.de/tags/encryption" term="encryption" label="encryption"/></entry><entry><title type="html">Multiple Git Configurations</title><link href="https://seb.xn--ho-hia.de/posts/multiple-git-configs/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/nvim-plugin-auto-updates/?utm_source=atom_feed" rel="related" type="text/html" title="Automatically update plugins for vim/nvim"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="related" type="text/html" title="Manage dotfiles with chezmoi and git"/><link href="https://seb.xn--ho-hia.de/posts/git-push-only-mirror/?utm_source=atom_feed" rel="related" type="text/html" title="Push-only mirrors for Git Repositories"/><link href="https://seb.xn--ho-hia.de/posts/git-mirror/?utm_source=atom_feed" rel="related" type="text/html" title="Mirror Git Repositories"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><id>https://seb.xn--ho-hia.de/posts/multiple-git-configs/</id><published>2023-01-05T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>To split yet re-use as much configuration for Git as possible, you can create one root configuration that looks similar to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl">  <span class="na">name</span> <span class="o">=</span> <span class="s">Your Name Here</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[includeIf &#34;gitdir:~/git/personal/&#34;]</span>
</span></span><span class="line"><span class="cl">  <span class="na">path</span> <span class="o">=</span> <span class="s">~/.config/git/personal</span>
</span></span><span class="line"><span class="cl"><span class="k">[includeIf &#34;gitdir:~/git/work/&#34;]</span>
</span></span><span class="line"><span class="cl">  <span class="na">path</span> <span class="o">=</span> <span class="s">~/.config/git/work</span>
</span></span></code></pre></div><p>The <a href="https://git-scm.com/docs/git-config#_includes">includeIf</a> directive supports multiple keywords. In my case, work and personal projects have a different root directory, therefore I can filter based on the location using <code>gitdir</code>. The personal Git configuration simply looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl">  <span class="na">email</span> <span class="o">=</span> <span class="s">personal.email@example.com</span>
</span></span></code></pre></div><p>and the work related configuration like this using a different email address:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[user]</span>
</span></span><span class="line"><span class="cl">  <span class="na">email</span> <span class="o">=</span> <span class="s">first.last@work.example</span>
</span></span></code></pre></div><p>Additional settings that are different for personal/work accounts can be split the same way, for example to use a different signing key for work.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/git" term="git" label="git"/><category scheme="https://seb.xn--ho-hia.de/tags/config" term="config" label="config"/></entry><entry><title type="html">passage fuzzy search</title><link href="https://seb.xn--ho-hia.de/posts/passage-fuzzy-search/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/passage-fuzzy-search/</id><published>2022-12-27T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To fuzzy search through passwords managed with <a href="https://github.com/FiloSottile/passage">passage</a>, I&rsquo;ve written the following script that is inspired by the upstream version which is using <code>fzf</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">fd --type<span class="o">=</span>file --base-directory<span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">PASSAGE_DIR</span><span class="k">:-</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="p">/.passage/store</span><span class="si">}</span><span class="s2">&#34;</span> .age --exec <span class="nb">echo</span> <span class="s1">&#39;{.}&#39;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  sk --cycle --layout<span class="o">=</span>reverse --tiebreak<span class="o">=</span>score --no-multi <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  xargs --replace --max-args<span class="o">=</span><span class="m">1</span> --no-run-if-empty <span class="se">\
</span></span></span><span class="line"><span class="cl">    passage show --clip<span class="o">=</span><span class="m">1</span> <span class="o">{}</span>
</span></span></code></pre></div><p>This version requires <a href="https://github.com/sharkdp/fd/">fd</a>, <a href="https://github.com/lotabout/skim">skim</a>, <a href="https://www.gnu.org/software/findutils/manual/html_node/find_html/Invoking-xargs.html">xargs</a>, and <a href="https://github.com/FiloSottile/passage">passage</a> itself of course. The detailed breakdown on how it works is as follows:</p>
<ol>
<li>Use <code>fd</code> to find all files within <code>${PASSAGE_DIR}</code> that end in <code>.age</code>. Each password in passage is inside that folder and has such a file extensions, therefore we are selecting every password we have.</li>
<li>Using both <code>--base-directory</code> and <code>--exec echo '{.}'</code> ensures that passwords are returned in such form that they can be passed back into <code>passage</code> again. The placeholder <code>'{.}'</code> is a feature provided by <code>fd</code> which strips the file extension from each returned value.</li>
<li>All passwords are then passed into <code>sk</code> to allow to fuzzy search across them all. Setting <code>--no-multi</code> ensures that only a single password can be selected.</li>
<li>Finally, <code>xargs</code> calls <code>passage</code> and replaces the curly braces with the selected password. Thanks to <code>--clip=1</code>, the first line in the selected password entry will be copied to the clipboard and automatically cleared after 45 seconds.</li>
</ol>
<p>To call that script, I&rsquo;ve saved it as <code>passage-fuzzy-search.sh</code> in my <code>.local/bin</code> folder and added some checks into it to verify that every required software is actually installed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env zsh
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">###############################################################################</span>
</span></span><span class="line"><span class="cl"><span class="c1"># This shell script presents passwords saved with passage through skim</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Call it like this:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   passage-fuzzy-search.sh</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Required software that isn&#39;t in GNU coreutils:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   - &#39;passage&#39; to read passwords</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   - &#39;fd&#39; to find passwords</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   - &#39;sk&#39; to filter passwords</span>
</span></span><span class="line"><span class="cl"><span class="c1">###############################################################################</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! <span class="o">((</span> <span class="si">${</span><span class="p">+commands[passage]</span><span class="si">}</span> <span class="o">))</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s1">&#39;passage not installed. Please install passage.&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! <span class="o">((</span> <span class="si">${</span><span class="p">+commands[sk]</span><span class="si">}</span> <span class="o">))</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s1">&#39;sk not installed. Please install skim.&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! <span class="o">((</span> <span class="si">${</span><span class="p">+commands[fd]</span><span class="si">}</span> <span class="o">))</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s1">&#39;fd not installed. Please install fd-find.&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">fd --type<span class="o">=</span>file --base-directory<span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">PASSAGE_DIR</span><span class="k">:-</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="p">/.passage/store</span><span class="si">}</span><span class="s2">&#34;</span> .age --exec <span class="nb">echo</span> <span class="s1">&#39;{.}&#39;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  sk --cycle --layout<span class="o">=</span>reverse --tiebreak<span class="o">=</span>score --no-multi <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  xargs --replace --max-args<span class="o">=</span><span class="m">1</span> --no-run-if-empty <span class="se">\
</span></span></span><span class="line"><span class="cl">    passage show --clip<span class="o">=</span><span class="m">1</span> <span class="o">{}</span>
</span></span></code></pre></div><p>Since typing <code>passage-fuzzy-search.sh</code> is way too long, I have added an alias like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">pp</span><span class="o">=</span><span class="s1">&#39;passage-fuzzy-search.sh&#39;</span>
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/passage" term="passage" label="passage"/><category scheme="https://seb.xn--ho-hia.de/tags/age" term="age" label="age"/><category scheme="https://seb.xn--ho-hia.de/tags/fuzzy" term="fuzzy" label="fuzzy"/><category scheme="https://seb.xn--ho-hia.de/tags/search" term="search" label="search"/><category scheme="https://seb.xn--ho-hia.de/tags/clipboard" term="clipboard" label="clipboard"/></entry><entry><title type="html">chezmoi auto-update</title><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-update/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/shell-init/?utm_source=atom_feed" rel="related" type="text/html" title="chezmoi &amp; shell init scripts"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-maintenance/?utm_source=atom_feed" rel="related" type="text/html" title="Maintaining dotfiles with chezmoi"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-gpg/?utm_source=atom_feed" rel="related" type="text/html" title="Encrypt dotfiles with chezmoi"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="related" type="text/html" title="Manage dotfiles with chezmoi and git"/><link href="https://seb.xn--ho-hia.de/posts/xdg-dot-files/?utm_source=atom_feed" rel="related" type="text/html" title="XDG Base Directory Specification"/><id>https://seb.xn--ho-hia.de/posts/chezmoi-auto-update/</id><published>2022-12-26T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>To automatically synchronize dotfiles across my computers, I&rsquo;ve written the following <code>systemd</code> unit:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-systemd" data-lang="systemd"><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Update chezmoi managed dotfiles</span>
</span></span><span class="line"><span class="cl"><span class="na">After</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl"><span class="na">Wants</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">oneshot</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/chezmoi update --no-tty --force</span>
</span></span><span class="line"><span class="cl"><span class="na">RemainAfterExit</span><span class="o">=</span><span class="s">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">default.target</span>
</span></span></code></pre></div><p>This unit pulls changes from upstream first and then applies the changes to the current computer after I&rsquo;m logged in and a network connection is available. The <code>--no-tty</code> flag is required because there is no tty when systemd executes <code>chezmoi</code>. Likewise, the <code>--force</code> flag ensures that no interactive prompt will be displayed which we cannot answer since <code>systemd</code> is executing this unit without us being involved.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/chezmoi" term="chezmoi" label="chezmoi"/><category scheme="https://seb.xn--ho-hia.de/tags/dotfiles" term="dotfiles" label="dotfiles"/><category scheme="https://seb.xn--ho-hia.de/tags/automation" term="automation" label="automation"/></entry><entry><title type="html">chezmoi &amp; shell init scripts</title><link href="https://seb.xn--ho-hia.de/posts/shell-init/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-maintenance/?utm_source=atom_feed" rel="related" type="text/html" title="Maintaining dotfiles with chezmoi"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-gpg/?utm_source=atom_feed" rel="related" type="text/html" title="Encrypt dotfiles with chezmoi"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="related" type="text/html" title="Manage dotfiles with chezmoi and git"/><link href="https://seb.xn--ho-hia.de/posts/nvim-plugin-auto-updates/?utm_source=atom_feed" rel="related" type="text/html" title="Automatically update plugins for vim/nvim"/><link href="https://seb.xn--ho-hia.de/posts/xdg-dot-files/?utm_source=atom_feed" rel="related" type="text/html" title="XDG Base Directory Specification"/><id>https://seb.xn--ho-hia.de/posts/shell-init/</id><published>2022-12-12T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>Many CLI applications offer initialization scripts to integrate into a shell, for example <code>starship init zsh</code> or <code>zoxide init zsh</code>. The documentation of these tools usually tell you to put something like <code>eval &quot;$(starship init zsh)&quot;</code> into your shell RC file. While this approach works fine, it does decrease startup speed of your shell because it needs to run the <code>init</code> command every time you open a new shell. Given that you open shells much more often than new versions of these tools are released and installed, you can cache the output of these commands to get a bit of speed back.</p>
<p><a href="https://chezmoi.io/">chezmoi</a> provides a template function called <a href="https://www.chezmoi.io/reference/templates/functions/output/">output</a> which replaces itself with the output of the command you specified. You can use that function this to integrate various tools into your shell as the following example shows while using <code>zsh</code>:</p>
<ol>
<li>Create a directory that holds all init scripts for every tool you want to use.
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"> $ mkdir --parents <span class="s2">&#34;</span><span class="si">${</span><span class="nv">ZDOTDIR</span><span class="si">}</span><span class="s2">&#34;</span>/tools.d
</span></span></code></pre></div></li>
<li>Let your shell load all available scripts in that directory. This snippet should be part of your <code>.zshrc</code> file:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="k">for</span> init_script in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">ZDOTDIR</span><span class="si">}</span><span class="s2">&#34;</span>/tools.d/*.sh<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">source</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">init_script</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span></code></pre></div></li>
<li>Create chezmoi <code>.tmpl</code> files for each tool and place them in the chezmoi source directory that matches the directory you created in step 1:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-text-template" data-lang="go-text-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="nx">output</span><span class="w"> </span><span class="s">&#34;starship&#34;</span><span class="w"> </span><span class="s">&#34;init&#34;</span><span class="w"> </span><span class="s">&#34;zsh&#34;</span><span class="w"> </span><span class="s">&#34;--print-full-init&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span></code></pre></div></li>
<li>Call <code>chezmoi apply</code> to generate the init scripts.</li>
</ol>
<p>The only downside here is that you have to re-run <code>chezmoi apply</code> after updating one of the tools because they change their init scripts sometimes. That problem can be solved with <a href="../chezmoi-auto-update">chezmoi auto-updates</a>.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/chezmoi" term="chezmoi" label="chezmoi"/><category scheme="https://seb.xn--ho-hia.de/tags/dotfiles" term="dotfiles" label="dotfiles"/><category scheme="https://seb.xn--ho-hia.de/tags/shell" term="shell" label="shell"/><category scheme="https://seb.xn--ho-hia.de/tags/performance" term="performance" label="performance"/></entry><entry><title type="html">awsenv</title><link href="https://seb.xn--ho-hia.de/posts/awsenv/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/awsenv/</id><published>2022-02-14T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To quickly log into and switch between AWS accounts in a terminal, I wrote the following script. It sets the <code>AWS_PROFILE</code> environment variable which is used by many tools that interact with the AWS API, like <code>awscli</code> or <code>terraform</code>. My current environment uses AzureAD as a single-sign-on provider, therefore this script uses <code>aws sso login</code> to perform an MFA login into AWS. The AWS profiles must be set up in such a way that <code>aws configure list-profiles</code> can detect them, which is typically done by adding them in <code>${AWS_CONFIG_FILE:-$HOME/.aws/config}</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env sh
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">###############################################################################</span>
</span></span><span class="line"><span class="cl"><span class="c1"># This script performs an AWS SSO login for the user-selected AWS profile</span>
</span></span><span class="line"><span class="cl"><span class="c1"># and sets the AWS_PROFILE environment variable afterwards. To use</span>
</span></span><span class="line"><span class="cl"><span class="c1"># this, create an alias that sources this script like this:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1">#     alias awsenv=&#39;source path/to/this/script.sh&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Required software that is not in GNU coreutils:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   - &#39;aws&#39; to list profiles &amp; get current caller identity</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   - &#39;fzf&#39; to list all available AWS profiles</span>
</span></span><span class="line"><span class="cl"><span class="c1">###############################################################################</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># prompt user to select one AWS profile</span>
</span></span><span class="line"><span class="cl"><span class="nv">profile</span><span class="o">=</span><span class="k">$(</span>aws configure list-profiles <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  fzf --cycle --layout<span class="o">=</span>reverse --tiebreak<span class="o">=</span>index<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># user can cancel switching profiles by pressing ESC</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">profile</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># check is access token exists and is valid for selected profile</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> ! aws --profile <span class="s2">&#34;</span><span class="si">${</span><span class="nv">profile</span><span class="si">}</span><span class="s2">&#34;</span> sts get-caller-identity &gt;/dev/null 2&gt;<span class="p">&amp;</span>1<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># perform login into profile in case access token is invalid</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! aws sso login --profile <span class="s2">&#34;</span><span class="si">${</span><span class="nv">profile</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># short circuit in case login failed</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># AWS_PROFILE is used by many AWS-related tools</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Setting AWS_PROFILE to [</span><span class="si">${</span><span class="nv">profile</span><span class="si">}</span><span class="s2">]&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">export</span> <span class="nv">AWS_PROFILE</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">profile</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># do not expose internal variables</span>
</span></span><span class="line"><span class="cl">  <span class="nb">unset</span> profile
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/aws" term="aws" label="aws"/><category scheme="https://seb.xn--ho-hia.de/tags/azure" term="azure" label="azure"/><category scheme="https://seb.xn--ho-hia.de/tags/fzf" term="fzf" label="fzf"/></entry><entry><title type="html">Clojure Java Interoperability</title><link href="https://seb.xn--ho-hia.de/posts/clojure-java-interoperability/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Java version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/clojure-java-interoperability/</id><published>2022-01-29T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>Clojure has several <a href="https://clojure.org/java_interop">forms and macros</a> to call Java code. However, calling Clojure code from Java is not always so straightforward. The following post shows the different options currently available.</p>
<h2 id="using-gen-class">Using <code>gen-class</code></h2>
<p>Clojure code can be <a href="https://clojure.org/compilation">compiled</a> to standard JVM bytecode using <a href="https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/gen-class">gen-class</a>.</p>
<h3 id="adding-static-modifiers">Adding static modifiers</h3>
<p>Clojure imposes the concept of immutability. As such Clojure functions are/should be void of any state or side effects and only operate on the given input. Therefore, exporting Clojure functions as static Java methods makes sense. The following example defines a Clojure function, a corresponding Java-callable function and exports the Java function as a static method in the class <code>com.example.Computation</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">ns </span><span class="nv">com.example.computation</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="ss">:gen-class</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:name</span> <span class="nv">com.example.Computation</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:methods</span> <span class="p">[</span><span class="o">#^</span><span class="p">{</span><span class="ss">:static</span> <span class="nv">true</span><span class="p">}</span> <span class="p">[</span><span class="nv">incrementRange</span> <span class="p">[</span><span class="nv">int</span><span class="p">]</span> <span class="nv">java.util.List</span><span class="p">]]))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">increment-range</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Creates a sequence of numbers up to max and then increments them.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nv">max</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">map inc </span><span class="p">(</span><span class="nb">take max </span><span class="p">(</span><span class="nf">range</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">-incrementRange</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;A Java-callable wrapper around the &#39;increment-range&#39; function.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nv">max</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">increment-range</span> <span class="nv">max</span><span class="p">))</span>
</span></span></code></pre></div><p>The Java wrapper has to follow the <a href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.8">standard rules</a> for method names. Therefore <code>increment-range</code> has to be renamed to <code>incrementRange</code> (or some similar name without the &ldquo;-&rdquo; in it). The &ldquo;-&rdquo; prefix for the Java wrapper can be configured inside the <code>:gen-class</code> form and will be removed once <code>gen-class</code> runs. The usage from Java looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">com.example</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">ClojureJavaInteropStatic</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">List</span><span class="w"> </span><span class="n">incrementedRange</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Computation</span><span class="p">.</span><span class="na">incrementRange</span><span class="p">(</span><span class="n">10</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="adding-generics">Adding generics</h3>
<p>The returned list in the above code is raw because the method definition doesn&rsquo;t use generics. To solve this problem declare that the generated class <code>:implements</code> a certain interface that exposes the desired method definition(s). You won&rsquo;t be able to declare your methods as static anymore, but get a generified method for all your Java needs.</p>
<p>The Java interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">com.example</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">RangeIncrementer</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">incrementRange</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">max</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The changed Clojure namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">ns </span><span class="nv">com.example.computation</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="ss">:gen-class</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:name</span> <span class="nv">com.example.Computation</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:implements</span> <span class="p">[</span><span class="nv">com.example.RangeIncrementer</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">increment-range</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Creates a sequence of numbers up to max and then increments them.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nv">max</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">map inc </span><span class="p">(</span><span class="nb">take max </span><span class="p">(</span><span class="nf">range</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">-incrementRange</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;A Java-callable wrapper around the &#39;increment-range&#39; function.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nv">this</span> <span class="nv">max</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">increment-range</span> <span class="nv">max</span><span class="p">))</span>
</span></span></code></pre></div><p>Finally, the generified usage from Java:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">com.example</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">ClojureJavaInteropGenerics</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">RangeIncrementer</span><span class="w"> </span><span class="n">incrementer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Computation</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="n">incrementedRange</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">incrementer</span><span class="p">.</span><span class="na">incrementRange</span><span class="p">(</span><span class="n">10</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Couple of notes for this as well: First the generated class still only returns the raw type (<code>List</code> instead of  <code>List&lt;Integer&gt;</code>). So instead of using the class, use the interface for the variable declaration (<code>RangeIncrementer incrementer = ..</code> instead of <code>Computation comp = ..</code>). The interface will return the non-raw <code>List</code>. Second the function definition for <code>-incrementRange</code> is now slightly different. It needs an additional parameter (<code>this</code>) which exposes the current instance to the generated class/method.</p>
<p>Returning an array of something is also possible with the following construct <code>&quot;[Ljava.lang.Object;&quot;</code>. Need a 2-dim array? Just use <code>&quot;[[Ljava.lang.Object;&quot;</code> (notice the extra <code>[</code>) and so on. However, be aware that the method return types have to match, for example you can&rsquo;t specify a return type of array if your Clojure function does not return an array. In the example above the call to <code>map</code> returns <code>LazySeq</code> which itself is a <code>java.util.List</code>. Therefore, the method declaration is valid, and you won&rsquo;t get any <code>ClassCastException</code> when calling <code>incrementRange</code> from Java.</p>
<h3 id="make-your-life-easier-with-macros">Make your life easier with macros</h3>
<p>Instead of defining every Clojure function which should be exported twice (the real function + the Java wrapper), it is possible to use a macro to do that extra work automatically.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">&#39;</span><span class="p">[</span><span class="nv">clojure.string</span> <span class="ss">:as</span> <span class="nv">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">camel-case</span> <span class="p">[</span><span class="nv">input</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">words</span> <span class="p">(</span><span class="nf">string/split</span> <span class="nv">input</span> <span class="o">#</span><span class="s">&#34;[\s_-]+&#34;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">string/join</span> <span class="p">(</span><span class="nb">cons </span><span class="p">(</span><span class="nf">string/lower-case</span> <span class="p">(</span><span class="nb">first </span><span class="nv">words</span><span class="p">))</span> <span class="p">(</span><span class="nb">map </span><span class="nv">string/capitalize</span> <span class="p">(</span><span class="nb">rest </span><span class="nv">words</span><span class="p">))))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">java-name</span> <span class="p">[</span><span class="nv">clojure-name</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">symbol </span><span class="p">(</span><span class="nb">str </span><span class="s">&#34;-&#34;</span> <span class="p">(</span><span class="nf">camel-case</span> <span class="p">(</span><span class="nb">str </span><span class="nv">clojure-name</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defmacro </span><span class="nv">defn*</span> <span class="p">[</span><span class="nb">name </span><span class="o">&amp;</span> <span class="nv">declarations</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">java-name</span> <span class="p">(</span><span class="nf">java-name</span> <span class="nv">name</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="o">`</span><span class="p">(</span><span class="k">do </span><span class="p">(</span><span class="kd">defn </span><span class="o">~</span><span class="nb">name </span><span class="o">~</span><span class="nv">declarations</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">       <span class="p">(</span><span class="kd">defn </span><span class="o">~</span><span class="nv">java-name</span> <span class="o">~</span><span class="nv">declarations</span><span class="p">))))</span>
</span></span></code></pre></div><p>The macro <code>defn*</code> replaces <code>defn</code> and automatically creates a second function with a valid camel-cased Java method name. The macro is available as a <a href="https://github.com/sebhoss/def-clj">small library</a> at <a href="https://search.maven.org/search?q=g:com.github.sebhoss%20a:def-clj">Maven Central</a>. The macro won&rsquo;t add the extra parameter mentioned above to Java wrapper, so it is only useful for declaring static methods.</p>
<h2 id="using-the-clojure-runtime">Using the Clojure Runtime</h2>
<p>Using <code>gen-class</code> imposes certain limitations on calling Clojure code from Java. One of those are functions which make use of Clojure <a href="https://clojure.org/reference/special_forms#binding-forms">parameter destructuring</a>. To invoke those functions you have to use the Clojure runtime.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// The Clojure &#39;require&#39; function from the &#39;clojure.core&#39; namespace.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Var</span><span class="w"> </span><span class="n">require</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RT</span><span class="p">.</span><span class="na">var</span><span class="p">(</span><span class="s">&#34;clojure.core&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;require&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Your namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Symbol</span><span class="w"> </span><span class="n">namespace</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Symbol</span><span class="p">.</span><span class="na">intern</span><span class="p">(</span><span class="s">&#34;DESIRED.NAMESPACE.HERE&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Your function</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Var</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RT</span><span class="p">.</span><span class="na">var</span><span class="p">(</span><span class="s">&#34;DESIRED.NAMESPACE.HERE&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;DESIRED-FUNCTION&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// The required keyword for the above function</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Keyword</span><span class="w"> </span><span class="n">keyword</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Keyword</span><span class="p">.</span><span class="na">intern</span><span class="p">(</span><span class="s">&#34;REQUIRED-KEYWORD&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Require/Import your namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">require</span><span class="p">.</span><span class="na">invoke</span><span class="p">(</span><span class="n">namespace</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Invoke your function with the given keyword and its value</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Object</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">function</span><span class="p">.</span><span class="na">invoke</span><span class="p">(</span><span class="n">keyword</span><span class="p">,</span><span class="w"> </span><span class="n">VALUE</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>The desired namespace has to be on the classpath for this to work. Alternatively it is possible to load an entire Clojure script, as shown in the following example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">RT</span><span class="p">.</span><span class="na">loadResourceScript</span><span class="p">(</span><span class="s">&#34;DESIRED/NAMESPACE/HERE.clj&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">RT</span><span class="p">.</span><span class="na">var</span><span class="p">(</span><span class="s">&#34;DESIRED.NAMESPACE.HERE&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;DESIRED-FUNCTION&#34;</span><span class="p">).</span><span class="na">invoke</span><span class="p">(</span><span class="n">PARAMETER</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>On a big project it is properly wise to move Java-&gt;Clojure interop code into helper classes/methods. Look <a href="https://github.com/mikera/clojure-utils/blob/master/src/main/java/mikera/cljutils/Clojure.java">here</a> for an example.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/clojure" term="clojure" label="clojure"/><category scheme="https://seb.xn--ho-hia.de/tags/java" term="java" label="java"/><category scheme="https://seb.xn--ho-hia.de/tags/interoperability" term="interoperability" label="interoperability"/></entry><entry><title type="html">Declarative conditional rendering in React</title><link href="https://seb.xn--ho-hia.de/posts/declarative-conditional-rendering-in-react/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/declarative-conditional-rendering-in-react/</id><published>2022-01-15T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>One feature that often surprises people while teaching them <a href="https://reactjs.org/">React</a> is that a component does not have to render anything. It seems trivial at first, however it quickly shows that a render-nothing components can reduce boilerplate code and improve code-reuse.</p>
<p>In its simplest (shortest) form a render-nothing component looks like the following snippet. It does not actually do anything and is not particularly helpful for anything. You could add it to every other component in your application without breaking or influencing anything.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">RendersNothing</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">&lt;&gt;&lt;/&gt;</span>
</span></span></code></pre></div><p>Now consider the following example, that adds some <code>if-then-else</code> logic to the same component:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">MightRenderSomething</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">someCondition</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span><span class="nx">hello</span> <span class="nx">world</span><span class="o">!</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">&lt;&gt;&lt;/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This component encapsulates the <code>if-then-else</code> logic of conditionally rendering a hello world message. Instead of cluttering your entire app with the same logic, you can now simply re-use that same component that contains this <code>if</code> condition. To see the full power of this technique, consider the following example. At first, we are going to define a hook that reads the current window width, then define components that conditionally render based on the current window width, and finally use those components in an example application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">useWindowWidth</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">width</span><span class="p">,</span> <span class="nx">setWidth</span><span class="p">]</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">React</span><span class="p">.</span><span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">handleResize</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">setWidth</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">innerWidth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;resize&#34;</span><span class="p">,</span> <span class="nx">handleResize</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nb">window</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">(</span><span class="s2">&#34;resize&#34;</span><span class="p">,</span> <span class="nx">handleResize</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">width</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The following components use that hook to implement UI breakpoints for small (mobile) and large (desktop) screens. Note that the value <code>768</code> is just an example - replace it with whatever your design system tells you to.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ForMobileDevicesOnly</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">windowWidth</span> <span class="o">=</span> <span class="nx">useWindowWidth</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">windowWidth</span> <span class="o">&lt;</span> <span class="mi">768</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">&lt;&gt;{</span><span class="nx">props</span><span class="p">.</span><span class="nx">children</span><span class="p">}&lt;/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">&lt;&gt;&lt;/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ForDesktopDevicesOnly</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">windowWidth</span> <span class="o">=</span> <span class="nx">useWindowWidth</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">windowWidth</span> <span class="o">&gt;=</span> <span class="mi">768</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">&lt;&gt;{</span><span class="nx">props</span><span class="p">.</span><span class="nx">children</span><span class="p">}&lt;/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">&lt;&gt;&lt;/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Both of these components simply render nothing when the window width does not have an appropriate size. If the window width does have the right size, they render their <code>children</code>. We can use those components in our application like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SomeActualComponent</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="nx">common</span> <span class="nx">headline</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">ForMobileDevicesOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span><span class="nx">only</span> <span class="nx">visible</span> <span class="nx">on</span> <span class="nx">mobile</span> <span class="nx">devices</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">ForMobileDevicesOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">ForDesktopDevicesOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span><span class="nx">only</span> <span class="nx">visible</span> <span class="nx">on</span> <span class="nx">desktop</span> <span class="nx">devices</span><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">ForDesktopDevicesOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>The above code snippet declares that some part of the UI can only be seen by mobile users, while others can only be seen by desktop users. Parts of the UI that are shared amongst all users are not wrapped by any of the components defined above.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/frontend" term="frontend" label="frontend"/><category scheme="https://seb.xn--ho-hia.de/tags/react" term="react" label="react"/><category scheme="https://seb.xn--ho-hia.de/tags/rendering" term="rendering" label="rendering"/><category scheme="https://seb.xn--ho-hia.de/tags/breakpoints" term="breakpoints" label="breakpoints"/></entry><entry><title type="html">Proper hostnames in your local network</title><link href="https://seb.xn--ho-hia.de/posts/home-network-hostnames/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/home-network-hostnames/</id><published>2022-01-08T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>Thanks to <a href="https://www.rfc-editor.org/rfc/rfc8375.html">RFC 8375</a>, we now have a proper domain to use for all our local devices. Simply move everything underneath <code>.home.arpa</code> to join the fun. In case you have <code>hostnamectl</code> available on your system run the following command to change the hostname of a device:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">#</span> <span class="nb">set</span> hostname
</span></span><span class="line"><span class="cl"><span class="gp">$</span> hostnamectl hostname some-device.home.arpa
</span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="gp">#</span> check hostname
</span></span><span class="line"><span class="cl"><span class="gp">$</span> hostnamectl status
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/network" term="network" label="network"/><category scheme="https://seb.xn--ho-hia.de/tags/hostnames" term="hostnames" label="hostnames"/><category scheme="https://seb.xn--ho-hia.de/tags/rfc" term="rfc" label="rfc"/></entry><entry><title type="html">Automatically update plugins for vim/nvim</title><link href="https://seb.xn--ho-hia.de/posts/nvim-plugin-auto-updates/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="related" type="text/html" title="Manage dotfiles with chezmoi and git"/><link href="https://seb.xn--ho-hia.de/posts/emacs-systemd/?utm_source=atom_feed" rel="related" type="text/html" title="emacs and systemd"/><link href="https://seb.xn--ho-hia.de/posts/git-push-only-mirror/?utm_source=atom_feed" rel="related" type="text/html" title="Push-only mirrors for Git Repositories"/><link href="https://seb.xn--ho-hia.de/posts/git-mirror/?utm_source=atom_feed" rel="related" type="text/html" title="Mirror Git Repositories"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><id>https://seb.xn--ho-hia.de/posts/nvim-plugin-auto-updates/</id><published>2022-01-01T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>Both <a href="https://www.vim.org/">Vim</a> and <a href="https://neovim.io/">Neovim</a> have a <a href="https://vimhelp.org/repeat.txt.html#packages">built-in</a> <a href="https://neovim.io/doc/user/usr_05.html#plugin">plugin mechanism</a> that loads plugins from <code>~/.vim/pack/*/{start,opt}/*</code> (Vim) or <code>~/.local/share/nvim/site/pack/*/{start,opt}/*</code> (Neovim). All you have to do to install new plugins, is to <code>git clone</code> their repository into those directories. To automatically update those clones, create the following script:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env zsh
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">###############################################################################</span>
</span></span><span class="line"><span class="cl"><span class="c1"># This script git-pulls all installed nvim plugins which are using the built-in</span>
</span></span><span class="line"><span class="cl"><span class="c1"># nvim plugin manager. Those plugins are located in .local/share/nvim/site/pack</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Required software that is not in GNU coreutils:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   - &#39;git&#39; to fetch plugin updates from upstream</span>
</span></span><span class="line"><span class="cl"><span class="c1">###############################################################################</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">### User specific variables, adjust to your own needs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># folder that contains all nvim plugins</span>
</span></span><span class="line"><span class="cl"><span class="nv">PLUGIN_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">XDG_DATA_HOME</span><span class="k">:-</span><span class="nv">$HOME</span><span class="p">/.local/share</span><span class="si">}</span><span class="s2">/nvim/site/pack&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">### Script logic</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;updating all plugins in </span><span class="si">${</span><span class="nv">PLUGIN_DIR</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># iterate through all directories and git pull em</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> directory in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PLUGIN_DIR</span><span class="si">}</span><span class="s2">&#34;</span>/*/<span class="o">{</span>start,opt<span class="o">}</span>/*<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> -d <span class="s2">&#34;</span><span class="si">${</span><span class="nv">directory</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nv">plugin</span><span class="o">=</span><span class="k">$(</span>basename <span class="s2">&#34;</span><span class="si">${</span><span class="nv">directory</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;updating </span><span class="si">${</span><span class="nv">plugin</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        git -C <span class="s2">&#34;</span><span class="si">${</span><span class="nv">directory</span><span class="si">}</span><span class="s2">&#34;</span> pull --quiet
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span></code></pre></div><p>In case you are using Vim, adjust the <code>PLUGIN_DIR</code> variable to point to your Vim plugin directory instead and save the resulting shell script as a file called <code>update-nvim-plugins.sh</code> in some folder of your choice. Do not forget to set the executable bit with <code>chmod +x /path/to/your/folder/update-nvim-plugins.sh</code>. Since all good developers must be lazy, write the following <a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html">systemd service</a> to execute the above script automatically:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-service" data-lang="service"><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">cron job that triggers an update of all nvim plugins</span>
</span></span><span class="line"><span class="cl"><span class="na">Wants</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl"><span class="na">After</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">oneshot</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/path/to/your/folder/update-nvim-plugins.sh</span>
</span></span><span class="line"><span class="cl"><span class="na">RemainAfterExit</span><span class="o">=</span><span class="s">false</span>
</span></span></code></pre></div><p>Adjust the <code>ExecStart</code> line to match the location where you saved the above script and place that service definition in a file called <code>nvim-plugins-update.service</code> into your local <code>~/.config/systemd/user</code> directory. Add another file called <code>nvim-plugins-update.timer</code> next to it that defines a <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html">systemd timer</a> with the following content:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-timer" data-lang="timer"><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Update nvim plugins every week</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Timer]</span>
</span></span><span class="line"><span class="cl"><span class="na">OnCalendar</span><span class="o">=</span><span class="s">weekly</span>
</span></span><span class="line"><span class="cl"><span class="na">Persistent</span><span class="o">=</span><span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">RandomizedDelaySec</span><span class="o">=</span><span class="s">5hours</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">timers.target</span>
</span></span></code></pre></div><p>Adjust how often you want to update the plugins you are using in the <code>OnCalendar</code> line. Enable this service/timer with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> systemctl --user <span class="nb">enable</span> nvim-plugins-update
</span></span></code></pre></div><p>Finally, add the following shell aliases to make it easier to interact with the created systemd units:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># trigger an update manually</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> update-nvim-plugins<span class="o">=</span><span class="s1">&#39;systemctl --user start nvim-plugins-update&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># see status of last auto-update</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> update-nvim-plugins-status<span class="o">=</span><span class="s1">&#39;systemctl --user status nvim-plugins-update&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># see logs of past auto-updates</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> update-nvim-plugins-logs<span class="o">=</span><span class="s1">&#39;journalctl --user --unit nvim-plugins-update&#39;</span>
</span></span></code></pre></div><p>With this setup in place, all your plugins will be automatically updated once per week or however often you have configured in the timer.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/vim" term="vim" label="vim"/><category scheme="https://seb.xn--ho-hia.de/tags/neovim" term="neovim" label="neovim"/><category scheme="https://seb.xn--ho-hia.de/tags/plugins" term="plugins" label="plugins"/><category scheme="https://seb.xn--ho-hia.de/tags/updates" term="updates" label="updates"/><category scheme="https://seb.xn--ho-hia.de/tags/systemd" term="systemd" label="systemd"/><category scheme="https://seb.xn--ho-hia.de/tags/shell" term="shell" label="shell"/><category scheme="https://seb.xn--ho-hia.de/tags/git" term="git" label="git"/></entry><entry><title type="html">Manage tmux sessions with tmuxp</title><link href="https://seb.xn--ho-hia.de/posts/tmux-tmuxp/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/tmux-status-bar/?utm_source=atom_feed" rel="related" type="text/html" title="tmux Status Bar"/><link href="https://seb.xn--ho-hia.de/posts/tmux-login-shell/?utm_source=atom_feed" rel="related" type="text/html" title="Use tmux as your login shell"/><link href="https://seb.xn--ho-hia.de/posts/tmux-peek/?utm_source=atom_feed" rel="related" type="text/html" title="Peeking with tmux"/><id>https://seb.xn--ho-hia.de/posts/tmux-tmuxp/</id><published>2021-12-20T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>To manage <a href="https://github.com/tmux/tmux">tmux</a> sessions, I like to use <a href="https://github.com/tmux-python/tmuxp">tmuxp</a>. It works by having pre-defined sessions in <code>~/.config/tmuxp</code> which looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">session_name</span><span class="p">:</span><span class="w"> </span><span class="l">cool-app</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">start_directory</span><span class="p">:</span><span class="w"> </span><span class="l">~/projects/cool-app</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">windows</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">window_name</span><span class="p">:</span><span class="w"> </span><span class="l">backend</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">start_directory</span><span class="p">:</span><span class="w"> </span><span class="l">backend</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">window_name</span><span class="p">:</span><span class="w"> </span><span class="l">frontend</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">start_directory</span><span class="p">:</span><span class="w"> </span><span class="l">frontend</span><span class="w">
</span></span></span></code></pre></div><p>In case the name of the file is <code>cool-app.yaml</code>, you can open the sessions with <code>tmuxp load cool-app --yes</code>.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/tmux" term="tmux" label="tmux"/><category scheme="https://seb.xn--ho-hia.de/tags/tmuxp" term="tmuxp" label="tmuxp"/></entry><entry><title type="html">Continuous Versioning with Maven</title><link href="https://seb.xn--ho-hia.de/posts/maven-cd-versioning/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-maven-packages/?utm_source=atom_feed" rel="related" type="text/html" title="GitHub Packages with Maven"/><link href="https://seb.xn--ho-hia.de/posts/maven-google-central/?utm_source=atom_feed" rel="related" type="text/html" title="Google Central"/><link href="https://seb.xn--ho-hia.de/posts/maven-encoding/?utm_source=atom_feed" rel="related" type="text/html" title="Specify encoding for Maven projects"/><link href="https://seb.xn--ho-hia.de/posts/maven-reproducible/?utm_source=atom_feed" rel="related" type="text/html" title="Creating reproducible artifacts with Maven"/><link href="https://seb.xn--ho-hia.de/posts/maven-github-sonarcloud/?utm_source=atom_feed" rel="related" type="text/html" title="Analyze Maven projects with SonarCloud using GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/maven-cd-versioning/</id><published>2021-12-06T00:00:00+00:00</published><updated>2023-01-06T16:46:32+01:00</updated><content type="html"><![CDATA[<p>To automatically version <a href="https://maven.apache.org/">Maven</a> projects, I like to use the <a href="https://www.mojohaus.org/versions-maven-plugin/">m-versions-p</a> like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ mvn versions:set -DnewVersion<span class="o">=</span>my.new.version -DgenerateBackupPoms<span class="o">=</span><span class="nb">false</span>
</span></span></code></pre></div><p>This will update the <code>version</code> property of every module in the reactor to prepare them for the next release. In case you are using <a href="https://github.com/features/actions">GitHub Actions</a>, consider using a <a href="../github-actions-create-timestamp">timestamp</a>.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/cd" term="cd" label="cd"/><category scheme="https://seb.xn--ho-hia.de/tags/maven" term="maven" label="maven"/><category scheme="https://seb.xn--ho-hia.de/tags/versioning" term="versioning" label="versioning"/></entry><entry><title type="html">GitHub Packages with Maven</title><link href="https://seb.xn--ho-hia.de/posts/github-maven-packages/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/maven-github-sonarcloud/?utm_source=atom_feed" rel="related" type="text/html" title="Analyze Maven projects with SonarCloud using GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/maven-google-central/?utm_source=atom_feed" rel="related" type="text/html" title="Google Central"/><link href="https://seb.xn--ho-hia.de/posts/maven-encoding/?utm_source=atom_feed" rel="related" type="text/html" title="Specify encoding for Maven projects"/><link href="https://seb.xn--ho-hia.de/posts/maven-reproducible/?utm_source=atom_feed" rel="related" type="text/html" title="Creating reproducible artifacts with Maven"/><id>https://seb.xn--ho-hia.de/posts/github-maven-packages/</id><published>2021-11-22T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p><a href="https://github.com/features/packages">GitHub Packages</a> can be used to host <a href="https://maven.apache.org/">Maven</a> packages with the following configuration in your <code>~/.m2/settings.xml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;settings&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;profiles&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;profile&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;id&gt;</span>github<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;repositories&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;repository&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;id&gt;</span>maven-build-process<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;name&gt;</span>GitHub maven-build-process Apache Maven Packages<span class="nt">&lt;/name&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;url&gt;</span>https://maven.pkg.github.com/metio/maven-build-process<span class="nt">&lt;/url&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;releases&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/releases&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;snapshots&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/snapshots&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/repository&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;repository&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;id&gt;</span>hcf4j<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;name&gt;</span>GitHub hcf4j Apache Maven Packages<span class="nt">&lt;/name&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;url&gt;</span>https://maven.pkg.github.com/metio/hcf4j<span class="nt">&lt;/url&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;releases&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/releases&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;snapshots&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/snapshots&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/repository&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;/repositories&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/profile&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/profiles&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;servers&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;server&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;id&gt;</span>maven-build-process<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;username&gt;</span>USERNAME<span class="nt">&lt;/username&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;password&gt;</span>GITHUB_TOKEN<span class="nt">&lt;/password&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/server&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;server&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;id&gt;</span>hcf4j<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;username&gt;</span>USERNAME<span class="nt">&lt;/username&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;password&gt;</span>GITHUB_TOKEN<span class="nt">&lt;/password&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/server&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/servers&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/settings&gt;</span>
</span></span></code></pre></div><p>You will have to add another repository/server for each project you are fetching from GitHub.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/maven" term="maven" label="maven"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-packages" term="github-packages" label="github packages"/></entry><entry><title type="html">Google Central</title><link href="https://seb.xn--ho-hia.de/posts/maven-google-central/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/maven-encoding/?utm_source=atom_feed" rel="related" type="text/html" title="Specify encoding for Maven projects"/><link href="https://seb.xn--ho-hia.de/posts/maven-reproducible/?utm_source=atom_feed" rel="related" type="text/html" title="Creating reproducible artifacts with Maven"/><link href="https://seb.xn--ho-hia.de/posts/maven-github-sonarcloud/?utm_source=atom_feed" rel="related" type="text/html" title="Analyze Maven projects with SonarCloud using GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/maven-google-central/</id><published>2021-11-08T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>Some time ago, <a href="https://www.google.com/">Google</a> started hosting a <a href="https://storage-download.googleapis.com/maven-central/index.html">copy</a> of <a href="https://search.maven.org/">Maven Central</a>. Configure it in your <code>~/.m2/settings.xml</code> like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;settings&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;mirrors&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;mirror&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;id&gt;</span>google-maven-central<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;name&gt;</span>Google Maven Central (Asia)<span class="nt">&lt;/name&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;url&gt;</span>https://maven-central-asia.storage-download.googleapis.com/maven2/<span class="nt">&lt;/url&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;mirrorOf&gt;</span>central<span class="nt">&lt;/mirrorOf&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/mirror&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;mirror&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;id&gt;</span>google-maven-central<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;name&gt;</span>Google Maven Central (EU)<span class="nt">&lt;/name&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;url&gt;</span>https://maven-central-eu.storage-download.googleapis.com/maven2/<span class="nt">&lt;/url&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;mirrorOf&gt;</span>central<span class="nt">&lt;/mirrorOf&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/mirror&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;mirror&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;id&gt;</span>google-maven-central<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;name&gt;</span>Google Maven Central (US)<span class="nt">&lt;/name&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;url&gt;</span>https://maven-central.storage-download.googleapis.com/maven2/<span class="nt">&lt;/url&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;mirrorOf&gt;</span>central<span class="nt">&lt;/mirrorOf&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/mirror&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/mirrors&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/settings&gt;</span>
</span></span></code></pre></div><p>Pick the mirror nearest to your location to get best speeds.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/maven" term="maven" label="maven"/><category scheme="https://seb.xn--ho-hia.de/tags/google" term="google" label="google"/><category scheme="https://seb.xn--ho-hia.de/tags/repository" term="repository" label="repository"/></entry><entry><title type="html">Backups with emacs</title><link href="https://seb.xn--ho-hia.de/posts/emacs-backups/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/emacs-systemd/?utm_source=atom_feed" rel="related" type="text/html" title="emacs and systemd"/><id>https://seb.xn--ho-hia.de/posts/emacs-backups/</id><published>2021-10-25T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p><a href="https://www.gnu.org/software/emacs/">emacs</a> will create backups of your files by default. Those backups are located right next to the original file and are called <code>&lt;file&gt;~</code>. Unfortunately, emacs will not clean those up by default, which annoys me from time to time. Therefore, I&rsquo;m now using the following configuration to keep those backups in a different folder:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-el" data-lang="el"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">setq</span> <span class="nv">version-control</span> <span class="no">t</span>     <span class="c1">;; Use version numbers for backups.</span>
</span></span><span class="line"><span class="cl">      <span class="nv">kept-new-versions</span> <span class="mi">10</span>  <span class="c1">;; Number of newest versions to keep.</span>
</span></span><span class="line"><span class="cl">      <span class="nv">kept-old-versions</span> <span class="mi">0</span>   <span class="c1">;; Number of oldest versions to keep.</span>
</span></span><span class="line"><span class="cl">      <span class="nv">delete-old-versions</span> <span class="no">t</span> <span class="c1">;; Don&#39;t ask to delete excess backup versions.</span>
</span></span><span class="line"><span class="cl">      <span class="nv">backup-by-copying</span> <span class="no">t</span><span class="p">)</span>  <span class="c1">;; Copy all files, don&#39;t rename them.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">setq</span> <span class="nv">vc-make-backup-files</span> <span class="no">t</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Default and per-save backups go here:</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">setq</span> <span class="nv">backup-directory-alist</span> <span class="o">&#39;</span><span class="p">((</span><span class="s">&#34;&#34;</span> <span class="o">.</span> <span class="s">&#34;~/.emacs.d/backup/per-save&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">force-backup-of-buffer</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="c1">;; Make a special &#34;per session&#34; backup at the first save of each</span>
</span></span><span class="line"><span class="cl">  <span class="c1">;; emacs session.</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nv">not</span> <span class="nv">buffer-backed-up</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; Override the default parameters for per-session backups.</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">backup-directory-alist</span> <span class="o">&#39;</span><span class="p">((</span><span class="s">&#34;&#34;</span> <span class="o">.</span> <span class="s">&#34;~/.emacs.d/backup/per-session&#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nv">kept-new-versions</span> <span class="mi">3</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">backup-buffer</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">  <span class="c1">;; Make a &#34;per save&#34; backup on each save.  The first save results in</span>
</span></span><span class="line"><span class="cl">  <span class="c1">;; both a per-session and a per-save backup, to keep the numbering</span>
</span></span><span class="line"><span class="cl">  <span class="c1">;; of per-save backups consistent.</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">buffer-backed-up</span> <span class="no">nil</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nv">backup-buffer</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;before-save-hook</span>  <span class="ss">&#39;force-backup-of-buffer</span><span class="p">)</span>
</span></span></code></pre></div><p>Thanks to that configuration, backups per-save will be created in <code>~/.emacs.d/backup/per-save</code> and backups per-session in <code>~/.emacs.d/backup/per-session</code>.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/emacs" term="emacs" label="emacs"/><category scheme="https://seb.xn--ho-hia.de/tags/backup" term="backup" label="backup"/></entry><entry><title type="html">Maintaining dotfiles with chezmoi</title><link href="https://seb.xn--ho-hia.de/posts/chezmoi-maintenance/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-gpg/?utm_source=atom_feed" rel="related" type="text/html" title="Encrypt dotfiles with chezmoi"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="related" type="text/html" title="Manage dotfiles with chezmoi and git"/><link href="https://seb.xn--ho-hia.de/posts/xdg-dot-files/?utm_source=atom_feed" rel="related" type="text/html" title="XDG Base Directory Specification"/><id>https://seb.xn--ho-hia.de/posts/chezmoi-maintenance/</id><published>2021-10-11T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>To make it easier managing many dotfiles with <a href="https://www.chezmoi.io/">chezmoi</a>, a shell function similar to the one below can be used:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="k">function</span> m-dotfiles-ok <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># public</span>
</span></span><span class="line"><span class="cl">    chezmoi add ~/.config/zsh --recursive
</span></span><span class="line"><span class="cl">    chezmoi add ~/.config/sway --recursive
</span></span><span class="line"><span class="cl">    chezmoi add ~/.config/tmux --recursive
</span></span><span class="line"><span class="cl">    chezmoi add ....
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># secrets</span>
</span></span><span class="line"><span class="cl">    chezmoi add --encrypt ~/.config/npm/npmrc
</span></span><span class="line"><span class="cl">    chezmoi add --encrypt ~/.ssh/id_rsa
</span></span><span class="line"><span class="cl">    chezmoi add --encrypt ...
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>Whenever you feel happy with your current setup, just call <code>m-dotfiles-ok</code> to push changes into the chezmoi source directory. Files will automatically be <a href="../chezmoi-gpg">encrypted</a> with <a href="https://www.gnupg.org/">gpg</a> and <a href="../chezmoi-auto-git">committed/pushed</a> into a <a href="https://git-scm.com/">Git</a> repository if you have done the necessary configuration beforehand.</p>
<p>In general, editing your dotfiles directly as explained in the second option of the <a href="https://www.chezmoi.io/user-guide/frequently-asked-questions/usage/#how-do-i-edit-my-dotfiles-with-chezmoi">FAQ</a> seems easier though. Refactoring your dotfiles is especially easy when the <code>exact_</code> prefix is used for directories. As explained in the <a href="https://www.chezmoi.io/reference/target-types/#directories">documentation</a>, all files that are not managed by <code>chezmoi</code> will be removed, therefore your configuration will always match what is in your source directory.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/dotfiles" term="dotfiles" label="dotfiles"/><category scheme="https://seb.xn--ho-hia.de/tags/chezmoi" term="chezmoi" label="chezmoi"/></entry><entry><title type="html">Encrypt dotfiles with chezmoi</title><link href="https://seb.xn--ho-hia.de/posts/chezmoi-gpg/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="related" type="text/html" title="Manage dotfiles with chezmoi and git"/><link href="https://seb.xn--ho-hia.de/posts/xdg-dot-files/?utm_source=atom_feed" rel="related" type="text/html" title="XDG Base Directory Specification"/><id>https://seb.xn--ho-hia.de/posts/chezmoi-gpg/</id><published>2021-09-20T00:00:00+00:00</published><updated>2023-01-08T09:25:44+01:00</updated><content type="html"><![CDATA[<p><strong>RECOMMENDATION</strong>: Use <a href="../chezmoi-age">age</a> instead of <code>gpg</code>.</p>
<p><a href="https://www.chezmoi.io/">chezmoi</a> can use various external tools to <a href="https://www.chezmoi.io/docs/how-to/#keep-data-private">keep data private</a>. <a href="https://www.gnupg.org/">gpg</a> is used by various other tools as well, so chances are that you already have a functional setup on your system. To configure <code>gpg</code> with <code>chezmoi</code>, just set yourself as the recipient like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">gpg</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">recipient</span> <span class="p">=</span> <span class="s2">&#34;your.name@example.com&#34;</span>
</span></span></code></pre></div><p>Calling <code>chezmoi add --encrypt /path/to/secret</code> will now create encrypt the file with your public key which allows you to decrypt them later with your private key.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/dotfiles" term="dotfiles" label="dotfiles"/><category scheme="https://seb.xn--ho-hia.de/tags/chezmoi" term="chezmoi" label="chezmoi"/><category scheme="https://seb.xn--ho-hia.de/tags/gpg" term="gpg" label="gpg"/><category scheme="https://seb.xn--ho-hia.de/tags/encryption" term="encryption" label="encryption"/></entry><entry><title type="html">Manage dotfiles with chezmoi and git</title><link href="https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/git-push-only-mirror/?utm_source=atom_feed" rel="related" type="text/html" title="Push-only mirrors for Git Repositories"/><link href="https://seb.xn--ho-hia.de/posts/git-mirror/?utm_source=atom_feed" rel="related" type="text/html" title="Mirror Git Repositories"/><link href="https://seb.xn--ho-hia.de/posts/xdg-dot-files/?utm_source=atom_feed" rel="related" type="text/html" title="XDG Base Directory Specification"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><link href="https://seb.xn--ho-hia.de/posts/short-git-clones/?utm_source=atom_feed" rel="related" type="text/html" title="Short Git Clones"/><id>https://seb.xn--ho-hia.de/posts/chezmoi-auto-git/</id><published>2021-09-06T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p><a href="https://www.chezmoi.io/">chezmoi</a> can automatically commit and push changes to your <a href="https://en.wikipedia.org/wiki/dotfile">dotfiles</a> into a (remote) <a href="https://git-scm.com/">Git</a> repository. Enable it with the following snippet in your <code>chezmoi.toml</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">sourceVCS</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">autoCommit</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="nx">autoPush</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p>Every time you call <code>chezmoi add /path/to/file</code> will now create a new commit in your local chezmoi repository and push those changes into your configured remote repository.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/dotfiles" term="dotfiles" label="dotfiles"/><category scheme="https://seb.xn--ho-hia.de/tags/chezmoi" term="chezmoi" label="chezmoi"/><category scheme="https://seb.xn--ho-hia.de/tags/git" term="git" label="git"/></entry><entry><title type="html">Waybar on SwayWM</title><link href="https://seb.xn--ho-hia.de/posts/sway-waybar/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/sway-screenlock/?utm_source=atom_feed" rel="related" type="text/html" title="Lock your screen on SwayWM"/><link href="https://seb.xn--ho-hia.de/posts/sway-screenshots/?utm_source=atom_feed" rel="related" type="text/html" title="Taken screenshots on SwayWM"/><id>https://seb.xn--ho-hia.de/posts/sway-waybar/</id><published>2021-08-23T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p><a href="https://github.com/Alexays/Waybar/">Waybar</a> can be used as a status bar for <a href="https://swaywm.org/">SwayWM</a>. You tell Sway to use it with the following snippet in your Sway configuration:</p>
<pre tabindex="0"><code>bar {
    swaybar_command waybar
}
</code></pre><p>Configure Waybar itself in <code>~/.config/waybar/config</code>:</p>
<pre tabindex="0"><code>{
    &#34;layer&#34;: &#34;top&#34;,
    &#34;modules-left&#34;: [&#34;sway/workspaces&#34;, &#34;sway/mode&#34;],
    &#34;modules-center&#34;: [&#34;sway/window&#34;],
    &#34;modules-right&#34;: [&#34;clock&#34;],
    &#34;sway/window&#34;: {
        &#34;max-length&#34;: 50
    },
    &#34;clock&#34;: {
        &#34;format-alt&#34;: &#34;{:%a, %d. %b  %H:%M}&#34;
    }
}
</code></pre>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/swaywm" term="swaywm" label="swaywm"/><category scheme="https://seb.xn--ho-hia.de/tags/waybar" term="waybar" label="waybar"/></entry><entry><title type="html">tmux Status Bar</title><link href="https://seb.xn--ho-hia.de/posts/tmux-status-bar/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/tmux-login-shell/?utm_source=atom_feed" rel="related" type="text/html" title="Use tmux as your login shell"/><link href="https://seb.xn--ho-hia.de/posts/tmux-peek/?utm_source=atom_feed" rel="related" type="text/html" title="Peeking with tmux"/><id>https://seb.xn--ho-hia.de/posts/tmux-status-bar/</id><published>2021-08-09T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>To see the currently active <a href="https://github.com/tmux/tmux">tmux</a> status bar configuration, call:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ tmux show-options -g <span class="p">|</span> grep status
</span></span></code></pre></div><p>Change on of those values with in the current tmux session:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ tmux set-option status-right <span class="s2">&#34;&#34;</span>
</span></span></code></pre></div><p>Persist the change in your <code>tmux.conf</code> like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># disable right side of status bar</span>
</span></span><span class="line"><span class="cl">set-option -g status-right <span class="s2">&#34;&#34;</span>
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/tmux" term="tmux" label="tmux"/><category scheme="https://seb.xn--ho-hia.de/tags/status-bar" term="status-bar" label="status bar"/></entry><entry><title type="html">emacs and systemd</title><link href="https://seb.xn--ho-hia.de/posts/emacs-systemd/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/emacs-systemd/</id><published>2021-07-26T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>I like to use <a href="https://www.gnu.org/software/emacs/">emacs</a> to edit files in a terminal. It tends to start a little slow, therefore I&rsquo;ve created a <a href="https://www.freedesktop.org/wiki/Software/systemd/">systemd</a> unit to automatically start the emacs daemon and use aliases to connect to the running daemon. The unit looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Emacs text editor [%I]</span>
</span></span><span class="line"><span class="cl"><span class="na">Documentation</span><span class="o">=</span><span class="s">info:emacs man:emacs(1) https://gnu.org/software/emacs/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">forking</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/emacs --daemon=%i</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStop</span><span class="o">=</span><span class="s">/usr/bin/emacsclient --eval &#34;(kill-emacs)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">SSH_AUTH_SOCK=%t/keyring/ssh</span>
</span></span><span class="line"><span class="cl"><span class="na">Restart</span><span class="o">=</span><span class="s">on-failure</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">default.target</span>
</span></span></code></pre></div><p>Enable it with <code>systemctl --user enable emacs@user</code> and define any number of aliases to make connecting to the emacs daemon easier:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">e</span><span class="o">=</span><span class="s1">&#39;emacsclient --tty --socket-name=user&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">vim</span><span class="o">=</span><span class="s1">&#39;emacsclient --tty --socket-name=user&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">vi</span><span class="o">=</span><span class="s1">&#39;emacsclient --tty --socket-name=user&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">nano</span><span class="o">=</span><span class="s1">&#39;emacsclient --tty --socket-name=user&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">ed</span><span class="o">=</span><span class="s1">&#39;emacsclient --tty --socket-name=user&#39;</span>
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/emacs" term="emacs" label="emacs"/><category scheme="https://seb.xn--ho-hia.de/tags/systemd" term="systemd" label="systemd"/></entry><entry><title type="html">Push-only mirrors for Git Repositories</title><link href="https://seb.xn--ho-hia.de/posts/git-push-only-mirror/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/git-mirror/?utm_source=atom_feed" rel="related" type="text/html" title="Mirror Git Repositories"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><link href="https://seb.xn--ho-hia.de/posts/short-git-clones/?utm_source=atom_feed" rel="related" type="text/html" title="Short Git Clones"/><id>https://seb.xn--ho-hia.de/posts/git-push-only-mirror/</id><published>2021-07-12T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>In case you want to have push-only mirrors for your <a href="https://git-scm.com/">Git</a> repository, consider adding a special mirror remote like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ git remote add mirrors DISABLED
</span></span><span class="line"><span class="cl">$ git remote set-url --add --push mirrors git@codeberg.org:org/repo.git
</span></span><span class="line"><span class="cl">$ git remote set-url --add --push mirrors git@gitlab.com:org/repo.git
</span></span><span class="line"><span class="cl">$ git remote set-url --add --push mirrors git@bitbucket.org:org/repo.git
</span></span></code></pre></div><p>The above will create a new remote called <code>mirrors</code> which has no <code>fetch</code> URL and therefore can only be pushed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ git remote -v
</span></span><span class="line"><span class="cl">mirrors DISABLED <span class="o">(</span>fetch<span class="o">)</span>
</span></span><span class="line"><span class="cl">mirrors git@codeberg.org:org/repo.git <span class="o">(</span>push<span class="o">)</span>
</span></span><span class="line"><span class="cl">mirrors git@gitlab.com:org/repo.git <span class="o">(</span>push<span class="o">)</span>
</span></span><span class="line"><span class="cl">mirrors git@bitbucket.org:org/repo.git <span class="o">(</span>push<span class="o">)</span>
</span></span></code></pre></div><p>Calling <code>git push mirrors main:main</code> will push the local <code>main</code> branch into all defined mirrors.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="../git-mirror">Git mirror</a></li>
<li><a href="../gitlab-distributor">gitlab-distributor</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/decentralized" term="decentralized" label="decentralized"/><category scheme="https://seb.xn--ho-hia.de/tags/git" term="git" label="git"/><category scheme="https://seb.xn--ho-hia.de/tags/push" term="push" label="push"/><category scheme="https://seb.xn--ho-hia.de/tags/mirror" term="mirror" label="mirror"/></entry><entry><title type="html">Mirror Git Repositories</title><link href="https://seb.xn--ho-hia.de/posts/git-mirror/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><link href="https://seb.xn--ho-hia.de/posts/short-git-clones/?utm_source=atom_feed" rel="related" type="text/html" title="Short Git Clones"/><id>https://seb.xn--ho-hia.de/posts/git-mirror/</id><published>2021-06-28T00:00:00+00:00</published><updated>2023-01-06T16:46:32+01:00</updated><content type="html"><![CDATA[<p>In case you want to make use of the decentralized nature of <a href="https://git-scm.com/">Git</a>, consider using multiple push targets like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ git remote set-url origin --push --add git@example.com/project.git
</span></span><span class="line"><span class="cl">$ git remote set-url origin --push --add git@another.com/project.git
</span></span></code></pre></div><p>Note that the first call to <code>set-url</code> will overwrite an existing remote creating with <code>git clone</code>. Any additional call will actually recognize the <code>--add</code> option and add the new target to an existing remote.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="../git-push-only-mirror">push only mirrors</a></li>
<li><a href="../gitlab-distributor">gitlab-distributor</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/decentralized" term="decentralized" label="decentralized"/><category scheme="https://seb.xn--ho-hia.de/tags/git" term="git" label="git"/><category scheme="https://seb.xn--ho-hia.de/tags/push" term="push" label="push"/><category scheme="https://seb.xn--ho-hia.de/tags/mirror" term="mirror" label="mirror"/></entry><entry><title type="html">Using multiple clusters with kubectl</title><link href="https://seb.xn--ho-hia.de/posts/kubectl-multi-cluster/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/kubectl-multi-cluster/</id><published>2021-06-14T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>To connect to multiple <a href="https://kubernetes.io/">Kubernetes</a> clusters with <a href="https://kubernetes.io/docs/reference/kubectl/overview/">kubectl</a>, I like to define aliases like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">rancher</span><span class="o">=</span><span class="s2">&#34;kubectl --kubeconfig ~/.kube/rancher.config&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">work</span><span class="o">=</span><span class="s2">&#34;kubectl --kubeconfig ~/.kube/work.config&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">customer</span><span class="o">=</span><span class="s2">&#34;kubectl --kubeconfig ~/.kube/customer.config&#34;</span>
</span></span></code></pre></div><p>Those aliases allow me to write things like <code>rancher get pods --namespace some-namespace</code> without worrying the wrong context is active. Using multiple configurations - one for each cluster - seems to be easier to manage since most clusters allow to download a ready-to-use configuration file. Instead of mangling them together manually, I just specify another alias whenever I get to work with another cluster.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/kubectl" term="kubectl" label="kubectl"/><category scheme="https://seb.xn--ho-hia.de/tags/kubernetes" term="kubernetes" label="kubernetes"/></entry><entry><title type="html">Specify encoding for Maven projects</title><link href="https://seb.xn--ho-hia.de/posts/maven-encoding/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/maven-reproducible/?utm_source=atom_feed" rel="related" type="text/html" title="Creating reproducible artifacts with Maven"/><link href="https://seb.xn--ho-hia.de/posts/maven-github-sonarcloud/?utm_source=atom_feed" rel="related" type="text/html" title="Analyze Maven projects with SonarCloud using GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/maven-encoding/</id><published>2021-05-31T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p><a href="https://maven.apache.org/">Maven</a> projects by default use the file encoding of the operating system. This can be problematic in case different operating systems with different encoding settings are used to build the project. Specify the encoding of your source code and resource files as in the following snippets to fix that problem.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;properties&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;project.build.sourceEncoding&gt;</span>UTF-8<span class="nt">&lt;/project.build.sourceEncoding&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;project.reporting.outputEncoding&gt;</span>UTF-8<span class="nt">&lt;/project.reporting.outputEncoding&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/properties&gt;</span>
</span></span></code></pre></div><h2 id="links">Links</h2>
<ul>
<li><a href="https://maven.apache.org/pom.html#Properties">https://maven.apache.org/pom.html#Properties</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/maven" term="maven" label="maven"/><category scheme="https://seb.xn--ho-hia.de/tags/encoding" term="encoding" label="encoding"/><category scheme="https://seb.xn--ho-hia.de/tags/windows" term="windows" label="windows"/><category scheme="https://seb.xn--ho-hia.de/tags/linux" term="linux" label="linux"/><category scheme="https://seb.xn--ho-hia.de/tags/mac" term="mac" label="mac"/></entry><entry><title type="html">Creating reproducible artifacts with Maven</title><link href="https://seb.xn--ho-hia.de/posts/maven-reproducible/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/maven-github-sonarcloud/?utm_source=atom_feed" rel="related" type="text/html" title="Analyze Maven projects with SonarCloud using GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/maven-reproducible/</id><published>2021-05-17T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>To create <a href="https://reproducible-builds.org/">reproducible builds</a> with <a href="https://maven.apache.org/">Maven</a> projects, it&rsquo;s enough to specify the <code>project.build.outputTimestamp</code> property like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;properties&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;project.build.outputTimestamp&gt;</span>2020<span class="nt">&lt;/project.build.outputTimestamp&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/properties&gt;</span>
</span></span></code></pre></div><h2 id="links">Links</h2>
<ul>
<li><a href="https://maven.apache.org/pom.html#Properties">https://maven.apache.org/pom.html#Properties</a></li>
<li><a href="https://maven.apache.org/guides/mini/guide-reproducible-builds.html">https://maven.apache.org/guides/mini/guide-reproducible-builds.html</a></li>
<li><a href="https://github.com/rodiontsev/maven-build-info-plugin">https://github.com/rodiontsev/maven-build-info-plugin</a></li>
<li><a href="https://github.com/phax/ph-buildinfo-maven-plugin">https://github.com/phax/ph-buildinfo-maven-plugin</a></li>
<li><a href="https://github.com/Zlika/reproducible-build-maven-plugin">https://github.com/Zlika/reproducible-build-maven-plugin</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/maven" term="maven" label="maven"/><category scheme="https://seb.xn--ho-hia.de/tags/reproducible" term="reproducible" label="reproducible"/></entry><entry><title type="html">Analyze Maven projects with SonarCloud using GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/maven-github-sonarcloud/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-schedule-build/?utm_source=atom_feed" rel="related" type="text/html" title="Delay GitHub Actions builds"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-send-toot/?utm_source=atom_feed" rel="related" type="text/html" title="Send toots with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-send-email/?utm_source=atom_feed" rel="related" type="text/html" title="Send emails with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-upload-release-assets/?utm_source=atom_feed" rel="related" type="text/html" title="Upload release assets with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/maven-github-sonarcloud/</id><published>2021-05-03T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>To analyze <a href="https://maven.apache.org/">Maven</a> projects with <a href="https://sonarcloud.io">SonarCloud</a> using <a href="https://github.com/features/actions">GitHub Actions</a>, first create the following <code>settings.xml</code> file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;settings</span> <span class="na">xmlns=</span><span class="s">&#34;http://maven.apache.org/SETTINGS/1.0.0&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">xmlns:xsi=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">xsi:schemaLocation=</span><span class="s">&#34;http://maven.apache.org/SETTINGS/1.0.0
</span></span></span><span class="line"><span class="cl"><span class="s">                      http://maven.apache.org/xsd/settings-1.0.0.xsd&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;pluginGroups&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;pluginGroup&gt;</span>org.sonarsource.scanner.maven<span class="nt">&lt;/pluginGroup&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/pluginGroups&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;activeProfiles&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;activeProfile&gt;</span>sonar<span class="nt">&lt;/activeProfile&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/activeProfiles&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;profiles&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;profile&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;id&gt;</span>sonar<span class="nt">&lt;/id&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;properties&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;sonar.host.url&gt;</span>https://sonarcloud.io<span class="nt">&lt;/sonar.host.url&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;sonar.organization&gt;</span>YOUR_ORG<span class="nt">&lt;/sonar.organization&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;sonar.projectKey&gt;</span>YOUR_PROJECT<span class="nt">&lt;/sonar.projectKey&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;sonar.login&gt;</span>${env.SONAR_TOKEN}<span class="nt">&lt;/sonar.login&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/properties&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/profile&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/profiles&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/settings&gt;</span>
</span></span></code></pre></div><p>Finally, add a step to your workflow:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Verify Project</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">mvn --settings $GITHUB_WORKSPACE/settings.xml verify sonar:sonar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">SONAR_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.SONAR_TOKEN }}</span><span class="w">
</span></span></span></code></pre></div><h2 id="links">Links</h2>
<ul>
<li><a href="https://maven.apache.org/settings.html">https://maven.apache.org/settings.html</a></li>
<li><a href="https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-maven/">https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-maven/</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/maven" term="maven" label="maven"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/sonarqube" term="sonarqube" label="sonarqube"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/></entry><entry><title type="html">Use tmux as your login shell</title><link href="https://seb.xn--ho-hia.de/posts/tmux-login-shell/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/tmux-peek/?utm_source=atom_feed" rel="related" type="text/html" title="Peeking with tmux"/><id>https://seb.xn--ho-hia.de/posts/tmux-login-shell/</id><published>2021-04-19T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>To use <a href="https://github.com/tmux/tmux">tmux</a> as your login shell, use <code>chsh</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># list all available shells</span>
</span></span><span class="line"><span class="cl">$ chsh --list-shells
</span></span><span class="line"><span class="cl">/bin/sh
</span></span><span class="line"><span class="cl">/bin/bash
</span></span><span class="line"><span class="cl">/sbin/nologin
</span></span><span class="line"><span class="cl">/usr/bin/sh
</span></span><span class="line"><span class="cl">/usr/bin/bash
</span></span><span class="line"><span class="cl">/usr/sbin/nologin
</span></span><span class="line"><span class="cl">/usr/bin/zsh
</span></span><span class="line"><span class="cl">/bin/zsh
</span></span><span class="line"><span class="cl">/usr/bin/tmux
</span></span><span class="line"><span class="cl">/bin/tmux
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># select login shell</span>
</span></span><span class="line"><span class="cl">$ chsh --shell /usr/bin/tmux
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/tmux" term="tmux" label="tmux"/><category scheme="https://seb.xn--ho-hia.de/tags/login-shell" term="login-shell" label="login shell"/><category scheme="https://seb.xn--ho-hia.de/tags/chsh" term="chsh" label="chsh"/></entry><entry><title type="html">Lock your screen on SwayWM</title><link href="https://seb.xn--ho-hia.de/posts/sway-screenlock/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/sway-screenshots/?utm_source=atom_feed" rel="related" type="text/html" title="Taken screenshots on SwayWM"/><id>https://seb.xn--ho-hia.de/posts/sway-screenlock/</id><published>2021-04-05T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p><a href="https://swaywm.org/">SwayWM</a> users can use <a href="https://github.com/swaywm/swaylock">swaylock</a> to lock their screen. Place the following key binding in your Sway configuration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># lock your screen</span>
</span></span><span class="line"><span class="cl">bindsym <span class="nv">$mod</span>+Ctrl+l <span class="nb">exec</span> swaylock --color <span class="m">000000</span>
</span></span></code></pre></div><p><code>$mod+Ctrl+l</code> will lock your screen and turn it to black. The <code>--color</code> flag allows any color in the form of <code>rrggbb[aa]</code>.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/swaywm" term="swaywm" label="swaywm"/><category scheme="https://seb.xn--ho-hia.de/tags/screenlock" term="screenlock" label="screenlock"/></entry><entry><title type="html">Peeking with tmux</title><link href="https://seb.xn--ho-hia.de/posts/tmux-peek/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/tmux-peek/</id><published>2021-04-05T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p><a href="https://github.com/tmux/tmux">tmux</a> uses can use the following snippet to peek at files. Place it in your <code>.bashrc</code> or similar file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">peek<span class="o">()</span> <span class="o">{</span> tmux split-window -p <span class="m">33</span> <span class="s2">&#34;</span><span class="nv">$EDITOR</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span> <span class="o">}</span>
</span></span></code></pre></div><p>Calling <code>peek &lt;file&gt;</code> will open <code>&lt;file&gt;</code> in lower third of tmux window.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/tmux" term="tmux" label="tmux"/><category scheme="https://seb.xn--ho-hia.de/tags/peek" term="peek" label="peek"/></entry><entry><title type="html">Taken screenshots on SwayWM</title><link href="https://seb.xn--ho-hia.de/posts/sway-screenshots/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/sway-screenshots/</id><published>2021-03-22T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p><a href="https://swaywm.org/">SwayWM</a> uses can use a mixture of <a href="https://github.com/emersion/grim">grim</a> and <a href="https://github.com/emersion/slurp">slurp</a> to take screenshots of their desktop. Place the following key binding in your Sway configuration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># take screenshot of currently focused screen</span>
</span></span><span class="line"><span class="cl">bindsym <span class="nv">$mod</span>+Print <span class="nb">exec</span> /usr/bin/grim -o <span class="k">$(</span>swaymsg -t get_outputs <span class="p">|</span> jq -r <span class="s1">&#39;.[] | select(.focused) | .name&#39;</span><span class="k">)</span> <span class="k">$(</span>xdg-user-dir PICTURES<span class="k">)</span>/<span class="k">$(</span>date +<span class="s1">&#39;%Y-%m-%d-%H%M%S.png&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># take screenshot of selection</span>
</span></span><span class="line"><span class="cl">bindsym <span class="nv">$mod</span>+Shift+p <span class="nb">exec</span> /usr/bin/grim -g <span class="s2">&#34;</span><span class="k">$(</span>/usr/bin/slurp<span class="k">)</span><span class="s2">&#34;</span> <span class="k">$(</span>xdg-user-dir PICTURES<span class="k">)</span>/<span class="k">$(</span>date +<span class="s1">&#39;%Y-%m-%d-%H%M%S.png&#39;</span><span class="k">)</span>
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/swaywm" term="swaywm" label="swaywm"/><category scheme="https://seb.xn--ho-hia.de/tags/screenshot" term="screenshot" label="screenshot"/><category scheme="https://seb.xn--ho-hia.de/tags/slurp" term="slurp" label="slurp"/><category scheme="https://seb.xn--ho-hia.de/tags/grim" term="grim" label="grim"/></entry><entry><title type="html">Executable READMEs</title><link href="https://seb.xn--ho-hia.de/posts/makefile-readme/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/makefile-ignore/?utm_source=atom_feed" rel="related" type="text/html" title="Ignore Exit Codes"/><link href="https://seb.xn--ho-hia.de/posts/makefile-help/?utm_source=atom_feed" rel="related" type="text/html" title="Makefile Help"/><id>https://seb.xn--ho-hia.de/posts/makefile-readme/</id><published>2021-03-08T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p><code>README</code> file typically contain information about the project itself, for example how it can be installed/used/build. Most of the time these files contains command line instructions that users/contributors copy and paste into their terminal. Instead of doing that, consider placing a <code>Makefile</code> in the root of your project which contains the exact same instructions. Thanks to <code>make</code>, all your contributors can now use TAB-completion to run any of the pre-defined <code>make</code> targets.</p>
<p>The following example is part of one of my projects, and I certainly don&rsquo;t want to type (or even copy) that all the time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">release</span>-<span class="n">into</span>-<span class="n">local</span>-<span class="n">nexus</span>
</span></span><span class="line"><span class="cl"><span class="nf">release-into-local-nexus</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	mvn versions:set <span class="se">\
</span></span></span><span class="line"><span class="cl">	   -DnewVersion<span class="o">=</span><span class="k">$(</span>TIMESTAMPED_VERSION<span class="k">)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">	   -DgenerateBackupPoms<span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl">	-mvn clean deploy scm:tag <span class="se">\
</span></span></span><span class="line"><span class="cl">	   -DpushChanges<span class="o">=</span><span class="nb">false</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">	   -DskipLocalStaging<span class="o">=</span><span class="nb">true</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">	   -Drelease<span class="o">=</span><span class="nb">local</span>
</span></span><span class="line"><span class="cl">	mvn versions:set <span class="se">\
</span></span></span><span class="line"><span class="cl">	   -DnewVersion<span class="o">=</span>9999.99.99-SNAPSHOT <span class="se">\
</span></span></span><span class="line"><span class="cl">	   -DgenerateBackupPoms<span class="o">=</span><span class="nb">false</span>
</span></span></code></pre></div><p>With the above target in place, everyone can now do <code>make release-into-local-nexus</code> instead of typing/copying the commands themselves. Thanks to TAB-completion you just have to do <code>make r&lt;TAB&gt;</code> and confirm with <code>&gt;ENTER&gt;</code> to perform a release.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.makeareadme.com/">https://www.makeareadme.com/</a></li>
<li><a href="https://opensource.guide/starting-a-project/#launching-your-own-open-source-project">https://opensource.guide/starting-a-project/#launching-your-own-open-source-project</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/make" term="make" label="make"/><category scheme="https://seb.xn--ho-hia.de/tags/makefile" term="makefile" label="Makefile"/><category scheme="https://seb.xn--ho-hia.de/tags/readme" term="readme" label="README"/><category scheme="https://seb.xn--ho-hia.de/tags/teamwork" term="teamwork" label="teamwork"/></entry><entry><title type="html">Delay GitHub Actions builds</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-schedule-build/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-send-toot/?utm_source=atom_feed" rel="related" type="text/html" title="Send toots with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-send-email/?utm_source=atom_feed" rel="related" type="text/html" title="Send emails with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-upload-release-assets/?utm_source=atom_feed" rel="related" type="text/html" title="Upload release assets with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-release/?utm_source=atom_feed" rel="related" type="text/html" title="Create GitHub releases with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/github-actions-schedule-build/</id><published>2021-02-22T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To delay the execution of an <a href="https://github.com/features/actions">GitHub Action</a>, use a mixture of the <code>on: schedule: ...</code> configuration, and a <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif">conditional build step</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">schedule</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">cron</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;&lt;CRON&gt;&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Count commits in last week</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">commits</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo &#34;::set-output name=count::$(git rev-list --count HEAD --since=&#39;&lt;DATE&gt;&#39;)&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build project</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.commits.outputs.count &gt; 0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">build-project</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;CRON&gt;</code>: cron expression - use <a href="https://crontab.guru/">https://crontab.guru/</a>.</li>
<li><code>&lt;DATE&gt;</code>: Git date expression that matches <code>&lt;CRON&gt;</code>.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/schedule" term="schedule" label="schedule"/></entry><entry><title type="html">Ignore Exit Codes</title><link href="https://seb.xn--ho-hia.de/posts/makefile-ignore/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/makefile-help/?utm_source=atom_feed" rel="related" type="text/html" title="Makefile Help"/><id>https://seb.xn--ho-hia.de/posts/makefile-ignore/</id><published>2021-02-08T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>In case you are using a <code>Makefile</code> to define a complex build step - for example start database, run tests, stop database - consider using the <code>-</code> qualifier in front of your actual build step like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">build</span>
</span></span><span class="line"><span class="cl"><span class="nf">build</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	start-database
</span></span><span class="line"><span class="cl">	-build-software
</span></span><span class="line"><span class="cl">	stop-database
</span></span></code></pre></div><p>Thanks to <code>-</code>, the database will be stopped even if building your software fails, therefore making sure to clean up after ourselves once the build finishes.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.gnu.org/software/make/manual/make.html#Recipe-Echoing">https://www.gnu.org/software/make/manual/make.html#Recipe-Echoing</a></li>
<li><a href="https://www.gnu.org/software/make/manual/make.html#Errors-in-Recipes">https://www.gnu.org/software/make/manual/make.html#Errors-in-Recipes</a></li>
<li><a href="https://www.gnu.org/software/make/manual/make.html#How-the-MAKE-Variable-Works">https://www.gnu.org/software/make/manual/make.html#How-the-MAKE-Variable-Works</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/make" term="make" label="make"/><category scheme="https://seb.xn--ho-hia.de/tags/makefile" term="makefile" label="Makefile"/><category scheme="https://seb.xn--ho-hia.de/tags/exit-code" term="exit-code" label="exit code"/></entry><entry><title type="html">Service workers with Hugo</title><link href="https://seb.xn--ho-hia.de/posts/hugo-serviceworkers/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/hugo-webmanifest/?utm_source=atom_feed" rel="related" type="text/html" title="Web app manifests with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-bundles/?utm_source=atom_feed" rel="related" type="text/html" title="Bundling with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-humans/?utm_source=atom_feed" rel="related" type="text/html" title="humans.txt with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-foaf/?utm_source=atom_feed" rel="related" type="text/html" title="FOAF with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-atom/?utm_source=atom_feed" rel="related" type="text/html" title="Atom Feed with Hugo"/><id>https://seb.xn--ho-hia.de/posts/hugo-serviceworkers/</id><published>2021-01-25T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To use a <a href="https://serviceworke.rs/">serviceworker</a> to cache a <a href="https://gohugo.io/">Hugo</a> site, configure a <a href="https://en.wikipedia.org/wiki/Media_type">media type</a> in your <code>config.toml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">mediaTypes</span><span class="p">.</span><span class="s2">&#34;application/javascript&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">suffixes</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;js&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">outputFormats</span><span class="p">.</span><span class="nx">ServiceWorker</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;ServiceWorker&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mediaType</span> <span class="p">=</span> <span class="s2">&#34;application/javascript&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">baseName</span> <span class="p">=</span> <span class="s2">&#34;serviceworker&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isPlainText</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">rel</span> <span class="p">=</span> <span class="s2">&#34;alternate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isHTML</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">noUgly</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">permalinkable</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></div><p>Create a new layout in <code>_default/home.serviceworker.js</code> with the following content:</p>
<pre tabindex="0"><code class="language-gotemplate" data-lang="gotemplate">const CACHE = &#39;cache-and-update&#39;;

self.addEventListener(&#39;install&#39;, (event) =&gt; {
  event.waitUntil(precache());
});

self.addEventListener(&#39;fetch&#39;, (event) =&gt; {
  event.respondWith(fromCache(event.request));
  event.waitUntil(update(event.request));
});

const precache = async () =&gt; {
    const cache = await caches.open(CACHE);
    return await cache.addAll([
        {{ range $i, $e := .Site.RegularPages }}
        &#39;{{ $.RelPermalink }}&#39;{{ if $i }}, {{ end }}
        {{ end }}
    ]);
}

const fromCache = async (request) =&gt; {
    const cache = await caches.open(CACHE);
    const match = await cache.match(request);
    return match || Promise.reject(&#39;no-match&#39;);
}

const update = async (request) =&gt; {
    const cache = await caches.open(CACHE);
    const response = await fetch(request);
    return await cache.put(request, response);
}
</code></pre><h2 id="links">Links</h2>
<ul>
<li><a href="https://github.com/gohugoio/hugo/issues/5495">https://github.com/gohugoio/hugo/issues/5495</a></li>
<li><a href="https://github.com/wildhaber/offline-first-sw">https://github.com/wildhaber/offline-first-sw</a></li>
<li><a href="https://gohugohq.com/howto/go-offline-with-service-worker/">https://gohugohq.com/howto/go-offline-with-service-worker/</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/website" term="website" label="website"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/><category scheme="https://seb.xn--ho-hia.de/tags/service-worker" term="service-worker" label="service worker"/></entry><entry><title type="html">Web app manifests with Hugo</title><link href="https://seb.xn--ho-hia.de/posts/hugo-webmanifest/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/hugo-bundles/?utm_source=atom_feed" rel="related" type="text/html" title="Bundling with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-humans/?utm_source=atom_feed" rel="related" type="text/html" title="humans.txt with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-foaf/?utm_source=atom_feed" rel="related" type="text/html" title="FOAF with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-atom/?utm_source=atom_feed" rel="related" type="text/html" title="Atom Feed with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/hugo-webmanifest/</id><published>2021-01-11T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To publish a <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest">web app manifest</a> document with your <a href="https://gohugo.io/">Hugo</a> site, configure a <a href="https://en.wikipedia.org/wiki/Media_type">media type</a> in your <code>config.toml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">mediaTypes</span><span class="p">.</span><span class="s2">&#34;application/manifest+json&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">suffixes</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;webmanifest&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">outputFormats</span><span class="p">.</span><span class="nx">Webmanifest</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Web App Manifest&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mediaType</span> <span class="p">=</span> <span class="s2">&#34;application/manifest+json&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">baseName</span> <span class="p">=</span> <span class="s2">&#34;manifest&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isPlainText</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">rel</span> <span class="p">=</span> <span class="s2">&#34;alternate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isHTML</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">noUgly</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">permalinkable</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></div><p>Create a new layout in <code>_default/home.manifest.json</code> with the following content:</p>
<pre tabindex="0"><code class="language-gotemplate" data-lang="gotemplate">{
  &#34;name&#34;: &#34;{{ .Site.Title }}&#34;,
  &#34;short_name&#34;: &#34;{{ .Site.Title }}&#34;,
  &#34;start_url&#34;: &#34;.&#34;,
  &#34;display&#34;: &#34;minimal-ui&#34;,
  &#34;background_color&#34;: &#34;#fff&#34;,
  &#34;description&#34;: &#34;{{ .Site.Params.description }}&#34;
}
</code></pre><h2 id="links">Links</h2>
<ul>
<li><a href="https://web.dev/add-manifest/">https://web.dev/add-manifest/</a></li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/website" term="website" label="website"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/><category scheme="https://seb.xn--ho-hia.de/tags/web-app" term="web-app" label="web app"/><category scheme="https://seb.xn--ho-hia.de/tags/manifest" term="manifest" label="manifest"/></entry><entry><title type="html">Bundling with Hugo</title><link href="https://seb.xn--ho-hia.de/posts/hugo-bundles/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/hugo-humans/?utm_source=atom_feed" rel="related" type="text/html" title="humans.txt with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-foaf/?utm_source=atom_feed" rel="related" type="text/html" title="FOAF with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-atom/?utm_source=atom_feed" rel="related" type="text/html" title="Atom Feed with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/hugo-bundles/</id><published>2020-12-28T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p><a href="https://gohugo.io/">Hugo</a> allows <a href="https://gohugo.io/hugo-pipes/bundling/">bundling</a> of assets with several built-in functions:</p>
<pre tabindex="0"><code class="language-gotemplate" data-lang="gotemplate">{{ $normalize := resources.Get &#34;/css/normalize.css&#34; }}
{{ $font := resources.Get &#34;/css/font.css&#34; }}
{{ $header := resources.Get &#34;/css/header.css&#34; }}
{{ $footer := resources.Get &#34;/css/footer.css&#34; }}
{{ $navigation := resources.Get &#34;/css/navigation.css&#34; }}
{{ $navigation_mobile := resources.Get &#34;/css/navigation-mobile.css&#34; }}
{{ $layout := resources.Get &#34;/css/layout.css&#34; }}
{{ $layout_mobile := resources.Get &#34;/css/layout-mobile.css&#34; }}
{{ $syntax := resources.Get &#34;/css/syntax.css&#34; }}
{{ $darkmode := resources.Get &#34;/css/darkmode.css&#34; | resources.Minify | resources.Fingerprint &#34;sha512&#34; }}

{{ $base := slice $normalize $font $header $footer $navigation $layout $syntax | resources.Concat &#34;css/base.css&#34; | resources.Minify | resources.Fingerprint &#34;sha512&#34; }}
{{ $mobile := slice $navigation_mobile $layout_mobile | resources.Concat &#34;css/mobile.css&#34; | resources.Minify | resources.Fingerprint &#34;sha512&#34; }}

&lt;link href=&#34;{{ $base.Permalink }}&#34; integrity=&#34;{{ $base.Data.Integrity }}&#34; media=&#34;screen&#34; rel=&#34;stylesheet&#34;&gt;
&lt;link href=&#34;{{ $mobile.Permalink }}&#34; integrity=&#34;{{ $mobile.Data.Integrity }}&#34; media=&#34;screen and (max-width: 800px)&#34; rel=&#34;stylesheet&#34;&gt;

&lt;link href=&#34;{{ $darkmode.Permalink }}&#34; integrity=&#34;{{ $darkmode.Data.Integrity }}&#34; media=&#34;screen and (prefers-color-scheme: dark)&#34; rel=&#34;stylesheet&#34;&gt;
</code></pre>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/website" term="website" label="website"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/><category scheme="https://seb.xn--ho-hia.de/tags/bundle" term="bundle" label="bundle"/><category scheme="https://seb.xn--ho-hia.de/tags/assets" term="assets" label="assets"/></entry><entry><title type="html">humans.txt with Hugo</title><link href="https://seb.xn--ho-hia.de/posts/hugo-humans/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/hugo-foaf/?utm_source=atom_feed" rel="related" type="text/html" title="FOAF with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/hugo-atom/?utm_source=atom_feed" rel="related" type="text/html" title="Atom Feed with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/hugo-humans/</id><published>2020-12-14T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To publish a <a href="http://humanstxt.org/">humans.txt</a> document with your <a href="https://gohugo.io/">Hugo</a> site, configure a <a href="https://en.wikipedia.org/wiki/Media_type">media type</a> in your <code>config.toml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">mediaTypes</span><span class="p">.</span><span class="s2">&#34;text/plain&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">suffixes</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;txt&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">outputFormats</span><span class="p">.</span><span class="nx">Humans</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Humans&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mediaType</span> <span class="p">=</span> <span class="s2">&#34;text/plain&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">baseName</span> <span class="p">=</span> <span class="s2">&#34;humans&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isPlainText</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">rel</span> <span class="p">=</span> <span class="s2">&#34;alternate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isHTML</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">noUgly</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">permalinkable</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></div><p>Create a new layout in <code>_default/home.humans.txt</code> with the following content:</p>
<pre tabindex="0"><code class="language-gotemplate" data-lang="gotemplate">/* TEAM */
{{ range $.Site.Data.contributors }}
{{ .title }}: {{ .first_name }} {{ .last_name }}
Site: {{ .website }}
{{ end }}
</code></pre>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/website" term="website" label="website"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/><category scheme="https://seb.xn--ho-hia.de/tags/humans.txt" term="humans.txt" label="humans.txt"/></entry><entry><title type="html">FOAF with Hugo</title><link href="https://seb.xn--ho-hia.de/posts/hugo-foaf/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/hugo-atom/?utm_source=atom_feed" rel="related" type="text/html" title="Atom Feed with Hugo"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/hugo-foaf/</id><published>2020-11-30T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To publish a <a href="http://www.foaf-project.org/">FOAF</a> document with your <a href="https://gohugo.io/">Hugo</a> site, configure a <a href="https://en.wikipedia.org/wiki/Media_type">media type</a> in your <code>config.toml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">mediaTypes</span><span class="p">.</span><span class="s2">&#34;application/rdf+xml&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">suffixes</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;rdf&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">outputFormats</span><span class="p">.</span><span class="nx">Foaf</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;FOAF&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mediaType</span> <span class="p">=</span> <span class="s2">&#34;application/rdf+xml&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">baseName</span> <span class="p">=</span> <span class="s2">&#34;foaf&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isPlainText</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">rel</span> <span class="p">=</span> <span class="s2">&#34;alternate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isHTML</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">noUgly</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">permalinkable</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></div><p>Create a new layout in <code>_default/home.foaf.rdf</code> with the following content:</p>
<pre tabindex="0"><code class="language-gotemplate" data-lang="gotemplate">&lt;rdf:RDF xmlns:rdf=&#34;http://www.w3.org/1999/02/22-rdf-syntax-ns#&#34; xmlns:rdfs=&#34;http://www.w3.org/2000/01/rdf-schema#&#34; xmlns:foaf=&#34;http://xmlns.com/foaf/0.1/&#34;&gt;
  &lt;foaf:PersonalProfileDocument rdf:about=&#34;&#34;&gt;
    &lt;foaf:maker rdf:resource=&#34;#me&#34; /&gt;
    &lt;foaf:primaryTopic rdf:resource=&#34;{{ .Site.Title }}&#34; /&gt;
  &lt;/foaf:PersonalProfileDocument&gt;

  &lt;foaf:Project rdf:ID=&#34;{{ .Site.Title }}&#34;&gt;
    &lt;foaf:name&gt;{{ .Site.Title }}&lt;/foaf:name&gt;
    &lt;foaf:homepage rdf:resource=&#34;{{ .Site.BaseURL }}&#34; /&gt;
  &lt;/foaf:Project&gt;

  {{ range $.Site.Data.contributors }}
  &lt;foaf:Person rdf:ID=&#34;{{ .id }}&#34;&gt;
    &lt;foaf:name&gt;{{ .first_name }} {{ .last_name }}&lt;/foaf:name&gt;
    &lt;foaf:title&gt;{{ .title }}&lt;/foaf:title&gt;
    &lt;foaf:givenname&gt;{{ .first_name }}&lt;/foaf:givenname&gt;
    &lt;foaf:family_name&gt;{{ .last_name }}&lt;/foaf:family_name&gt;
    &lt;foaf:mbox rdf:resource=&#34;mailto:{{ .email }}&#34; /&gt;
    &lt;foaf:homepage rdf:resource=&#34;{{ .website }}&#34; /&gt;
  &lt;/foaf:Person&gt;
  {{ end }}
&lt;/rdf:RDF&gt;
</code></pre>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/website" term="website" label="website"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/><category scheme="https://seb.xn--ho-hia.de/tags/foaf" term="foaf" label="foaf"/></entry><entry><title type="html">Atom Feed with Hugo</title><link href="https://seb.xn--ho-hia.de/posts/hugo-atom/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/hugo-atom/</id><published>2020-11-16T00:00:00+00:00</published><updated>2023-01-06T16:40:24+01:00</updated><content type="html"><![CDATA[<p>To publish Atom feeds for your <a href="https://gohugo.io/">Hugo</a> site, configure a <a href="https://en.wikipedia.org/wiki/Media_type">media type</a> in your <code>config.toml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">mediaTypes</span><span class="p">.</span><span class="s2">&#34;application/atom+xml&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">suffixes</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;xml&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">outputFormats</span><span class="p">.</span><span class="nx">Atom</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;Atom&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mediaType</span> <span class="p">=</span> <span class="s2">&#34;application/atom+xml&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">baseName</span> <span class="p">=</span> <span class="s2">&#34;atom&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isPlainText</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">rel</span> <span class="p">=</span> <span class="s2">&#34;alternate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isHTML</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="nx">noUgly</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">permalinkable</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></div><p>Create a new layout in <code>_default/list.atom.xml</code> with the following content:</p>
<pre tabindex="0"><code class="language-gotemplate" data-lang="gotemplate">{{ printf `&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;` | safeHTML }}
&lt;feed xmlns=&#34;http://www.w3.org/2005/Atom&#34;{{ with site.LanguageCode }} xml:lang=&#34;{{ . }}&#34;{{ end }}&gt;
    &lt;generator uri=&#34;https://gohugo.io/&#34; version=&#34;{{ hugo.Version }}&#34;&gt;Hugo&lt;/generator&gt;
    {{- $title := site.Title -}}
    {{- with .Title -}}
        {{- if (not (eq . site.Title)) -}}
            {{- $title = printf `%s %s %s` . (i18n &#34;feed_title_on&#34; | default &#34;on&#34;) site.Title -}}
        {{- end -}}
    {{- end -}}
    {{- if .IsTranslated -}}
        {{ $title = printf &#34;%s (%s)&#34; $title (index site.Data.i18n.languages .Lang) }}
    {{- end -}}
    {{ printf `&lt;title type=&#34;html&#34;&gt;&lt;![CDATA[%s]]&gt;&lt;/title&gt;` $title | safeHTML }}
    {{ with (or (.Param &#34;subtitle&#34;) (.Param &#34;tagline&#34;)) }}
        {{ printf `&lt;subtitle type=&#34;html&#34;&gt;&lt;![CDATA[%s]]&gt;&lt;/subtitle&gt;` . | safeHTML }}
    {{ end }}
    {{ $output_formats := .OutputFormats }}
    {{ range $output_formats -}}
        {{- $rel := (or (and (eq &#34;atom&#34; (.Name | lower)) &#34;self&#34;) &#34;alternate&#34;) -}}
        {{ with $output_formats.Get .Name }}
            {{ printf `&lt;link href=%q rel=%q type=%q title=%q /&gt;` .Permalink $rel .MediaType.Type .Name | safeHTML }}
        {{- end -}}
    {{- end }}
    {{- range .Translations }}
        {{ $output_formats := .OutputFormats }}
        {{- $lang := .Lang }}
        {{- $langstr := index site.Data.i18n.languages .Lang }}
        {{ range $output_formats -}}
            {{ with $output_formats.Get .Name }}
                {{ printf `&lt;link href=%q rel=&#34;alternate&#34; type=%q hreflang=%q title=&#34;[%s] %s&#34; /&gt;` .Permalink .MediaType.Type $lang $langstr .Name | safeHTML }}
            {{- end -}}
        {{- end }}
    {{- end }}
    &lt;updated&gt;{{ now.Format &#34;2006-01-02T15:04:05-07:00&#34; | safeHTML }}&lt;/updated&gt;
    {{ with site.Copyright }}
        {{- $copyright := replace . &#34;{year}&#34; now.Year -}} {{/* In case the site.copyright uses a special string &#34;{year}&#34; */}}
        {{- $copyright = replace $copyright &#34;&amp;copy;&#34; &#34;©&#34; -}}
        &lt;rights&gt;{{ $copyright | plainify }}&lt;/rights&gt;
    {{- end }}
    {{ with .Param &#34;feed&#34; }}
        {{/* For this to work, the $icon file should be present in the assets/ directory */}}
        {{- $icon := .icon | default &#34;icon.svg&#34; -}}
        {{- with resources.Get $icon -}}
            &lt;icon&gt;{{ (. | fingerprint).Permalink }}&lt;/icon&gt;
        {{- end }}

        {{/* For this to work, the $logo file should be present in the assets/ directory */}}
        {{- $logo := .logo | default &#34;logo.svg&#34; -}}
        {{- with resources.Get $logo -}}
            &lt;logo&gt;{{ (. | fingerprint).Permalink }}&lt;/logo&gt;
        {{- end }}
    {{ end }}
    {{ with site.Author.name -}}
        &lt;author&gt;
            &lt;name&gt;{{ . }}&lt;/name&gt;
            {{ with site.Author.email }}
                &lt;email&gt;{{ . }}&lt;/email&gt;
            {{ end -}}
        &lt;/author&gt;
    {{- end }}
    {{ with site.Params.id }}
        &lt;id&gt;{{ . | plainify }}&lt;/id&gt;
    {{ else }}
        &lt;id&gt;{{ .Permalink }}&lt;/id&gt;
    {{ end }}
    {{- $limit := (cond (le site.Config.Services.RSS.Limit 0) 65536 site.Config.Services.RSS.Limit) }}
    {{- $feed_sections := site.Params.feedSections | default site.Params.mainSections -}}
    {{/* Range through only the pages with a Type in $feed_sections. */}}
    {{- $pages := where .RegularPages &#34;Type&#34; &#34;in&#34; $feed_sections -}}
    {{- if (eq .Kind &#34;home&#34;) -}}
        {{- $pages = where site.RegularPages &#34;Type&#34; &#34;in&#34; $feed_sections -}}
    {{- end -}}
    {{/* Remove the pages that have the disable_feed parameter set to true. */}}
    {{- $pages = where $pages &#34;.Params.disable_feed&#34; &#34;!=&#34; true -}}
    {{- range first $limit $pages }}
        {{ $page := . }}
        &lt;entry&gt;
            {{ printf `&lt;title type=&#34;html&#34;&gt;&lt;![CDATA[%s]]&gt;&lt;/title&gt;` .Title | safeHTML }}
            &lt;link href=&#34;{{ .Permalink }}?utm_source=atom_feed&#34; rel=&#34;alternate&#34; type=&#34;text/html&#34; /&gt;
            {{- range .Translations }}
                {{- $link := printf &#34;%s?utm_source=atom_feed&#34; .Permalink | safeHTML }}
                {{- printf `&lt;link href=%q rel=&#34;alternate&#34; type=&#34;text/html&#34; hreflang=%q /&gt;` $link .Lang | safeHTML }}
            {{- end }}
            {{/* rel=related: See https://validator.w3.org/feed/docs/atom.html#link */}}
            {{- range first 5 (site.RegularPages.Related .) }}
                &lt;link href=&#34;{{ .Permalink }}?utm_source=atom_feed&#34; rel=&#34;related&#34; type=&#34;text/html&#34; title=&#34;{{ .Title }}&#34; /&gt;
            {{- end }}
            {{ with .Params.id }}
                &lt;id&gt;{{ . | plainify }}&lt;/id&gt;
            {{ else }}
                &lt;id&gt;{{ .Permalink }}&lt;/id&gt;
            {{ end }}
            {{ with .Params.author -}}
                {{- range . -}} &lt;!-- Assuming the author front-matter to be a list --&gt;
                    &lt;author&gt;
                        &lt;name&gt;{{ . }}&lt;/name&gt;
                    &lt;/author&gt;
                {{- end -}}
            {{- end }}
            &lt;published&gt;{{ .Date.Format &#34;2006-01-02T15:04:05-07:00&#34; | safeHTML }}&lt;/published&gt;
            &lt;updated&gt;{{ .Lastmod.Format &#34;2006-01-02T15:04:05-07:00&#34; | safeHTML }}&lt;/updated&gt;
            {{ $description1 := .Description | default &#34;&#34; }}
            {{ $description := (cond (eq &#34;&#34; $description1) &#34;&#34; (printf &#34;&lt;blockquote&gt;%s&lt;/blockquote&gt;&#34; ($description1 | markdownify))) }}
            {{ printf `&lt;content type=&#34;html&#34;&gt;&lt;![CDATA[%s%s]]&gt;&lt;/content&gt;` $description .Content | safeHTML }}
            {{ with site.Taxonomies }}
                {{ range $taxo,$_ := . }} &lt;!-- Defaults taxos: &#34;tags&#34;, &#34;categories&#34; --&gt;
                    {{ with $page.Param $taxo }}
                        {{ $taxo_list := . }} &lt;!-- $taxo_list will be the tags/categories list --&gt;
                        {{ with site.GetPage (printf &#34;/%s&#34; $taxo) }}
                            {{ $taxonomy_page := . }}
                            {{ range $taxo_list }} &lt;!-- Below, assuming pretty URLs --&gt;
                                &lt;category scheme=&#34;{{ printf &#34;%s%s&#34; $taxonomy_page.Permalink (. | urlize) }}&#34; term=&#34;{{ (. | urlize) }}&#34; label=&#34;{{ . }}&#34; /&gt;
                            {{ end }}
                        {{ end }}
                    {{ end }}
                {{ end }}
            {{ end }}
        &lt;/entry&gt;
    {{ end }}
&lt;/feed&gt;
</code></pre>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/categories/website" term="website" label="website"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/><category scheme="https://seb.xn--ho-hia.de/tags/atom" term="atom" label="atom"/></entry><entry><title type="html">Send toots with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-send-toot/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-send-email/?utm_source=atom_feed" rel="related" type="text/html" title="Send emails with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-upload-release-assets/?utm_source=atom_feed" rel="related" type="text/html" title="Upload release assets with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-release/?utm_source=atom_feed" rel="related" type="text/html" title="Create GitHub releases with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/github-actions-send-toot/</id><published>2020-11-16T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/rzr/fediverse-action">rzr/fediverse-action</a> action allows to send a <a href="https://joinmastodon.org/">toot</a> in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;NAME&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish Toot</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">rzr/fediverse-action@master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">access-token</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MASTODON_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">message</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;MESSAGE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MASTODON_SERVER }}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;MESSAGE&gt;</code>: Message for the toot.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/toot" term="toot" label="toot"/><category scheme="https://seb.xn--ho-hia.de/tags/mastodon" term="mastodon" label="mastodon"/></entry><entry><title type="html">Send emails with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-send-email/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-upload-release-assets/?utm_source=atom_feed" rel="related" type="text/html" title="Upload release assets with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-release/?utm_source=atom_feed" rel="related" type="text/html" title="Create GitHub releases with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/github-actions-send-email/</id><published>2020-11-02T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/dawidd6/action-send-mail">dawidd6/action-send-mail</a> action allows to send an email in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Send mail</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">dawidd6/action-send-mail@v3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">server_address</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MAIL_SERVER }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">server_port</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MAIL_PORT }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MAIL_USERNAME }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MAIL_PASSWORD }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">subject</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;SUBJECT&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">body</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;BODY&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">to</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MAIL_RECIPIENT }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">from</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.MAIL_SENDER }}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;SUBJECT&gt;</code>: Subject for the email.</li>
<li><code>&lt;BODY&gt;</code>: Body for the email.</li>
</ul>
<p>Create appropriate secrets in your organization or project. In case you are using an organization, but different mailing lists, define <code>MAIL_RECIPIENT</code> for each project.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/email" term="email" label="email"/></entry><entry><title type="html">Upload release assets with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-upload-release-assets/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-release/?utm_source=atom_feed" rel="related" type="text/html" title="Create GitHub releases with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Java version with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/github-actions-upload-release-assets/</id><published>2020-10-19T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/actions/upload-release-asset">actions/upload-release-asset</a> action allows to upload a <a href="https://developer.github.com/v3/repos/releases/#upload-a-release-asset">release artifact</a> in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload Release Asset</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">upload_release_asset</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-release-asset@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">GITHUB_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.GITHUB_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">upload_url</span><span class="p">:</span><span class="w"> </span><span class="l">${{ steps.create_release.outputs.upload_url }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">asset_path</span><span class="p">:</span><span class="w"> </span><span class="l">./some/path/to/file.zip</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">asset_name</span><span class="p">:</span><span class="w"> </span><span class="l">public-name-for-file.zip</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">asset_content_type</span><span class="p">:</span><span class="w"> </span><span class="l">application/zip</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/release" term="release" label="release"/><category scheme="https://seb.xn--ho-hia.de/tags/asset" term="asset" label="asset"/><category scheme="https://seb.xn--ho-hia.de/tags/upload" term="upload" label="upload"/></entry><entry><title type="html">Create GitHub releases with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-release/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="related" type="text/html" title="Publish Hugo site with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Java version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/?utm_source=atom_feed" rel="related" type="text/html" title="Create Timestamp with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/github-actions-create-release/</id><published>2020-10-05T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/actions/create-release">actions/create-release</a> action allows to create a new <a href="https://help.github.com/en/github/administering-a-repository/about-releases">GitHub releases</a> in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create Release</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/create-release@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="nt">GITHUB_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.GITHUB_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="nt">tag_name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;TAG&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="nt">release_name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RELEASE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="nt">draft</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="nt">prerelease</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="nt">body</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">           Your release text here
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">           Some code block:
</span></span></span><span class="line"><span class="cl"><span class="sd">           ```yaml
</span></span></span><span class="line"><span class="cl"><span class="sd">           yaml:
</span></span></span><span class="line"><span class="cl"><span class="sd">             inside:
</span></span></span><span class="line"><span class="cl"><span class="sd">               of:
</span></span></span><span class="line"><span class="cl"><span class="sd">                 another: yaml
</span></span></span><span class="line"><span class="cl"><span class="sd">           ```</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;TAG&gt;</code>: The Git tag to create.</li>
<li><code>&lt;RELEASE&gt;</code>: The release name to use.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/release" term="release" label="release"/></entry><entry><title type="html">Publish Hugo site with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="related" type="text/html" title="Cache Maven artifacts with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Java version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/?utm_source=atom_feed" rel="related" type="text/html" title="Create Timestamp with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><id>https://seb.xn--ho-hia.de/posts/github-actions-publish-hugo-site/</id><published>2020-09-21T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/peaceiris/actions-gh-pages">peaceiris/actions-gh-pages</a> action allows to publish a <a href="https://gohugo.io/">Hugo</a> site in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy Website</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-gh-pages@v3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">github_token</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.GITHUB_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">publish_dir</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PUBLISH_DIR&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">force_orphan</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cname</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;CNAME&gt;</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;PUBLISH_DIR&gt;</code>: The file system location of the built site.</li>
<li><code>&lt;CNAME&gt;</code>: The <code>CNAME</code> of your custom domain.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/publish" term="publish" label="publish"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/></entry><entry><title type="html">Cache Maven artifacts with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Hugo version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Java version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/?utm_source=atom_feed" rel="related" type="text/html" title="Create Timestamp with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><id>https://seb.xn--ho-hia.de/posts/github-actions-cache-maven-artifacts/</id><published>2020-09-07T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/peaceiris/actions-hugo">actions/cache</a> action allows to cache artifacts in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Cache Maven artifacts</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/cache@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">~/.m2/repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">${{ runner.os }}-maven-${{ hashFiles(&#39;**/pom.xml&#39;) }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">restore-keys</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">            ${{ runner.os }}-maven-</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/cache" term="cache" label="cache"/><category scheme="https://seb.xn--ho-hia.de/tags/maven" term="maven" label="maven"/></entry><entry><title type="html">Use a specific Hugo version with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="related" type="text/html" title="Use a specific Java version with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/?utm_source=atom_feed" rel="related" type="text/html" title="Create Timestamp with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><id>https://seb.xn--ho-hia.de/posts/github-actions-specify-hugo-version/</id><published>2020-08-24T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/peaceiris/actions-hugo">actions-hugo</a> action allows to use a specific <a href="https://gohugo.io/">Hugo</a> version in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup hugo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-hugo@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">hugo-version</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;HUGO_VERSION&gt;</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;HUGO_VERSION&gt;</code>: The <a href="https://github.com/gohugoio/hugo/releases">released versions</a> or use <code>latest</code> to always use the latest version of Hugo.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/hugo" term="hugo" label="hugo"/></entry><entry><title type="html">Use a specific Java version with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/?utm_source=atom_feed" rel="related" type="text/html" title="Create Timestamp with GitHub Actions"/><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="related" type="text/html" title="GitLab the Git Distributor"/><id>https://seb.xn--ho-hia.de/posts/github-actions-specify-java-version/</id><published>2020-08-10T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://github.com/actions/setup-java">setup-java</a> action allows to use a specific Java version in your <a href="https://github.com/features/actions">GitHub Action</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up JDK &lt;JDK_VERSION&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-java@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">java-version</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;JDK_VERSION&gt;</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;JDK_VERSION&gt;</code>: The required Java version for your project.</li>
</ul>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/java" term="java" label="java"/></entry><entry><title type="html">XDG Base Directory Specification</title><link href="https://seb.xn--ho-hia.de/posts/xdg-dot-files/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/xdg-dot-files/</id><published>2020-07-27T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>The <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG Base Directory Specification</a> has been around for a while, yet not every software has adopted it yet. Here is an incomplete list of fixes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># use existing env variables or define new</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$XDG_CACHE_HOME</span><span class="s2">&#34;</span>  <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">export</span> <span class="nv">XDG_CACHE_HOME</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$XDG_CONFIG_DIRS</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">export</span> <span class="nv">XDG_CONFIG_DIRS</span><span class="o">=</span><span class="s2">&#34;/etc/xdg&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$XDG_CONFIG_HOME</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">export</span> <span class="nv">XDG_CONFIG_HOME</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$XDG_DATA_DIRS</span><span class="s2">&#34;</span>   <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">export</span> <span class="nv">XDG_DATA_DIRS</span><span class="o">=</span><span class="s2">&#34;/usr/local/share:/usr/share&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$XDG_DATA_HOME</span><span class="s2">&#34;</span>   <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">export</span> <span class="nv">XDG_DATA_HOME</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/share&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># gradle</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">GRADLE_USER_HOME</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$XDG_DATA_HOME</span><span class="s2">/gradle&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># httpie</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">HTTPIE_CONFIG_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$XDG_CONFIG_HOME</span><span class="s2">/httpie&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># npm</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">NPM_CONFIG_USERCONFIG</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$XDG_CONFIG_HOME</span><span class="s2">/npm/npmrc&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">npm_config_cache</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$XDG_CACHE_HOME</span><span class="s2">/npm&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># password-store</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">PASSWORD_STORE_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$XDG_DATA_HOME</span><span class="s2">/password-store&#34;</span>
</span></span></code></pre></div><p>To make your own software XDG-aware, consider using the <a href="https://github.com/dirs-dev">dirs-dev</a> or <a href="https://github.com/shibukawa/configdir">configdir</a> libraries.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/dotfiles" term="dotfiles" label="dotfiles"/><category scheme="https://seb.xn--ho-hia.de/tags/xdg" term="xdg" label="xdg"/></entry><entry><title type="html">GitLab the Git Distributor</title><link href="https://seb.xn--ho-hia.de/posts/gitlab-distributor/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://seb.xn--ho-hia.de/posts/short-git-clones/?utm_source=atom_feed" rel="related" type="text/html" title="Short Git Clones"/><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/?utm_source=atom_feed" rel="related" type="text/html" title="Create Timestamp with GitHub Actions"/><id>https://seb.xn--ho-hia.de/posts/gitlab-distributor/</id><published>2020-07-13T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p><a href="https://git-scm.com/">Git</a> at its core is a decentralized version control system. Yet many people are relying on a single central server (github.com at the time of this writing) to share their work with others. <a href="https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows">Pro Git</a> rightfully mentions it at first position in its chapter about distributed workflows because using a central server is usually the simplest approach to sharing code.</p>
<p>While the central server approach is easy to use, it might not work in all scenarios:</p>
<ol>
<li>An enterprise wants to share internal code as part of an open source project. All development is happening internally, and the public mirror gets an occasional update once in a while.</li>
<li>To protect against outages of the central server, mirrors should be created and be kept up-to-date.</li>
</ol>
<p>In case of the first scenario, tools like <a href="https://github.com/google/copybara">copybara</a>, <a href="https://github.com/repoSpanner/repoSpanner">repoSpanner</a>, or <a href="https://github.com/google/distributed-git-forks">distributed-Git-forks</a> offer a wide range of features to cover most details.</p>
<p>The second scenario can be solved manually with tools like <a href="https://github.com/muesli/gitomatic">gitomatic</a> or automatically with <a href="https://gitlab.com">GitLab</a>&rsquo;s <a href="https://docs.gitlab.com/ee/user/project/repository/repository_mirroring.html">mirror feature</a> quite easy. GitLab allows to create a single pull-mirror and multiple push-mirrors. Therefore, it can be used to pull from your central server and push into all mirrors.</p>
<p><strong>NOTE</strong>: This feature was previously available in the free tier but has now moved to GitLab Ultimate.</p>
<pre tabindex="0"><code>               +----------------+               
               |     GitHub     |               
               +----------------+               
                        ^                       
                        |                       
                        |                       
               +----------------+               
         +-----|     GitLab     |------+        
         |     +----------------+      |        
         |                             |        
         |                             |        
         v                             v        
+----------------+            +----------------+
|    Codeberg    |            |    BitBucket   |
+----------------+            +----------------+
</code></pre><p>To create such a setup, follow these steps:</p>
<ol>
<li>Go to <code>Settings &gt; Repository</code> and expand <code>Mirroring repositories</code>
<img src="/images/gitlab-mirror-settings.png" alt="Code Flow"></li>
<li>Enter the URL of your central Git repository as the pull source, for example <code>https://github.com/metio/ilo.git</code>
<img src="/images/gitlab-pull-mirror.png" alt="Code Flow"></li>
<li>Enter one push target for each mirror. Since pushing usually requires authentication, verify that the URL of the mirror contains a username, for example <code>https://YOUR_USER@codeberg.org/metio.wtf/ilo.git</code>. Add an access token for each mirror and select <code>password</code> as authentication method.
<img src="/images/gitlab-push-mirror.png" alt="Code Flow"></li>
</ol>
<p>In case you prefer SSH keys over HTTP access tokens, just select <code>SSH public key</code> as authentication method and verify that your key is both saved in GitLab and all mirrors.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/configuration" term="configuration" label="configuration"/><category scheme="https://seb.xn--ho-hia.de/tags/git" term="git" label="git"/><category scheme="https://seb.xn--ho-hia.de/tags/gitlab" term="gitlab" label="gitlab"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/codeberg" term="codeberg" label="codeberg"/><category scheme="https://seb.xn--ho-hia.de/tags/bitbucket" term="bitbucket" label="bitbucket"/><category scheme="https://seb.xn--ho-hia.de/tags/repo.or.cz" term="repo.or.cz" label="repo.or.cz"/></entry><entry><title type="html">Makefile Help</title><link href="https://seb.xn--ho-hia.de/posts/makefile-help/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/makefile-help/</id><published>2020-06-29T00:00:00+00:00</published><updated>2023-01-06T16:22:24+01:00</updated><content type="html"><![CDATA[<p>Use the following <a href="https://www.perl.org/">Perl</a> snippet to automatically generate help output for your <code>Makefile</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">GREEN</span>  <span class="o">:=</span> <span class="k">$(</span>shell tput -Txterm setaf 2<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">WHITE</span>  <span class="o">:=</span> <span class="k">$(</span>shell tput -Txterm setaf 7<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">YELLOW</span> <span class="o">:=</span> <span class="k">$(</span>shell tput -Txterm setaf 3<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">RESET</span>  <span class="o">:=</span> <span class="k">$(</span>shell tput -Txterm sgr0<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">HELP_FUN</span> <span class="o">=</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    %help<span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">while</span><span class="o">(</span>&lt;&gt;<span class="o">)</span> <span class="o">{</span> push @<span class="o">{</span><span class="nv">$$</span>help<span class="o">{</span><span class="nv">$$</span><span class="m">2</span> // <span class="s1">&#39;targets&#39;</span><span class="o">}}</span>, <span class="o">[</span><span class="nv">$$</span>1, <span class="nv">$$</span>3<span class="o">]</span> <span class="k">if</span> /^<span class="o">([</span>a-zA-Z<span class="se">\-</span><span class="o">]</span>+<span class="o">)</span><span class="se">\s</span>*:.*<span class="se">\#\#</span><span class="o">(</span>?:@<span class="o">([</span>a-zA-Z<span class="se">\-</span><span class="o">]</span>+<span class="o">))</span>?<span class="se">\s</span><span class="o">(</span>.*<span class="o">)</span><span class="nv">$$</span>/ <span class="o">}</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    print <span class="s2">&#34;usage: make [target]\n\n&#34;</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="o">(</span>sort keys %help<span class="o">)</span> <span class="o">{</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    print <span class="s2">&#34;</span><span class="si">${</span><span class="nv">WHITE</span><span class="si">}</span><span class="nv">$$</span><span class="s2">_:</span><span class="si">${</span><span class="nv">RESET</span><span class="si">}</span><span class="s2">\n&#34;</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="o">(</span>@<span class="o">{</span><span class="nv">$$</span>help<span class="o">{</span><span class="nv">$$</span>_<span class="o">}})</span> <span class="o">{</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">$$sep</span> <span class="o">=</span> <span class="s2">&#34; &#34;</span> x <span class="o">(</span><span class="m">32</span> - length <span class="nv">$$</span>_-&gt;<span class="o">[</span>0<span class="o">])</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    print <span class="s2">&#34;  </span><span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span><span class="nv">$$</span><span class="s2">_-&gt;[0]</span><span class="si">${</span><span class="nv">RESET</span><span class="si">}</span><span class="nv">$$</span><span class="s2">sep</span><span class="si">${</span><span class="nv">GREEN</span><span class="si">}</span><span class="nv">$$</span><span class="s2">_-&gt;[1]</span><span class="si">${</span><span class="nv">RESET</span><span class="si">}</span><span class="s2">\n&#34;</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="o">}</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    print <span class="s2">&#34;\n&#34;</span><span class="p">;</span> <span class="o">}</span>
</span></span></code></pre></div><p>To use <code>HELP_FUN</code>, add the following <code>help</code> target to the same <code>Makefile</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">.DEFAULT_GOAL</span> <span class="o">:=</span> <span class="nb">help</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">help</span>
</span></span><span class="line"><span class="cl"><span class="nf">help</span><span class="o">:</span> <span class="c">##@other Show this help
</span></span></span><span class="line"><span class="cl">	@perl -e <span class="s1">&#39;$(HELP_FUN)&#39;</span> <span class="k">$(</span>MAKEFILE_LIST<span class="k">)</span>
</span></span></code></pre></div><p>Each target in the <code>Makefile</code> is marked as <a href="https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html">phony</a> to signal that those targets are not actually files that are generated as part of your build process. The optional description of a target can be placed after the <code>##@</code> prefix. The first word represents the group of a target and everything that follows is the description of a target. All targets should be formatted just like the <code>help</code> target:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">compile</span>
</span></span><span class="line"><span class="cl"><span class="nf">compile</span><span class="o">:</span> <span class="c">##@hacking Compile your code
</span></span></span><span class="line"><span class="cl">	&lt;compile some code&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">test</span>
</span></span><span class="line"><span class="cl"><span class="nf">test</span><span class="o">:</span> <span class="c">##@hacking Test your code
</span></span></span><span class="line"><span class="cl">	&lt;<span class="nb">test</span> some code&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">sign</span>-<span class="n">cla</span>
</span></span><span class="line"><span class="cl"><span class="nf">sign-cla</span><span class="o">:</span> <span class="c">##@contrib Sign the contributor license agreement
</span></span></span><span class="line"><span class="cl">	&lt;sign some file&gt;
</span></span></code></pre></div><p>Once in place, you can either use <code>make</code> without any argument to call the <code>help</code> target or use <code>make help</code> to see the generated output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ make
</span></span><span class="line"><span class="cl">usage: make <span class="o">[</span>target<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">contrib:
</span></span><span class="line"><span class="cl">  sign-cla            Sign the contributor license agreement
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">hacking:
</span></span><span class="line"><span class="cl">  compile             Compile your code
</span></span><span class="line"><span class="cl">  <span class="nb">test</span>                Test your code
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">other:
</span></span><span class="line"><span class="cl">  <span class="nb">help</span>                Show this <span class="nb">help</span>
</span></span></code></pre></div>]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/make" term="make" label="make"/><category scheme="https://seb.xn--ho-hia.de/tags/makefile" term="makefile" label="Makefile"/><category scheme="https://seb.xn--ho-hia.de/tags/help" term="help" label="help"/><category scheme="https://seb.xn--ho-hia.de/tags/perl" term="perl" label="perl"/></entry><entry><title type="html">Short Git Clones</title><link href="https://seb.xn--ho-hia.de/posts/short-git-clones/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/short-git-clones/</id><published>2020-06-15T00:00:00+00:00</published><updated>2023-01-06T17:32:06+01:00</updated><content type="html"><![CDATA[<p>In case you don&rsquo;t want to write <code>git clone git@github.com:orga/repo.git</code> all the time, consider using a custom SSH configuration (<code>~/.ssh/config</code>) like this:</p>
<pre tabindex="0"><code>Host github
    HostName github.com
    User git
    IdentityFile ~/.ssh/&lt;KEY-FOR-GITHUB&gt;

Host gitlab
    HostName gitlab.com
    User git
    IdentityFile ~/.ssh/&lt;KEY-FOR-GITLAB&gt;

Host bitbucket
    HostName bitbucket.org
    User git
    IdentityFile ~/.ssh/&lt;KEY-FOR-BITBUCKET&gt;

Host codeberg
    HostName codeberg.org
    User git
    IdentityFile ~/.ssh/&lt;KEY-FOR-CODEBERG&gt;
</code></pre><p>Once configured, you can now write:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ git clone github:orga/repo
</span></span><span class="line"><span class="cl">$ git clone gitlab:orga/repo
</span></span><span class="line"><span class="cl">$ git clone bitbucket:orga/repo
</span></span><span class="line"><span class="cl">$ git clone codeberg:orga/repo
</span></span></code></pre></div><p>In case you are working with many repositories inside a single organization, consider adding the following Git configuration (<code>$XDG_CONFIG_HOME/git/config</code> or <code>~/.gitconfig</code>):</p>
<pre tabindex="0"><code>[url &#34;github:orga/&#34;]
  insteadOf = orga:
[url &#34;gitlab:orga/&#34;]
  insteadOf = orgl:
[url &#34;bitbucket:orga/&#34;]
  insteadOf = orgb:
[url &#34;codeberg:orga/&#34;]
  insteadOf = orgc:
</code></pre><p>Which allows you to just write:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ git clone orga:repo
</span></span><span class="line"><span class="cl">$ git clone orgl:repo
</span></span><span class="line"><span class="cl">$ git clone orgb:repo
</span></span><span class="line"><span class="cl">$ git clone orgc:repo
</span></span></code></pre></div><p>Git will substitute the <code>insteadOf</code> values like <code>orga:</code> with the configured <code>url</code> (for example <code>github:orga/</code>). The actual clone URL is <code>github:orga/repo</code> at this point, which can be used by Git together with the SSH configuration mentioned above to clone repositories.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/git" term="git" label="git"/><category scheme="https://seb.xn--ho-hia.de/tags/clone" term="clone" label="clone"/><category scheme="https://seb.xn--ho-hia.de/tags/ssh" term="ssh" label="ssh"/></entry><entry><title type="html">Create Timestamp with GitHub Actions</title><link href="https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://seb.xn--ho-hia.de/posts/github-actions-create-timestamp/</id><published>2020-06-11T00:00:00+00:00</published><updated>2023-01-06T15:27:21+01:00</updated><content type="html"><![CDATA[<p>In case you are into <a href="https://calver.org/">calver</a> or have another reason to create a timestamp with <a href="https://github.com/features/actions">GitHub Actions</a>, do the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;PIPELINE&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;RUN_ON&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create release version</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;ID&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo &#34;::set-output name=&lt;NAME&gt;::$(date +&#39;%Y.%m.%d-%H%M%S&#39;)&#34;</span><span class="w">
</span></span></span></code></pre></div><ul>
<li><code>&lt;PIPELINE&gt;</code>: The name of your pipeline.</li>
<li><code>&lt;RUN_ON&gt;</code>: The runner to use, see GitHub&rsquo;s own <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">documentation</a> for possible values.</li>
<li><code>&lt;ID&gt;</code>: The unique ID of the timestamp step.</li>
<li><code>&lt;NAME&gt;</code>: The name of the created timestamp.</li>
</ul>
<p>The special syntax <code>::set-output name=&lt;NAME&gt;::</code> declares that the output of the command (<code>echo</code>) should be saved in a variable called <code>&lt;NAME&gt;</code>. Together with the <code>&lt;ID&gt;</code> of the pipeline step, this value can be referenced with the expression <code>${{ steps.&lt;ID&gt;.outputs.&lt;NAME&gt; }}</code> in the following steps of your pipeline.</p>
]]></content><category scheme="https://seb.xn--ho-hia.de/categories/devops" term="devops" label="devops"/><category scheme="https://seb.xn--ho-hia.de/categories/cd" term="cd" label="cd"/><category scheme="https://seb.xn--ho-hia.de/categories/snippet" term="snippet" label="snippet"/><category scheme="https://seb.xn--ho-hia.de/tags/github" term="github" label="github"/><category scheme="https://seb.xn--ho-hia.de/tags/github-actions" term="github-actions" label="github actions"/><category scheme="https://seb.xn--ho-hia.de/tags/timestamp" term="timestamp" label="timestamp"/></entry></feed>