Fail CI on comments in TypeScript, TSX, Astro and CSS — and optionally post the fix as review suggestions the author can click to accept.
Built for codebases that treat the code as the only source of truth: if a constraint is real it belongs in a name, a type or a test, where it cannot drift out of date.
- uses: actions/checkout@v4
- uses: 1hachem/no-comments@v1// inside a string is not a comment. This action parses .ts/.tsx with the
TypeScript compiler and walks .css and Astro markup with real scanners, so none of
these are reported:
const d = '// not a comment';.b { content: "/* not a comment */"; }<div title="<!-- not a comment -->">Check (default) — lists offenders as path:line and fails the step. With
annotations on, each one also shows up inline on the diff.
Suggest — computes the removal in memory and posts it as a single batched review of
```yaml
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: 1hachem/no-comments@v1
with:
suggest: true
Fix — strips the comments in place. Useful in a scheduled cleanup job that opens its
own PR. Mutually exclusive with suggest. Run your formatter afterwards to reflow.
The same binary runs locally, so a hook and CI can share one set of rules. Install it straight from the tag:
pnpm add -D github:1hachem/no-comments#v1--staged narrows the scan to what is staged for commit, which is what makes a
pre-commit hook cheap; without it the whole tracked tree is scanned, which is what you
want on pre-push.
no-comments --staged --fix # strip what you are about to commit
no-comments # check the whole tree, exit 1 on a hit
no-comments --helpEvery input below has a flag of the same name. Booleans need no value and take a --no-
form, and --ignore/--allow may be repeated:
no-comments --extensions '*.ts *.tsx' --ignore '^vendor/' --no-fail-on-violations.husky/pre-commit — strip and re-stage, so the commit lands clean:
mapfile -d '' -t files < <(git diff --cached --name-only --diff-filter=ACMR -z)
[ "${#files[@]}" -gt 0 ] || exit 0
pnpm exec no-comments --staged --fix
git add -- "${files[@]}".husky/pre-push — the commits already exist, so here it can only report:
pnpm exec no-comments- repo: local
hooks:
- id: no-comments
name: no-comments
entry: pnpm exec no-comments --staged --fix
language: system
pass_filenames: false
files: \.(ts|tsx|astro|css)$pass_filenames: false matters: the tool finds its own files through git, and passing
paths as bare arguments is an error.
Flags and with: are fine for one-offs, but a hook, CI and a manual run should agree.
Put the shared rules in no-comments.json at the repo root:
{
"extensions": ["*.ts", "*.tsx"],
"ignore": ["routeTree\\.gen\\.ts$", "/_generated/"],
"allow": ["^//\\s*@boundaries-ignore\\b"]
}A "no-comments" key in package.json works the same way. Precedence is flag > action
input > config file > built-in default, so CI can still override a single rule with
with: without the config file drifting from what runs locally.
| Input | Default | Description |
|---|---|---|
extensions |
*.ts *.tsx *.astro *.css |
Space separated git pathspecs to scan. |
ignore |
'' |
Newline or comma separated regexes; a tracked path matching any is skipped. |
allow |
'' |
Regexes matched against a comment; a hit is permitted. Added to the built-in directives. |
fix |
false |
Strip comments in place. |
suggest |
false |
Post review suggestions instead. Needs pull-requests: write. |
token |
${{ github.token }} |
Token used to post suggestions. |
annotations |
true |
Emit ::error annotations. |
fail-on-violations |
true |
Set false to adopt the check in warn-only mode. |
working-directory |
. |
Directory to run from. |
| Output | Description |
|---|---|
violations |
Number of comments found. |
files |
Number of files scanned. |
suggestions |
Number of review suggestions posted. |
Tooling directives are not prose, so these pass untouched:
/// <reference, @ts-expect-error, @ts-ignore, @ts-nocheck, @vitest-environment,
@vite-ignore, #__PURE__, prettier-ignore (line, block and HTML), biome-ignore,
eslint-disable.
Add your own with allow:
with:
allow: |
^//\s*@boundaries-ignore\b
^//\s*oxlint-disable- Only lines in the PR diff can carry a suggestion. A comment in a file the PR does not touch still fails the step and still gets an annotation, but GitHub has nowhere to anchor a suggestion to it. The action warns with a count when that happens.
- Re-runs do not duplicate. Suggestions carry a marker and already-posted ones at the same path and line range are skipped.
- Only
pull_requestevents. On anything else the action warns once and degrades to a plain check. - Posting failures are never fatal to the result. A 403 or a rate limit logs a warning; the violation count still decides the exit code.
- Forks. A
pull_requestrun from a fork gets a read-only token, so suggestions are skipped and only annotations appear.pull_request_targetwould supply a writable token, but it runs the workflow from the base branch while this action must read the PR's code — a privileged checkout of untrusted code. Preferpull_requestand accept annotations-only on fork PRs. GITHUB_TOKENis read-only in many orgs. Setpermissions: pull-requests: writeexplicitly, or the 403 is the first thing you will hit.
- Only tracked files are scanned — it runs
git ls-files(orgit diff --cachedunder--staged), soactions/checkoutmust run first. --stagedreads the file from the working tree, not the staged blob. With an unstaged edit on top of a staged one you are checking the former.- GitHub renders at most 10 annotations of a level per step. The full list is always in the step log.
.astromarkup is scanned with hand-written scanners rather than the Astro compiler. Frontmatter,<script>and<style>bodies go through real parsers;is:rawblocks andtype="*json*"scripts are skipped.
MIT