Skip to content

ci: lint the shell commands printed in the manual - #866

Merged
mik-tf merged 1 commit into
developmentfrom
ci/check-doc-commands
Jul 28, 2026
Merged

ci: lint the shell commands printed in the manual#866
mik-tf merged 1 commit into
developmentfrom
ci/check-doc-commands

Conversation

@mik-tf

@mik-tf mik-tf commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Answers the farmers/ vs labs/ duplication question, and fixes six real bugs found while building the check.

On the duplication itself: leave it alone

Those five pages are not a stale copy. farmers/6_boot_3node is a 22-line summary with its own prose; the labs version is 166 lines of detail. Only 2–7 lines are unique to farmers, but the farmers pages are condensations written for a different reader, not excerpts. Merging them would destroy the simple manual.

What can be fixed is the actual risk: a fix lands in one tree and not the other. That produced the V4-screenshots-on-a-V3-page bug, and I hit it three times in this session — the wipe guide, the dd command, the bootstrap page — each time fixing both copies by hand with nothing enforcing it.

The check

scripts/check_docs_commands.py, run in pr_check.yml:

  1. Lints every shell block with shellcheck at .
  2. Flags command drift between the farmers/ and labs/ copies of the five shared pages.

Why shellcheck and not bash -n

I originally proposed bash -n, tested it, and it did not catch the original bug. That loop —

for i in /dev/sd*; do if [ "$i"!= "/dev/sdX"* ]; then wipefs -af $i; fi; done

— is syntactically valid. It fails at runtime (unary operator expected). bash -n passes it. shellcheck catches it: SC1108, you need a space before and after the =.

It also only looked at ```bash blocks — but that loop lived in an untagged block, and most of the manual's commands are untagged. Both gaps are fixed.

Tested, not assumed

  • Re-injected the exact original wipe loop → check fails with the correct file and line, exit 1
  • Changed a flag in one tree only → drift check fails, exit 1
  • Clean tree → exit 0, 328 blocks linted, 9 shared twin blocks, no drift

Severity is , not : warning level flags 36 blocks, mostly cd without || exit, which is noise for documentation. Error level sits at zero.

Conventions are respected rather than fought — <placeholder> is normalised, pasted terminal sessions are skipped, and heredoc blocks are skipped because their body is data that shellcheck mis-parses out of context.

Six bugs the check found

Real breakage:

file bug
message.md command ends with a trailing \ and nothing after — a copied command hangs waiting for input
db_testing.md same, after --reset \
docker_basics.md dangling backtick: --filter "until= ", with prose reading "replace with the complete date"

Mislabelled blocks (output tagged as runnable shell): api_token.md (JSON), ssh_wsl.md (a Windows path), farmerbot_information.md (--help output, ×2), cloud_provider_farming.md (a terminal session).

The docker_basics.md one is notable: my earlier full-manual scan missed it, because that scan did not normalise placeholders and the real error was masked among ~30 false positives.

Net effect

Broken commands and cross-tree drift are now build failures instead of something a farmer discovers with a dead node.

Adds scripts/check_docs_commands.py, wired into pr_check.yml. Two checks,
both aimed at the same failure: a farmer copies a command out of the
manual and it does not work.

1. Lints every shell block with shellcheck (severity=error), falling back
   to bash -n when shellcheck is unavailable.

   shellcheck rather than bash -n because bash -n only checks syntax. The
   disk-wipe loop that silently wiped nothing was syntactically VALID and
   failed at runtime; bash -n passes it, shellcheck catches it (SC1108).
   Verified by re-injecting that exact loop: the check fails with the
   correct file and line.

   Covers untagged blocks too, not just ```bash -- most of the manual's
   commands are untagged, and that wipe loop lived in one. Respects the
   manual's conventions: <placeholder> is normalised, pasted terminal
   sessions are skipped, and heredoc blocks are skipped because their body
   is data that shellcheck mis-parses out of context.

2. Flags command blocks that have drifted between the farmers/ and labs/
   copies of the five shared build pages. Those pages are deliberately
   different documents for different audiences -- the prose is meant to
   diverge, the commands are not. Verified by changing a flag in one tree
   only: the check fails.

Fixes the six issues the new check found:

- message.md and db_testing.md ended a command with a trailing backslash
  and nothing after it, so a copied command hangs waiting for input
- docker_basics.md had a dangling backtick: --filter "until=`" , with
  prose reading 'replace ` with the complete date'
- api_token.md tagged JSON output as bash, ssh_wsl.md tagged a Windows
  path as sh, farmerbot_information.md tagged --help output as bash, and
  cloud_provider_farming.md tagged a terminal session as sh
@mik-tf
mik-tf merged commit 534e212 into development Jul 28, 2026
1 check passed
@mik-tf
mik-tf deleted the ci/check-doc-commands branch July 28, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant