aboutsummaryrefslogtreecommitdiff
path: root/src/content/tils/2020/12
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2024-11-18 08:21:58 -0300
committerEuAndreh <eu@euandre.org>2024-11-18 08:44:57 -0300
commit960e4410f76801356ebd42801c914b2910a302a7 (patch)
tree615d379416f72956d0c1666c63ce062859041fbe /src/content/tils/2020/12
parentRemove jekyll infrastructure setup (diff)
downloadeuandre.org-960e4410f76801356ebd42801c914b2910a302a7.tar.gz
euandre.org-960e4410f76801356ebd42801c914b2910a302a7.tar.xz
v0 migration to mkwbHEADmain
Diffstat (limited to 'src/content/tils/2020/12')
-rw-r--r--src/content/tils/2020/12/15/shellcheck-repo.adoc171
-rw-r--r--src/content/tils/2020/12/29/svg.adoc134
2 files changed, 305 insertions, 0 deletions
diff --git a/src/content/tils/2020/12/15/shellcheck-repo.adoc b/src/content/tils/2020/12/15/shellcheck-repo.adoc
new file mode 100644
index 0000000..71d10a3
--- /dev/null
+++ b/src/content/tils/2020/12/15/shellcheck-repo.adoc
@@ -0,0 +1,171 @@
+---
+
+title: 'Awk snippet: ShellCheck all scripts in a repository'
+
+date: 2020-12-15
+
+updated_at: 2020-12-16
+
+layout: post
+
+lang: en
+
+ref: awk-snippet-shellcheck-all-scripts-in-a-repository
+
+eu_categories: shell
+
+---
+
+Inspired by Fred Herbert's "[Awk in 20 Minutes][awk-20min]", here's a problem I
+just solved with a line of Awk: run ShellCheck in all scripts of a repository.
+
+In my repositories I usually have Bash and POSIX scripts, which I want to keep
+tidy with [ShellCheck][shellcheck]. Here's the first version of
+`assert-shellcheck.sh`:
+
+```shell
+#!/bin/sh -eux
+
+find . -type f -name '*.sh' -print0 | xargs -0 shellcheck
+```
+
+This is the type of script that I copy around to all repositories, and I want it
+to be capable of working on any repository, without requiring a list of files to
+run ShellCheck on.
+
+This first version worked fine, as all my scripts had the '.sh' ending. But I
+recently added some scripts without any extension, so `assert-shellcheck.sh`
+called for a second version. The first attempt was to try grepping the shebang
+line:
+
+```shell
+$ grep '^#!/' assert-shellcheck.sh
+#!/usr/sh
+```
+
+Good, we have a grep pattern on the first try. Let's try to find all the
+matching files:
+
+```shell
+$ find . -type f | xargs grep -l '^#!/'
+./TODOs.org
+./.git/hooks/pre-commit.sample
+./.git/hooks/pre-push.sample
+./.git/hooks/pre-merge-commit.sample
+./.git/hooks/fsmonitor-watchman.sample
+./.git/hooks/pre-applypatch.sample
+./.git/hooks/pre-push
+./.git/hooks/prepare-commit-msg.sample
+./.git/hooks/commit-msg.sample
+./.git/hooks/post-update.sample
+./.git/hooks/pre-receive.sample
+./.git/hooks/applypatch-msg.sample
+./.git/hooks/pre-rebase.sample
+./.git/hooks/update.sample
+./build-aux/with-guile-env.in
+./build-aux/test-driver
+./build-aux/missing
+./build-aux/install-sh
+./build-aux/install-sh~
+./bootstrap
+./scripts/assert-todos.sh
+./scripts/songbooks
+./scripts/compile-readme.sh
+./scripts/ci-build.sh
+./scripts/generate-tasks-and-bugs.sh
+./scripts/songbooks.in
+./scripts/with-container.sh
+./scripts/assert-shellcheck.sh
+```
+
+This approach has a problem, though: it includes files ignored by Git, such as
+`builld-aux/install-sh~`, and even goes into the `.git/` directory and finds
+sample hooks in `.git/hooks/*`.
+
+To list the files that Git is tracking we'll try `git ls-files`:
+
+```shell
+$ git ls-files | xargs grep -l '^#!/'
+TODOs.org
+bootstrap
+build-aux/with-guile-env.in
+old/scripts/assert-docs-spelling.sh
+old/scripts/build-site.sh
+old/scripts/builder.bats.sh
+scripts/assert-shellcheck.sh
+scripts/assert-todos.sh
+scripts/ci-build.sh
+scripts/compile-readme.sh
+scripts/generate-tasks-and-bugs.sh
+scripts/songbooks.in
+scripts/with-container.sh
+```
+
+It looks to be almost there, but the `TODOs.org` entry shows a flaw in it: grep
+is looking for a `'^#!/'` pattern on any part of the file. In my case,
+`TODOs.org` had a snippet in the middle of the file where a line started with
+`#!/bin/sh`.
+
+So what we actually want is to match the **first** line against the pattern. We
+could loop through each file, get the first line with `head -n 1` and grep
+against that, but this is starting to look messy. I bet there is another way of
+doing it concisely...
+
+Let's try Awk. I need a way to select the line numbers to replace `head -n 1`,
+and to stop processing the file if the pattern matches. A quick search points me
+to using `FNR` for the former, and `{ nextline }` for the latter. Let's try it:
+
+```shell
+$ git ls-files | xargs awk 'FNR>1 { nextfile } /^#!\// { print FILENAME; nextfile }'
+bootstrap
+build-aux/with-guile-env.in
+old/scripts/assert-docs-spelling.sh
+old/scripts/build-site.sh
+old/scripts/builder.bats.sh
+scripts/assert-shellcheck.sh
+scripts/assert-todos.sh
+scripts/ci-build.sh
+scripts/compile-readme.sh
+scripts/generate-tasks-and-bugs.sh
+scripts/songbooks.in
+scripts/with-container.sh
+```
+
+Great! Only `TODOs.org` is missing, but the script is much better: instead of
+matching against any part of the file that may have a shebang-like line, we only
+look for the first. Let's put it back into the `assert-shellcheck.sh` file and
+use `NULL` for separators to accommodate files with spaces in the name:
+
+```
+#!/usr/sh -eux
+
+git ls-files -z | \
+ xargs -0 awk 'FNR>1 { nextfile } /^#!\// { print FILENAME; nextfile }' | \
+ xargs shellcheck
+```
+
+This is where I've stopped, but I imagine a likely improvement: match against
+only `#!/bin/sh` and `#!/usr/bin/env bash` shebangs (the ones I use most), to
+avoid running ShellCheck on Perl files, or other shebangs.
+
+Also when reviewing the text of this article, I found that `{ nextfile }` is a
+GNU Awk extension. It would be an improvement if `assert-shellcheck.sh` relied
+on the POSIX subset of Awk for working correctly.
+
+## *Update*
+
+After publishing, I could remove `{ nextfile }` and even make the script
+simpler:
+
+```shell
+#!/usr/sh -eux
+
+git ls-files -z | \
+ xargs -0 awk 'FNR==1 && /^#!\// { print FILENAME }' | \
+ xargs shellcheck
+```
+
+Now both the shell and Awk usage are POSIX compatible.
+
+[awk-20min]: https://ferd.ca/awk-in-20-minutes.html
+[shellcheck]: https://www.shellcheck.net/
diff --git a/src/content/tils/2020/12/29/svg.adoc b/src/content/tils/2020/12/29/svg.adoc
new file mode 100644
index 0000000..54cca9a
--- /dev/null
+++ b/src/content/tils/2020/12/29/svg.adoc
@@ -0,0 +1,134 @@
+---
+
+title: SVG favicon
+
+date: 2020-12-29
+
+updated_at: 2021-01-12
+
+layout: post
+
+lang: en
+
+ref: svg-favicon
+
+---
+
+I've wanted to change this website's favicon from a plain `.ico` file to a
+proper SVG. The problem I was trying to solve was to reuse the same image on
+other places, such as avatars.
+
+Generating a PNG from the existing 16x16 icon was possible but bad: the final
+image was blurry. Converting the `.ico` to an SVG was possible, but sub-optimal:
+tools try to guess some vector paths, and the final SVG didn't match the
+original.
+
+Instead I used a tool to draw the "vector pixels" as black squares, and after
+getting the final result I manually cleaned-up the generated XML:
+
+```xml
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
+ <path d="M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z" />
+ <path d="M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z" />
+ <path d="M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z" />
+ <path d="M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z" />
+ <path d="M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z" />
+ <path d="M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z" />
+ <path d="M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z" />
+ <path d="M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z" />
+ <path d="M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z" />
+ <path d="M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z" />
+ <path d="M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z" />
+ <path d="M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z" />
+ <path d="M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z" />
+ <path d="M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z" />
+ <path d="M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z" />
+ <path d="M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z" />
+ <path d="M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z" />
+ <path d="M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z" />
+ <path d="M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z" />
+ <path d="M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z" />
+ <path d="M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z" />
+ <path d="M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z" />
+ <path d="M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z" />
+ <path d="M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z" />
+ <path d="M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z" />
+ <path d="M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z" />
+ <path d="M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z" />
+ <path d="M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z" />
+ <path d="M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z" />
+ <path d="M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z" />
+ <path d="M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z" />
+ <path d="M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z" />
+ <path d="M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z" />
+ <path d="M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z" />
+ <path d="M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z" />
+ <path d="M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z" />
+ <path d="M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z" />
+ <path d="M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z" />
+ <path d="M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z" />
+ <path d="M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z" />
+ <path d="M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z" />
+ <path d="M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z" />
+ <path d="M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z" />
+ <path d="M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z" />
+ <path d="M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z" />
+ <path d="M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z" />
+ <path d="M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z" />
+ <path d="M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z" />
+ <path d="M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z" />
+ <path d="M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z" />
+ <path d="M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z" />
+ <path d="M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z" />
+ <path d="M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z" />
+ <path d="M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z" />
+ <path d="M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z" />
+ <path d="M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z" />
+ <path d="M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z" />
+ <path d="M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z" />
+ <path d="M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z" />
+</svg>
+```
+
+The good thing about this new favicon
+(at [`/static/lord-favicon.svg`](/static/lord-favicon.svg)) is that
+a) it is simple enough that I feel
+comfortable editing it manually and b) it is an SVG, which means I can generate
+any desired size.
+
+With the new favicon file, I now had to add to the templates' `<head>` a
+`<link>` to this icon:
+```html
+<head>
+ <meta charset="UTF-8" />
+ <link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
+ ...
+```
+
+Still missing is a bitmap image for places that can't handle vector images. I
+used Jekyll generator to create an PNG from the existing SVG:
+
+```ruby
+module Jekyll
+ class FaviconGenerator < Generator
+ safe true
+ priority :high
+
+ SIZE = 420
+
+ def generate(site)
+ svg = 'static/favicon.svg'
+ png = 'static/favicon.png'
+ unless File.exist? png then
+ puts "Missing '#{png}', generating..."
+ puts `inkscape -o #{png} -w #{SIZE} -h #{SIZE} #{svg}`
+ end
+ end
+ end
+end
+```
+
+I had to increase the priority of the generator so that it would run before
+other places that would use a `{% link /static/lord-favicon.png %}`, otherwise
+the file would be considered missing.