cmc/cleberg.net

My personal web garden & blog.

clone: git clone https://gitbay.org/cmc/cleberg.net.git

c01dba4633dd14ecfdaac508b12d347c55ee8af0

unsigned

author: Christian Cleberg <hello@cleberg.net> · 2026-08-22T03:54:16Z

Check links weekly, and point six internal links at their canonical URLs

The six links were written before a URL scheme change and only resolved because
the web server redirects the old paths. orgo --strict did not catch them: it
resolves file: links, and these were bare relative paths passed through as
opaque URLs. Rewriting them as file: links makes orgo validate them from now on
and drops a redirect hop. Link text is unchanged.

Internal links fail the job, external links only report — a dead third-party
link is worth knowing about but is not a reason to block the site.
 .github/workflows/link-check.yml                  | 83 +++++++++++++++++++++++
 content/blog/2020-01-25-linux-software.org        |  2 +-
 content/blog/2020-01-26-steam-on-ntfs.org         |  2 +-
 content/blog/2020-02-09-cryptography-basics.org   |  2 +-
 content/blog/2020-03-25-session-messenger.org     |  2 +-
 content/blog/2021-04-17-gemini-server.org         |  2 +-
 content/blog/2024-02-13-ubuntu-emergency-mode.org |  2 +-
 7 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml
new file mode 100644
index 0000000..f608200
--- /dev/null
+++ b/.github/workflows/link-check.yml
@@ -0,0 +1,83 @@
+name: Link check
+
+# Weekly rather than per-PR: most of these links are external, and the web is
+# flaky enough that a per-PR run would fail for reasons no author controls.
+# The paths filter still runs it on any PR that edits this workflow, so a change
+# here is validated by the thing it changes.
+on:
+  schedule:
+    - cron: "0 7 * * 1"
+  workflow_dispatch:
+  pull_request:
+    paths: [".github/workflows/link-check.yml"]
+
+permissions:
+  contents: read
+
+env:
+  ORGO_VERSION: "0.22.0"
+
+jobs:
+  links:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v7
+
+      - name: Cache orgo
+        id: cache-orgo
+        uses: actions/cache@v6
+        with:
+          path: ~/.cargo/bin/orgo
+          key: orgo-${{ env.ORGO_VERSION }}-${{ runner.os }}
+
+      - name: Install orgo
+        if: steps.cache-orgo.outputs.cache-hit != 'true'
+        run: cargo install orgo --version "$ORGO_VERSION" --locked
+
+      # orgo directly, not build.py. A production build rewrites
+      # img.cleberg.net URLs to /img/, which is served from a different docroot
+      # and would look like 198 broken links here. The development output keeps
+      # them absolute, so the image host gets checked for real.
+      - name: Build the site
+        run: orgo build content -o .build-dev --strict
+
+      # Internal links are the half worth gating on: they are entirely within
+      # this repo, so a failure is always a real defect and always fixable here.
+      - name: Internal links
+        uses: lycheeverse/lychee-action@v2.9.0
+        with:
+          args: --offline --base .build-dev --include-verbatim '.build-dev/**/*.html'
+          fail: true
+          output: internal.md
+
+      # External links are reported, not gated. A dead third-party link is worth
+      # knowing about, but it is not a reason to block the site.
+      - name: External links
+        uses: lycheeverse/lychee-action@v2.9.0
+        with:
+          args: >-
+            --exclude-mail
+            --exclude '\.onion'
+            --max-concurrency 8
+            --max-retries 2
+            --timeout 20
+            --accept 200,206,301,302,303,307,308,401,403,429
+            --include-verbatim
+            '.build-dev/**/*.html'
+          fail: false
+          output: external.md
+
+      - name: Summary
+        if: always()
+        run: |
+          { echo "## Internal"; cat internal.md 2>/dev/null || echo "no report";
+            echo; echo "## External"; cat external.md 2>/dev/null || echo "no report";
+          } >> "$GITHUB_STEP_SUMMARY"
+
+      - uses: actions/upload-artifact@v7
+        if: always()
+        with:
+          name: link-reports
+          path: |
+            internal.md
+            external.md
diff --git a/content/blog/2020-01-25-linux-software.org b/content/blog/2020-01-25-linux-software.org
index 17be3b8..1cad7d2 100644
--- a/content/blog/2020-01-25-linux-software.org
+++ b/content/blog/2020-01-25-linux-software.org
@@ -179,7 +179,7 @@ Finally, install the program:
 sudo pacman -S steam
 #+end_src
 
