From 8715f9787743a0e08f3ac03a01b97a63f9bba1fe Mon Sep 17 00:00:00 2001 From: bitWarrior Date: Tue, 1 Sep 2026 13:19:10 -0700 Subject: [PATCH] Prepare the README and changelog for a public launch The first run on a mature codebase reports roughly ten warnings per file -- measured at 57 errors, 1862 warnings and 395 info over 167 stdlib files -- and a wall of findings is the usual reason a linter gets closed and never reopened. The README now opens on the low-noise path (errors first, then a baseline, then that as the CI command) before the full usage firehose. Add a "How it compares" section with measured numbers rather than claims. Ruff is roughly 13x faster and has 30x the rules, and the section says so plainly; being straight about that is worth more than the comparison flatters. Among the Python-implemented checkers CodeSnake is the quick one, about 5x faster than Bandit and 10x faster than pylint on the same files. Methodology and the pylint flags used are noted so the numbers can be checked. The header now leads with the two properties that actually differentiate the tool -- it never imports or executes analyzed code, and it has no runtime dependencies -- rather than with the AST implementation detail. Also adds CI, PyPI, Python-version and license badges, and CHANGELOG.md covering 1.1.0 and 1.2.0 in Keep a Changelog format. Verified: every internal README link resolves, the three onboarding commands work end to end on a fresh file, and the benchmark figures are best-of-run measurements on Python 3.12, not estimates. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0114gUUe4CxYr8W8oC95ffmD --- CHANGELOG.md | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 79 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4822711 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,101 @@ +# Changelog + +All notable changes to CodeSnake are documented here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.2.0] - 2026-09-01 + +A correctness release. Three defects made the tool quietly under-deliver, and six rules +missed legitimate spellings of patterns they already detected. 212 tests, up from 190. + +### Fixed + +- **`--format github` emits workspace-relative paths.** A directory argument produced + absolute paths in the `file=` property, and GitHub silently drops annotations whose + path does not match the workspace — so no annotation from `codesnake check --format + github src/` ever attached to a pull request diff. +- **`.gitignore` patterns holding a separator are anchored to their own directory,** + matching git. `sub/drop.py` no longer also ignores `other/sub/drop.py`, which git + keeps. Real source files were being skipped with nothing to indicate a short scan. +- **`codesnake config` refuses to overwrite an existing file** without `--force`, + instead of replacing tuned thresholds with defaults and reporting success. +- **A file that cannot be analyzed no longer sinks the run.** Analysis recurses per AST + node, so a long chained expression could exhaust the stack; the `RecursionError` + escaped as a traceback and no file in the run was reported. It is now contained per + file as a single `IO001`. +- **Overlapping targets are analyzed once.** `codesnake check pkg/a.py pkg/` analyzed + the same file twice and doubled every finding and count. +- **The banner honors `--no-color` and `NO_COLOR`,** so escape codes no longer leak + into CI logs and pre-commit output. +- **`max_*` thresholds must be 1 or greater.** `max_complexity: -1` was accepted and + flagged every function in the tree; it is now a config error naming the key. + +### Added + +- **Taint reaches sinks through keyword arguments.** `subprocess.run(args=input(), + shell=True)` is now the `error` the positional form always was. Shell sinks read + their command by name as well as by position. +- **EXC002** matches `builtins.Exception` and tuple clauses such as + `except (ValueError, Exception)`. +- **EXC005** covers a `raise` in the `else` of a `try` nested inside a handler. +- **`__all__` re-export forms** — `+=`, `.extend()`, and `.append()` now count as uses, + so a package `__init__.py` no longer reports every re-exported import as unused. +- **BUG002** detects duplicate tuple keys. + +### Changed + +- Documentation corrections across `docs/INTEGRATIONS.md` and + `docs/BASH_SCRIPTS_GUIDE.md`: per-format path behavior, `--staged` reading the working + tree rather than staged blobs, baselines being written and read from the same + directory, what fingerprints normalize (digits only), the VS Code `problemMatcher` + `fileLocation`, Python 3.10 config discovery, and which launcher scripts fall back to + `python`. +- CI actions moved past the Node.js 20 deprecation and are pinned to commit SHAs. +- The text report splits each file's source once instead of once per issue. + +### Security + +- `SECURITY.md` documents the threat model and a private reporting path. +- Workflow permissions are deny-by-default; the release workflow publishes to PyPI via + Trusted Publishing (OIDC) with no stored credential. + +## [1.1.0] - 2026-09-01 + +A correctness, precision, and packaging release. 189 tests, up from 91. + +### Added + +- **Configuration discovery** — `.codesnake.json` or a `pyproject.toml` + `[tool.codesnake]` table, found by walking up to the repository root. +- **Parallel analysis** — `-j/--jobs N`, automatic once 8 or more files are checked. +- **Stable baselines** with fingerprints that survive line-number drift. +- `SEC003` coverage for `os.system`, `os.popen`, and `subprocess.check_output`, + `check_call`, and `getoutput`; `SEC002` coverage for `dill`, `cloudpickle`, + `jsonpickle`, `marshal`, `shelve`, `pickle.Unpickler(...).load()`, and `yaml.load` + without a safe `Loader`. +- `check_reliability` category covering `REL002` and `ASY001`. + +### Fixed + +- Closures no longer produce false "unused variable" warnings; function bodies are + analyzed after their enclosing scope is fully bound. +- Constants are invalidated on reassignment. +- `x is 0` no longer triggers `STYLE001`; only real booleans do. +- `--staged` works from any subdirectory of the repository. +- Columns are character offsets rather than UTF-8 byte offsets, 1-based in `text`, + `github`, and `sarif` output. +- `except*` handlers and `match` bodies are checked. + +### Changed + +- Restructured into a `src/codesnake/` package with a single CLI; `codesnake FILES...` + is shorthand for `codesnake check FILES...`. +- Version single-sourced from `codesnake/_version.py`. +- MIT `LICENSE` added, with PEP 639 metadata; CI on Python 3.10 through 3.13. + +[Unreleased]: https://github.com/bitWarrior/codesnake/compare/v1.2.0...HEAD +[1.2.0]: https://github.com/bitWarrior/codesnake/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/bitWarrior/codesnake/releases/tag/v1.1.0 diff --git a/README.md b/README.md index 5a7f254..7abfe8d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,20 @@ # CodeSnake +[![CI](https://github.com/bitWarrior/codesnake/actions/workflows/ci.yml/badge.svg)](https://github.com/bitWarrior/codesnake/actions/workflows/ci.yml) +[![PyPI](https://img.shields.io/pypi/v/codesnake.svg)](https://pypi.org/project/codesnake/) +[![Python](https://img.shields.io/pypi/pyversions/codesnake.svg)](https://pypi.org/project/codesnake/) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) + Semantic code checker for Python 3. It parses files into an AST, walks them, and reports security problems, common bugs, unused names, and complexity smells. -Requires **Python 3.10+**. The checker itself has **no runtime dependencies** beyond the standard library. +Two properties set it apart from the fast general-purpose linters: + +- **It never imports or executes the code it analyzes.** Everything runs on the AST from `ast.parse`, so pointing it at untrusted Python — a fork's pull request, a submitted plugin — does not run that Python. +- **It has no runtime dependencies.** Standard library only, so it vendors cleanly, works air-gapped, and adds nothing to your supply chain. + +It also does light **taint tracking**: `eval()` on a literal is `info`, `eval()` on something derived from `input()` or `request.args` is an `error`. See [how it compares](#how-it-compares) to Ruff, Bandit, and pylint. + +Requires **Python 3.10+**. ## Install @@ -28,6 +40,28 @@ PYTHONPATH=src python -m codesnake file.py # straight from a checkout ./codesnake.sh file.py # creates/activates codesnake-venv/ ``` +## First run on an existing codebase + +CodeSnake reports complexity, length, and unused-name findings by default, so the +first run on a mature codebase is loud — expect roughly ten warnings per file. That +is a backlog, not an emergency: only `error` severity fails the run. Start narrow and +widen when you are ready. + +```bash +# 1. What would actually fail CI. Start here. +codesnake check --severity error src/ + +# 2. Snapshot everything else, so CI only fails on NEW findings. +codesnake check --update-baseline .codesnake-baseline.json src/ +git add .codesnake-baseline.json + +# 3. From now on, this is your CI command. +codesnake check --baseline .codesnake-baseline.json src/ +``` + +Then tune thresholds in `.codesnake.json` and shrink the baseline as you go. The full +adoption path is in [docs/INTEGRATIONS.md](docs/INTEGRATIONS.md#adopting-codesnake-on-an-existing-codebase). + ## Usage ```bash @@ -212,10 +246,53 @@ rc = run_check( Each `Issue` includes `line`, `col`, `end_line`, `end_col`, `suggestion`, and `source` (`codesnake` or `bandit`). `col` / `end_col` are **0-based character offsets** (AST byte offsets are converted), and JSON output reports them as-is. The `text`, `github`, and `sarif` formats print **1-based** columns. +## How it compares + +CodeSnake is not trying to replace Ruff. Use both. + +| | CodeSnake | Ruff | Bandit | pylint | +|---|---|---|---|---| +| Speed, 167 stdlib files | 1.7s | **0.14s** | 9.1s | 17.4s | +| Rules | ~25 | 800+ | ~70 security | 400+ | +| Runtime dependencies | **none** | none (Rust binary) | several | several | +| Imports the analyzed code | **never** | never | never | in some modes | +| Taint tracking | **yes** | no | limited | no | +| Autofix | no | **yes** | no | no | +| SARIF output | **yes** | no | **yes** | no | +| Baselines | **yes** | no | via `--baseline` | no | + +**Ruff is roughly 13x faster and has 30x the rules.** If you want one fast +general-purpose linter with autofix, use Ruff — CodeSnake is not competing for that job. +Among the Python-implemented checkers, though, CodeSnake is the quick one: about 5x +faster than Bandit and 10x faster than pylint on the same files. + +Measured on Python 3.12, best of 2–3 runs over the same 167 files from the standard +library, each tool using its own parallelism where it has any (`codesnake` auto, +`pylint -j 0`). pylint ran with `--disable=all --enable=W,E`, a reduced rule set in its +favor. Your numbers will differ; the ranking is the point, not the digits. + +CodeSnake is worth adding when you want one of these: + +- **Taint tracking.** `eval(x)` where `x` came from `input()` or `request.args` is an + `error`; `eval("1+1")` is `info`. The fast linters flag the call site without asking + where the data came from. +- **Analysis of untrusted code.** No import, no execution, no dependencies — safe to + run over a fork's PR or a user-submitted plugin. +- **A vendorable checker.** One pure-Python package with an empty dependency list, + auditable in an afternoon, no toolchain. +- **SARIF plus stable baselines**, for GitHub code scanning on a codebase with an + existing backlog. + +Bandit has far broader security coverage; `--bandit` merges its findings into the same +report if you want both. + ## Performance Files are analyzed in a process pool once there are 8 or more of them (one worker per CPU); pass `--jobs 1` for a strictly sequential run or `--jobs N` to pin the count. Output order is always the input order. +Roughly 100 files/second single-process on a modern laptop. CodeSnake is pure Python +doing a full AST walk per file; if analysis time dominates your CI, reach for Ruff. + ## Tests ```bash