ci: let shellcheck see the extension-less scripts in bin/ - #24
Conversation
MegaLinter has never linted a single shell script in this repo. Every one of
them is extension-less, and the BASH descriptor ships
file_extensions [".sh", ".bash", ".dash", ".ksh"] with no "" entry.
filter_files() gates on the extension first:
if file_extension in file_extensions: pass
elif "*" in file_extensions: pass
elif file_names_regex_object.fullmatch(base_file_name): pass
else: continue
os.path.splitext("executable_pai-git-sync") gives an extension of "", which is
not in the set, and BASH declares no file_names_regex — so the file is dropped
before the file_contains_regex shebang match ever runs. The shebang patterns in
the descriptor are dead code for these files.
Symptom: the MegaLinter report on every PR lists only REPOSITORY-level linters,
never a BASH row, even when the diff is a bash script. The existing
BASH_SHELLCHECK_FILTER_REGEX_EXCLUDE and the BASH_EXEC entry in DISABLE_LINTERS
both assume a linter that was not running.
Adding "" makes extension-less files candidates; the descriptor's own shebang
regexes then select the shell ones. Scoped to BASH_SHELLCHECK on purpose:
BASH_SHFMT would pull the same 18 files into APPLY_FIXES, which runs shfmt -w
and would open a reformat PR across all of them. That is a separate decision.
Verified before committing: 18 files become candidates, and all 18 are
shellcheck-clean at 0.9.0, so this turns the gate on without turning CI red.
MegaLinter ships a newer shellcheck, which may add findings the local run does
not see.
✅MegaLinter analysis: Success
See detailed reports in MegaLinter artifacts
|
|
Two things worth adding before this is merged. Does adding
|
| stage | files |
|---|---|
| tracked | 365 |
| extension-less, so candidates after this change | 85 |
| dropped by the shebang filter | 64 |
| kept | 21 |
minus deprecated/ (2) and the msmtpq exclusion (1) |
18 linted |
The 64 dropped are what you would expect: .gitignore, .chezmoiroot,
.lycheeignore, the shell-history files, the old i3 / polybar / dunst configs.
The residual risk is real but not present today. file_contains() reads the
whole file and matches with re.MULTILINE, so #!/bin/sh at the start of any
line matches, not just line 1 — an extension-less doc quoting a shell snippet
would be picked up. Right now none of the 21 has its shebang anywhere but line
- If one ever appears, it is one more entry in the
BASH_SHELLCHECK_FILTER_REGEX_EXCLUDEthat already exists.
This PR's own green does not test the fix
VALIDATE_ALL_CODEBASE is false on pull requests, and the diff here is one YAML
file, so the run linted ACTION, YAML, SPELL and REPOSITORY and still shows no
BASH row. BASH will first appear on the push-to-main run after merge, where
VALIDATE_ALL_CODEBASE is true.
So the check that matters was run locally instead, at a newer shellcheck than
the one MegaLinter ships, with MegaLinter's own arguments:
shellcheck 0.11.0, --color --external-sources, all 18 targets
exit 0, zero findings
Merging should leave main green rather than lighting up a backlog.
_MREVIEW — Automated ReviewPlatform: github | Ref: #24 | CI: pass (2 checks) Verdict: approved. Two minor factual points below. Neither blocks, and neither Premise checkThe mechanism holds, verified against MegaLinter at the pinned SHA
Reimplementing Minor — the description's opening line overstates the gap"MegaLinter has never linted a shell script in this repo" is not right. Eight Both finished with 0 errors. What has never been linted is the 18 Minor — the shellcheck version risk is already closedThe description says MegaLinter ships a newer shellcheck than the local 0.9.0 So the versions match exactly, which makes the local run the definitive check The post-merge push run should be green, and the residual uncertainty the Posted by |
|
Both points confirmed independently, both were mine to get right, and the The The version risk was never open. v10.0.0 pins Nothing here changes the diff — one env line, unchanged. |

MegaLinter has never linted the extension-less scripts in this repo — 18 of them,
including everything in
home/private_dot_local/bin/. The 8.shfiles arealready covered and always have been; this PR is about the other half.
MegaLinter's BASH descriptor ships
file_extensions: [".sh", ".bash", ".dash", ".ksh"]with no""entry, and itsfilter_files()gates on the extensionbefore anything else:
os.path.splitext("executable_pai-git-sync")yields an extension of"", whichis not in the set, and the BASH descriptor declares no
file_names_regex. So thefile is dropped there, before the
file_contains_regexshebang match runs — thedescriptor's
^#!/usr/bin/env bashpatterns are dead code for these files.Adding
""makes extension-less files candidates; the descriptor's ownfile_contains_regex_extensions: [""]then scopes the shebang check to exactlythat case, so
.sh-family files keep being trusted on their extension andnon-shell extension-less files are dropped.
get_list()parses a value startingwith
[as JSON, so the empty-string element survives the trip through theworkflow env.
Scope
Deliberately
BASH_SHELLCHECKonly —Linter.pyreads<name>_FILE_EXTENSIONSwhere the name is the linter, not the descriptor, so
BASH_SHFMTkeeps itscurrent file set. That matters: shfmt would pull the same 18 files into
APPLY_FIXES: all, which runsshfmt -wand would open a reformat PR acrossevery script in
bin/. Worth deciding on its own terms, not as a side effect.Verified before opening
deprecated/(2) and the msmtpq exclusion (1)The 64 dropped are
.gitignore,.chezmoiroot,.lycheeignore, theshell-history files, the old i3 / polybar / dunst configs. None of the 21 carries
its shebang anywhere but line 1, so the
re.MULTILINEmatch infile_contains()costs nothing today.All 18 are clean under shellcheck v0.11.0 — the version MegaLinter v10.0.0
pins (
ARG BASH_SHELLCHECK_VERSION=v0.11.0), run in the samekoalaman/shellcheckimage with MegaLinter's own--external-sourcesarguments. Same version, same arguments, so this is the real check and not a
proxy for it.
Note that this PR's own CI does not exercise the change:
VALIDATE_ALL_CODEBASEis false on pull requests and the diff is one YAML file, so no BASH row appears
here. The BASH row on the push-to-
mainrun goes from 8 files to 26 after merge.