ci: lint the scripts and workflows on every pull request - #376
Merged
Conversation
Nine findings across the shell scripts and the `run:` blocks, reducing to three
causes. None had ever been reported by anything the repository runs: shellcheck
and actionlint are not installed here, and SonarQube's shell analysis arrives
after the merge.
SC1091, twice, was real. `. "$(dirname "$0")/../trains.sh"` is a path shellcheck
cannot resolve statically, so it was skipping the sourced file entirely — the
single source of truth for the release trains went unchecked in both scripts
that read it. A `# shellcheck source=` directive fixes the analysis, not just the
message.
The other seven are false positives, each annotated where it fires rather than
disabled repository-wide. SC2016 (five) reads the Markdown backticks in a printf
format as command substitution; every one of these formats emits a step summary
or a pull-request comment. SC2317 (two) calls the hook's two rule functions
unreachable, because they are reached through the `"rule_${rule}"` dispatch a
static reader cannot follow — the rule's own message says to ignore it when the
call is indirect.
A `.shellcheckrc` would have silenced all seven in one line and blinded both
rules everywhere else, including where they are right.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bxVrnCNsXW3RvwmLc9Kvy
Every analysis in this repository runs inside a build — the Roslyn analyzers, the explicit-type rule ADR-0055 restated in .editorconfig, the warning ratchet — so a contributor meets it while writing the code. The shell scripts and the workflow files had no such moment. The only thing reading them was the sonar scan, which reports after the merge and enforces nothing: its job goes green as soon as the analysis uploads, whatever the Quality Gate says. Two findings typed VULNERABILITY reached main through that gap, and 21 shell findings accumulated behind it. The new `lint` workflow runs shellcheck over every *.sh and actionlint over .github/workflows on every pull request. Both run on our own runners, so the signal arrives before the merge and survives a SonarQube outage — which the current arrangement does not: the sonar check is required and calls the service, so an outage blocks merging while a red Quality Gate does not. shellcheck is preinstalled on the runner image, so no third-party action enters the supply chain. actionlint is a pinned release tarball verified by SHA-256, for the same reason Scorecard's Pinned-Dependencies check exists. The job declares its own `contents: read` rather than inheriting a workflow-level grant — the S8264 lesson applied to the workflow that now guards against it. The bar is zero findings, `info` included, and the tree is clean at that bar. A lower bar would let `info` accumulate exactly the way the Sonar report did. Documented in the workflow reference, English and French, including what this does NOT cover: actionlint audits correctness, not security posture, so the class that produced the two VULNERABILITY findings needs a dedicated auditor and a separate decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017bxVrnCNsXW3RvwmLc9Kvy
Reefact
force-pushed
the
claude/sonar-points-restants-12zlnr
branch
from
July 29, 2026 17:16
317a161 to
9ccacfc
Compare
Reefact
enabled auto-merge
July 29, 2026 17:17
This was referenced Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings static analysis of the shell scripts and workflow definitions forward to the pull request, where every other check in this repository already lives. Until now nothing in CI read them: the only analysis was the
sonarscan, which reports after the merge and enforces nothing — its job goes green as soon as the upload succeeds, whatever the Quality Gate says.Type of change
Changes
New
lintworkflowshellcheckover every*.sh(14 scripts),actionlintover.github/workflows(18 files), on push tomain, on every PR, and on dispatch.shellcheckships preinstalled on the runner image, so no third-party action enters the supply chain;actionlintis a release tarball pinned by version and verified by SHA-256, for the same reason OpenSSF Scorecard's Pinned-Dependencies check exists.contents: readinstead of inheriting a workflow-level grant —githubactions:S8264applied to the workflow that now guards against it.infoincluded. A lower bar would letinfoaccumulate exactly the way the Sonar report did, which is the problem this exists to prevent.Making the tree clean at that bar — 9 findings, three causes
SC1091×2 was real.. "$(dirname "$0")/../trains.sh"is a path shellcheck cannot resolve statically, so it was skipping the sourced file entirely:tools/trains.sh, the single source of truth for the release trains, went unchecked in both scripts that read it. A# shellcheck source=directive fixes the analysis, not just the message.SC2016×5 — false positive. Reads the Markdown backticks in aprintfformat as command substitution; every one of these formats emits a step summary or a PR comment.SC2317×2 — false positive. Calls the hook's two rule functions unreachable, because they are reached through the"rule_${rule}"dispatch a static reader cannot follow. The rule's own message says to ignore it when the call is indirect..shellcheckrcwould have silenced all seven in one line and blinded both rules everywhere else, including where they are right.Documentation
lint.en.md/lint.fr.mdin the workflow reference, plus a row in both README tables.Testing
dotnet build FirstClassErrors.slndotnet test FirstClassErrors.slnFirstClassErrors.Analyzers.UnitTests)Zero warnings, 2092 tests passing. No C# changes in this PR, so the .NET suite is a regression check rather than the relevant one.
What actually verifies this change was run locally, using the exact commands the workflow will run:
shellcheckover all 14 scripts — exit 0, clean.actionlintover all 18 workflows, including the newlint.ymlitself — clean.OK.history-hygieneexits 0).Documentation
doc/updateddoc/handwritten/for-users/README.fr.md) updated if user-facing behavior changedWorkflow reference updated in English and French, including an explicit statement of what this does not cover (see below).
README.fr.mdunderfor-usersis untouched — no user-facing behaviour changes.Architecture decisions
Proposed: ADR-____This PR is one step of a larger move — what blocks a merge runs on our runners; the SaaS informs, it does not guard — whose remaining steps are
SonarAnalyzer.CSharpin the build and de-requiring thesonarcheck. I would rather draft that ADR once with the whole sequence than record a fragment of it here, but @Reefact should judge: say the word and I will propose it now.Notes for the reviewer
actionlintaudits correctness, not security posture. It does not flag over-broad permissions, spoofable actor checks or dangerous triggers — the class that produced this repository's two VULNERABILITY findings. This is stated in the workflow comment and in the reference doc so nobody mistakes this workflow for that coverage.The tool that does cover it,
zizmor, was run locally against the 18 workflows and reports 77 findings, 2 of them HIGH:dependabot-autofix.yml:32—workflow_runtrigger, a known privilege-escalation vector.dependabot-automerge.yml:30—if: github.actor == 'dependabot[bot]', spoofable.Plus 23
artipacked(medium, checkout withoutpersist-credentials: false) and 10template-injection(informational). Deliberately not in this PR: 77 findings, two of them on the Dependabot auto-merge path, deserve their own look rather than being buried here.Related issues
None.
Generated by Claude Code