-[[./2020-01-26-steam-on-ntfs-drives.html][Problem Launching Steam Games? Click Here.]]
+[[file:2020-01-26-steam-on-ntfs.org][Problem Launching Steam Games? Click Here.]]
 
 * Command-Line Packages
 
diff --git a/content/blog/2020-01-26-steam-on-ntfs.org b/content/blog/2020-01-26-steam-on-ntfs.org
index f210b0e..81ed5c0 100644
--- a/content/blog/2020-01-26-steam-on-ntfs.org
+++ b/content/blog/2020-01-26-steam-on-ntfs.org
@@ -6,7 +6,7 @@
 
 * Auto-Mount Steam Drives
 
-If you want to see how to install Steam on Linux, see my other post: [[../linux-software/][Linux
+If you want to see how to install Steam on Linux, see my other post: [[file:2020-01-25-linux-software.org][Linux
 Software]].
 
 Are you having trouble launching games, even though they've installed correctly?
diff --git a/content/blog/2020-02-09-cryptography-basics.org b/content/blog/2020-02-09-cryptography-basics.org
index 9be7ebf..6df472e 100644
--- a/content/blog/2020-02-09-cryptography-basics.org
+++ b/content/blog/2020-02-09-cryptography-basics.org
@@ -6,7 +6,7 @@
 
 * Similar Article Available
 
-If you haven't already, feel free to read my post on [[../aes-encryption/][AES Encryption]].
+If you haven't already, feel free to read my post on [[file:2018-11-28-aes-encryption.org][AES Encryption]].
 
 * What is Cryptography?
 
diff --git a/content/blog/2020-03-25-session-messenger.org b/content/blog/2020-03-25-session-messenger.org
index 8ff0458..36bb9e9 100644
--- a/content/blog/2020-03-25-session-messenger.org
+++ b/content/blog/2020-03-25-session-messenger.org
@@ -9,7 +9,7 @@
 The company behind Session (Loki Foundation) is from Australia. If you didn't
 know, Australia has introduced [[https://parlinfo.aph.gov.au/parlInfo/download/legislation/bills/r6195_aspassed/toc_pdf/18204b01.pdf][legislation]] mandating companies comply with
 government requests to build back door access into applications. For more
-information, read my article on [[./2020-01-25-aes-encryption.html][AES Encryption]].
+information, read my article on [[file:2018-11-28-aes-encryption.org][AES Encryption]].
 
 * About Session
 
diff --git a/content/blog/2021-04-17-gemini-server.org b/content/blog/2021-04-17-gemini-server.org
index 11636ec..6d00577 100644
--- a/content/blog/2021-04-17-gemini-server.org
+++ b/content/blog/2021-04-17-gemini-server.org
@@ -7,7 +7,7 @@
 * Similar Article Available
 
 To read more about Gemini and ways to test out this new protocol without your
-own server, see my previous post [[../launching-a-gemini-capsule/][Launching a Gemini Capsule]].
+own server, see my previous post [[file:2021-03-28-gemini-capsule.org][Launching a Gemini Capsule]].
 
 * Preparation
 
diff --git a/content/blog/2024-02-13-ubuntu-emergency-mode.org b/content/blog/2024-02-13-ubuntu-emergency-mode.org
index ccbe6dd..b318e9a 100644
--- a/content/blog/2024-02-13-ubuntu-emergency-mode.org
+++ b/content/blog/2024-02-13-ubuntu-emergency-mode.org
@@ -6,7 +6,7 @@
 
 * The Problem
 
-I recently [[../zfs/][migrated my hard drives to a ZFS pool]] and found myself stuck in
+I recently [[file:2024-02-06-zfs.org][migrated my hard drives to a ZFS pool]] and found myself stuck in
 Ubuntu's emergency mode after the first reboot I performed after creating the
 ZFS pool.