Files
flan 469a2e4651
CI / shell (shellcheck + syntax) (push) Successful in 9s
CI / python 3.11 (push) Successful in 14s
CI / python 3.12 (push) Successful in 16s
CI / python 3.13 (push) Successful in 17s
TrueNAS compatibility / compat (push) Successful in 11s
Release / release (push) Successful in 14s
Installing the patch permanently blocked updating it
install.sh chmod +x's update.sh, and git recorded update.sh as 100644 -- so the chmod
was a TRACKED modification, and update.sh refuses to run over a dirty tree. Install
once and you could never update again. The error even told you to 'git checkout -- .',
which just undoes the exec bit so the next install can re-dirty it.

Found on the real box, which had been sitting on v0.4.1 for exactly this reason.

Fixed on both sides: the scripts install.sh chmods are executable in git (so the
chmod is a no-op), and update.sh's dirty check now looks at CONTENT, not mode --
git diff --numstat reports 0 0 for a mode-only change. A test asserts every script in
install.sh's chmod loop is already 100755 in git.
2026-07-13 18:58:46 +00:00

134 lines
5.0 KiB
Python

"""The docs must not lie about themselves.
The README was 969 lines with the install instructions at line 517. Splitting it into
docs/ fixed that and broke every cross-reference in the process -- which is the normal
outcome of moving Markdown around, and exactly why this is a test rather than a
careful afternoon.
A dead link in a recovery doc is worse than a dead link anywhere else: the person
following it is, by definition, already having a bad day.
"""
import os
import re
import pytest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DOCS = os.path.join(ROOT, "docs")
LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")
HEADING_RE = re.compile(r"^#{1,6}\s+(.*)$", re.M)
def markdown_files():
files = [os.path.join(ROOT, "README.md"), os.path.join(ROOT, "CHANGELOG.md")]
if os.path.isdir(DOCS):
files += [os.path.join(DOCS, f) for f in sorted(os.listdir(DOCS))
if f.endswith(".md")]
return files
def anchors(text):
"""GitHub/Gitea slugs for every heading in `text`."""
out = set()
for h in HEADING_RE.findall(text):
slug = re.sub(r"[^a-z0-9 -]", "", h.lower()).replace(" ", "-")
out.add(slug)
return out
@pytest.mark.parametrize("path", markdown_files(), ids=os.path.basename)
def test_every_internal_link_resolves(path):
with open(path, encoding="utf-8") as fh:
text = fh.read()
here = anchors(text)
base = os.path.dirname(path)
broken = []
for label, target in LINK_RE.findall(text):
if target.startswith(("http://", "https://", "mailto:")):
continue
rel, _, anchor = target.partition("#")
if not rel: # same-file anchor
if anchor and anchor not in here:
broken.append(f"[{label}](#{anchor}) — no such heading here")
continue
dest = os.path.normpath(os.path.join(base, rel))
if not os.path.exists(dest):
broken.append(f"[{label}]({target}) — file does not exist")
continue
if anchor and dest.endswith(".md"):
with open(dest, encoding="utf-8") as fh:
if anchor not in anchors(fh.read()):
broken.append(f"[{label}]({target}) — no such heading there")
assert not broken, "broken links in {}:\n {}".format(
os.path.basename(path), "\n ".join(broken)
)
class TestTheReadmeStaysAReadme:
def test_install_is_near_the_top(self):
# It was at line 517 of 969, under a wall of internals. Somebody deciding
# whether to use this should not have to scroll past the boot sequence.
with open(os.path.join(ROOT, "README.md"), encoding="utf-8") as fh:
lines = fh.read().splitlines()
install = next(i for i, ln in enumerate(lines, 1) if ln.startswith("## Install"))
assert install < 40, f"## Install is at line {install}"
def test_the_readme_does_not_grow_back(self):
with open(os.path.join(ROOT, "README.md"), encoding="utf-8") as fh:
n = len(fh.read().splitlines())
assert n < 300, (
f"README is {n} lines. Detail belongs in docs/ — the README is what "
f"someone reads before they trust this with their backups."
)
def test_the_minimum_version_is_stated_before_the_install_command(self):
with open(os.path.join(ROOT, "README.md"), encoding="utf-8") as fh:
text = fh.read()
assert "24.10" in text[:text.index("## Install")], (
"the minimum TrueNAS version must be visible above the install steps"
)
class TestInstallDoesNotDirtyTheCheckout:
"""install.sh chmod +x's scripts. If git records them as 100644, that chmod is a
TRACKED MODIFICATION -- and update.sh refuses to run over a dirty tree.
So installing once permanently blocked updating, for every user, with a message
telling them to `git checkout -- .` (which would just undo the exec bit and let
the next install re-dirty it). Found on a real box that had been stuck on an old
version for exactly this reason.
Every script install.sh makes executable must already be executable in git.
"""
def test_every_chmodded_script_is_already_executable_in_git(self):
import re
import subprocess
with open(os.path.join(ROOT, "install.sh"), encoding="utf-8") as fh:
m = re.search(r"^for _exe in (.+?); do", fh.read(), re.M)
assert m, "could not find install.sh's chmod loop"
scripts = m.group(1).split()
out = subprocess.run(
["git", "ls-files", "-s", *scripts],
cwd=ROOT, capture_output=True, text=True, check=True,
).stdout
not_exec = [
line.split("\t")[-1] for line in out.strip().splitlines()
if not line.startswith("100755")
]
assert not not_exec, (
"install.sh chmod +x's these, but git records them as non-executable — "
"so installing dirties the checkout and update.sh then refuses to run:\n "
+ "\n ".join(not_exec)
)