- Shell 100%
entrypoint.sh does not longer exist. Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca> |
||
|---|---|---|
| .github/workflows | ||
| checks | ||
| repo-skeleton | ||
| scripts | ||
| tests | ||
| .check-json-files | ||
| .check-shell-files | ||
| .check-sorted | ||
| .editorconfig | ||
| .gitignore | ||
| .gitlint | ||
| .markdownlint-cli2.jsonc | ||
| .yamllint.yaml | ||
| action-apt-depends.txt | ||
| action-depends.txt | ||
| action.yml | ||
| cspell.json | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| words-dev-config-project.txt | ||
Pre-commit Check Action
A reusable Forgejo / GitHub Action that runs comprehensive
pre-commit code quality checks on your repository. It validates commit messages,
shell scripts, JSON files, spelling, markdown formatting, and more — producing
TAP-13 output on stdout,
detailed pass/fail results in the GitHub Actions workflow summary via
GITHUB_OUTPUT,
and human-readable color-coded terminal output.
Contents
Overview
This repository provides a reusable action that can be included in any Forgejo or GitHub Actions workflow to enforce code quality standards before code is committed.
Note
For use with GitHub Actions,
runs-on: dockerneeds to beruns-on: ubuntu-latest
What does it check?
| Category | Tools | What's validated |
|---|---|---|
| Commit messages | gitlint |
Subject length, prefix format, capitalization, body length, sign-off matching, cherry-pick tags |
| Shell scripts | shellcheck, shfmt |
Code quality, style formatting |
| JSON files | jq |
Valid JSON, pretty-printed formatting |
| Spelling | cspell |
Typos in all tracked files |
| Markdown files | markdownlint-cli2 |
Formatting consistency |
| YAML files | yamllint |
Formatting standards |
| Secrets | gitleaks |
Accidental credential commits |
| File names | git | Non-ASCII filenames, trailing whitespace |
Features
- TAP-13 output — machine-readable test results on
stdout - GitHub Actions
GITHUB_OUTPUT— structured key-value pairs accessible by subsequent steps - Colored terminal output — color-coded pass (
green), warn (yellow), fail (red), skip (default) statuses - Selective exceptions — automatically skips Dependabot and Weblate commits
- Configurable rules — customize limits, enabled/disabled checks via environment variables
- Pre-commit and CI modes —
STAGED-onlychecks when used as a local hook, full branchdiffchecks in CI
Installation
CI Checks
Note
The action is not containerized and depends on being in a Debian-like runner and having the ability to use
apt-get install ...andnpm install -Dwhere the apt packages are at least the versions in Debian 13 (trixie), npm is as least version 9.2.0, and Node.js is at least v20.19.2.
Add the action as a dependency in your .github/workflows/<file>.yaml:
- name: Do pre-commit checks
uses: "https://forgejo.d-f-d.ca/danielfdickinson/pre-commit-action@main"
Local Git Hooks
To install local pre-commit and commit-msg hooks:
bash scripts/git-hook-setup.sh
This configures your local Git repository's core.hooksPath to point to
checks/git-hooks, making your local commits and pushes subject to the same
quality checks the CI enforces.
Warning: The script checks that required system binaries (
gitleaks,gitlint,shellcheck,shfmt,yamllint) are available on your system before installing hooks. If any dependency is missing, the hooks will not be installed.
Checks Performed
HyperStickler: Formalities (checks/hyperstickler/check_formalities.sh)
Based on HyperStickler formalities checks for OpenWrt by George Sapkin
Checks authored-by metadata and commit message formatting:
- Author name — must be a real name (
firstname lastname) or alias - Author email — must not be a GitHub noreply address
- Commit subject — must start with
<package prefix>:, must not start with whitespace, must not end with a period, must be ≤ 60 characters (soft warn: ≤ 50) - Commit body —
Signed-off-bymust match the author, body lines ≤ 72 characters - Cherry-pick detection — commits to
main/mastermust include(cherry picked from commit ...)marker - Merge commit detection — merge commits will fail with a pass-through
- Pull request branch — PRs must come from a feature branch (not
main/master)
Configure HyperStickler rules via environment variables:
| Variable | Default | Description |
|---|---|---|
CHECK_BRANCH |
true |
Enable branch validation |
CHECK_SIGNOFF |
true |
Enable sign-off validation |
CHECK_CHERRY_PICK |
true |
Enable cherry-pick detection |
MAX_SUBJECT_LEN_HARD |
60 |
Hard limit for commit subject length |
MAX_SUBJECT_LEN_SOFT |
50 |
Soft limit (warns when exceeded) |
MAX_BODY_LINE_LEN |
72 |
Hard limit for body line length |
EXCLUDE_DEPENDABOT |
false |
Skip checks on Dependabot commits |
EXCLUDE_WEBLATE |
false |
Skip checks on Weblate commits |
Worktree Checks (checks/scripts/check-worktree.sh)
Checks staged/unstaged files:
| Check | Tool | Default Pattern |
|---|---|---|
| Non-ASCII filenames | git | All text files (via git) |
| Trailing whitespace | git | All text files (via git) |
| File sorting | sort |
.check-sorted |
| Shellcheck | shellcheck -x |
.check-shell-files |
| shfmt formatting | shfmt -d |
.check-shell-files |
| JSON validation | jq --tab . |
.check-json-files |
| JSON beautification | jq --tab . |
.check-json-files |
| Spelling | cspell lint |
all tracked files |
| Markdown formatting | markdownlint-cli2 |
**/*.md, **/*.markdown |
| YAML formatting | yamllint |
all YAML files |
Override file-glob patterns with environment variables:
CHECK_SORT_LIST, CHECK_SHELLCHECK_LIST, CHECK_JSON_LIST.
Or place a local .check-sorted, .check-shell-files, .check-json-files in the
repository root to override the defaults.
Secret Scanning (checks/scripts/check-worktree.sh — gitleaks)
Checks for accidentally committed secrets using gitleaks.
| Variable | Default | Description |
|---|---|---|
GITLEAKS_MODE |
none |
none, pre-commit when called from the pre-commit git-hook, ci when run in CI context, or worktree when run in dev context |
GITLEAKS_BASELINE |
auto-detected | Path to gitleaks baseline file |
In pre-commit mode, the baseline file gitleaks-<pkg>-baseline.json is
automatically sought in the parent directory of the repository (e.g. the
OpenWrt-RepoTool workspace root when using OpenWrt-RepoTool).
Commit-msg Git Hook Rules (checks/git-hooks/commit-msg)
In addition to HyperStickler rules, the commit-msg hook enforces:
- Gitlint — commit message must pass
gitlint --msg-filename - No duplicate Signed-off-by — each SoB line must be unique
- Subject/body separator — there must be an empty line between subject and body
Configuration
Environment Variables
| Variable | Scope | Default | Description |
|---|---|---|---|
SHOW_LEGEND |
All | true |
Show/hide the TAP result legend |
FEEDBACK_URL |
All | Forgejo issues URL | Feedback URL shown in output |
GITLEAKS_MODE |
All | none (see above) |
none, pre-commit, or ci |
GITLEAKS_BASELINE |
All | auto (see above) | gitleaks baseline file path |
CHECK_BRANCH |
HyperStickler | true |
Enable branch checking |
CHECK_SIGNOFF |
HyperStickler | true |
Enable sign-off checking |
CHECK_CHERRY_PICK |
HyperStickler | true |
Enable cherry-pick checking |
MAX_SUBJECT_LEN_HARD |
HyperStickler | 60 |
Subject hard limit (characters) |
MAX_SUBJECT_LEN_SOFT |
HyperStickler | 50 |
Subject soft limit (characters) |
MAX_BODY_LINE_LEN |
HyperStickler | 72 |
Body line limit (characters) |
EXCLUDE_DEPENDABOT |
HyperStickler | false |
Skip Dependabot commits |
EXCLUDE_WEBLATE |
HyperStickler | false |
Skip Weblate commits |
CHECK_SORT_LIST |
Worktree | words-*.txt |
Files to check for alphabetical sort |
CHECK_SHELLCHECK_LIST |
Worktree | *.sh |
Shell files to check with shellcheck/shfmt |
CHECK_JSON_LIST |
Worktree | *.json* |
JSON files to validate/format |
Configuration Files
| File | Purpose |
|---|---|
.check-sorted |
Override CHECK_SORT_LIST with local file contents |
.check-shell-files |
Override CHECK_SHELLCHECK_LIST |
.check-json-files |
Override CHECK_JSON_LIST |
.gitlint |
gitlint configuration |
cspell.json |
cspell dictionary and settings |
.markdownlint-cli2.jsonc |
markdownlint configuration |
.yamllint.yaml |
yamllint configuration |
Output Format
stdout (Colored Terminal Output)
The action writes human-readable TAP output to stdout with ANSI color codes:
TAP version 13
[32m☑[39m Branch check passed
[32m☑[39m Author name valid: Daniel F Dickinson
[33m☟[39m Warning: subject line exceeds soft limit (56 vs 50)
[31m✗[39m Shell files do not pass shellcheck
[39m⌀[39m Skipped: sign-off check (Dependabot)
1..5
Status colors:
| Result | Color | Meaning |
|---|---|---|
✓ ok |
Green (32) | Check passed |
☟ warn |
Yellow (33) | Check passed with a warning |
✗ not ok |
Red (31) | Check failed |
⌀ skip |
Default (39) | Check skipped |
The TAP trailer line (1..N) is written to stdout
GITHUB_OUTPUT (Structured Key-Value Pairs)
Each check produces these keys for GitHub Actions:
check_name_1=<check description>
check_severity_1=pass|warn|fail|skip
check_result_1=pass|warn|fail|skip
check_tap_1=ok|not ok
check_diag_1=<diagnostic info if applicable>
stderr
Error messages are written to stderr.
Architecture
Directory Structure
pre-commit-action/
├── action.yml # Reusable action definition
├── action-apt-depends.txt # apt dependencies
├── action-depends.txt # node/system dependencies
├── cspell.json # cspell config
├── .markdownlint-cli2.jsonc # markdownlint config
├── .yamllint.yaml # yamllint config
├── .gitlint # gitlint config
├── entrypoint.sh # Single unified entrypoint script
├── scripts/
│ ├── check-depends.sh # Dependency health check
│ └── git-hook-setup.sh # Local git hook installer
├── checks/
│ ├── common.sh # Shared TAP infrastructure (primary module)
│ ├── git-hooks/
│ │ ├── commit-msg # Git commit-msg hook (thin wrapper)
│ │ └── pre-commit # Git pre-commit hook (thin wrapper)
│ ├── hyperstickler/
│ │ ├── check_formalities.sh # HyperStickler commit rules
│ │ └── helpers.sh # Color output helpers
│ └── scripts/
│ └── check-worktree.sh # Worktree/file-level checks
├── .github/workflows/main.yml # CI workflow
Sourcing Chain
entrypoint.sh {pre-commit|commit-msg|worktree} ← Unified entrypoint
├── check_formalities.sh
│ └── common.sh (TAP infrastructure)
└── check_worktree.sh
└── common.sh (TAP infrastructure)
commit-msg [msg-file] ← Git commit-msg hook
├── entrypoint.sh (calls with commit-msg context)
└── check_formalities.sh
└── common.sh (TAP infrastructure)
pre-commit ← Git pre-commit hook
├── entrypoint.sh (calls with pre-commit context)
├── check_formalities.sh
│ └── common.sh (TAP infrastructure)
└── check_worktree.sh
└── common.sh (TAP infrastructure)
common.sh uses a re-source guard (if [ -z "$TAP_COUNTER" ]) so that TAP
counters are not zeroed when the file is sourced multiple times.
Core Functions
| Function | Module | Purpose |
|---|---|---|
tap_init() |
common.sh |
Emit "TAP version 13" on first check |
tap_done() |
common.sh |
Emit "1..N" TAP trailer after all checks |
_tap_next() |
common.sh |
Get and increment TAP test counter |
output_pass() |
common.sh |
Pass → TAP ok; write kv-pairs to GITHUB_OUTPUT |
output_warn() |
common.sh |
Warn → TAP ok (yellow); write kv-pairs |
output_fail() |
common.sh |
Fail → TAP not ok (red); set FAIL code |
output_skip() |
common.sh |
Skip → TAP ok (default, #skip); write kv-pairs |
check() |
common.sh |
Dispatcher: evaluates condition funcs, calls output |
main_common() |
common.sh |
Entry point: legend, exceptions, gitleaks |
check_files_fail() |
common.sh |
Run a check across a file glob |
is_exception() |
common.sh |
Check if committer is in exception list |
push_exception() |
common.sh |
Add a committer to the exception list |
main() |
check_formalities.sh |
HyperStickler commit rule checks |
worktree_main() |
check_worktree.sh |
File-level worktree quality checks |
status_pass/warn/fail/skip() |
helpers.sh |
Colorized status output |
feedback() |
common.sh |
Print feedback URL |
legend() |
common.sh |
Print TAP result legend |
git_hook_main() |
git-hooks/* |
Git hook entry point |
AI Note
This README is largely AI generated (using L.A.T.E. using ollama with model Qwen 3.6) with some editing by Daniel F. Dickinson (human).
Acknowledgements
Inspired by HyperStickler formalities checks for OpenWrt (by George Sapkin), pre-commit, and Husky.
Original code is from HyperStickler and Daniel F. Dickinson, now modified by the above mentioned AI tools.
License
This repository is licensed under the GNU General Public License v2.0 only.
SPDX-License-Identifier: GPL-2.0-only