Skip to content

Commit 3707647

Browse files
committed
feat(cli): rename command to canpy; auto-generate README --help block
Rename the CLI command from `codeanalyzer` to `canpy`, paralleling the TypeScript sibling's `cants`. The PyPI package (`codeanalyzer-python`) and the importable `codeanalyzer` module are unchanged — only the console-script entry point, the Typer app name, and user-facing docs/installer change. - pyproject.toml: console-script entry point `canpy` - __main__.py: Typer app name `canpy` - packaging/install: rename installer to canpy-installer.sh; CANPY_* env vars - scripts/update_readme.py: render `canpy --help` into the README between <!-- BEGIN/END canpy-help --> markers (mirrors codeanalyzer-typescript's scripts/update-readme.ts), so the documented options can't drift from the CLI - release.yml: sync the README --help block alongside schema.neo4j.json before publishing, and stage canpy-installer.sh as a release asset - README/CHANGELOG: command + installer references; regenerated --help block (also surfaces previously-undocumented --ray/--skip-tests/--file-name)
1 parent fc220e0 commit 3707647

9 files changed

Lines changed: 231 additions & 123 deletions

File tree

.github/workflows/release.yml

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,23 @@ jobs:
3434
- name: Install dependencies
3535
run: uv pip install -e ".[neo4j]"
3636

37-
# Keep the checked-in Neo4j schema contract in lockstep with the code being
38-
# released: regenerate schema.neo4j.json from source and commit it back to main.
39-
# Releases are cut from main HEAD, so this fast-forwards; best-effort if main moved.
40-
- name: Sync Neo4j schema contract (schema.neo4j.json)
37+
# Keep generated docs in lockstep with the code being released: regenerate the
38+
# README `canpy --help` block and the Neo4j schema.json from source, and commit
39+
# them back to main. Releases are cut from main HEAD, so this fast-forwards;
40+
# best-effort if main moved.
41+
- name: Sync generated docs (README --help + Neo4j schema)
4142
if: startsWith(github.ref, 'refs/tags/')
4243
run: |
43-
uv run codeanalyzer --emit schema > schema.neo4j.json
44-
if git diff --quiet schema.neo4j.json; then
45-
echo "Neo4j schema already current."
44+
uv run python scripts/update_readme.py
45+
uv run canpy --emit schema > schema.neo4j.json
46+
if git diff --quiet README.md schema.neo4j.json; then
47+
echo "Generated docs already current."
4648
else
4749
git config user.name "github-actions[bot]"
4850
git config user.email "github-actions[bot]@users.noreply.github.com"
49-
git add schema.neo4j.json
50-
git commit -m "docs: sync Neo4j schema contract for ${GITHUB_REF#refs/tags/}"
51-
git push origin HEAD:main || echo "::warning::could not push schema sync to main (diverged?)"
51+
git add README.md schema.neo4j.json
52+
git commit -m "docs: sync README --help and Neo4j schema for ${GITHUB_REF#refs/tags/}"
53+
git push origin HEAD:main || echo "::warning::could not push doc sync to main (diverged?)"
5254
fi
5355
5456
- name: Run tests
@@ -75,8 +77,8 @@ jobs:
7577
- name: Stage release assets (Neo4j schema + installer script)
7678
run: |
7779
mkdir -p release-assets
78-
uv run codeanalyzer --emit schema > release-assets/schema.json
79-
cp packaging/install/codeanalyzer-installer.sh release-assets/codeanalyzer-installer.sh
80+
uv run canpy --emit schema > release-assets/schema.json
81+
cp packaging/install/canpy-installer.sh release-assets/canpy-installer.sh
8082
ls -lh release-assets
8183
8284
- name: Get version from tag

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- **`codeanalyzer.neo4j`** package: `catalog` (the single source-of-truth schema catalog), `project` (pure IR → graph rows), `cypher` (snapshot writer), `bolt` (incremental writer), and `rows` (the output-agnostic intermediate).
1717
- **Schema conformance test** (`test/test_neo4j_schema.py`, always runs) — asserts the emitter never produces a label/relationship/property the catalog doesn't declare, and that the checked-in `schema.neo4j.json` is regenerated.
1818
- **Neo4j Testcontainers integration test** (`test/test_neo4j_bolt.py`, opt-in via `RUN_CONTAINER_TESTS=1`) — spins up a real Neo4j and asserts the pushed graph, idempotent re-push, vanished-declaration cleanup, and full-run orphan pruning.
19-
- **Install script** (`packaging/install/codeanalyzer-installer.sh`) — a `curl … | sh` installer that provisions the CLI via uv / pipx / pip, published as a release asset.
19+
- **Install script** (`packaging/install/canpy-installer.sh`) — a `curl … | sh` installer that provisions the CLI via uv / pipx / pip, published as a release asset.
2020
- **`schema-uml.drawio`** — a clean UML of the `analysis.json` schema (the `PyApplication` containment tree).
2121

2222
### Changed
23-
- The release workflow now installs the `[neo4j]` extra, syncs `schema.neo4j.json` from source before publishing, and uploads the schema contract (`schema.json`) and installer script as GitHub Release assets.
23+
- **The CLI command is now `canpy`** (was `codeanalyzer`), matching the `cants` (TypeScript) sibling. The PyPI package name is unchanged (`codeanalyzer-python`), as is the importable `codeanalyzer` module.
24+
- The README `canpy --help` block is now generated from the live CLI (`scripts/update_readme.py`, between `<!-- BEGIN/END canpy-help -->` markers) so it can't drift from the code.
25+
- The release workflow now installs the `[neo4j]` extra, syncs both the README `--help` block and `schema.neo4j.json` from source before publishing, and uploads the schema contract (`schema.json`) and installer script as GitHub Release assets.
2426

2527
## [0.1.15] - 2026-05-15
2628

README.md

Lines changed: 77 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pip install 'codeanalyzer-python[neo4j]'
1919
Or install the CLI as an isolated tool with the one-line installer (provisions via uv / pipx / pip):
2020

2121
```bash
22-
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/codellm-devkit/codeanalyzer-python/releases/latest/download/codeanalyzer-installer.sh | sh
22+
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/codellm-devkit/codeanalyzer-python/releases/latest/download/canpy-installer.sh | sh
2323
```
2424

2525
### Prerequisites
@@ -68,98 +68,136 @@ pyenv global 3.12.0 # or pyenv local 3.12.0 for project-specific
6868
6969
## Usage
7070

71-
The codeanalyzer provides a command-line interface for performing static analysis on Python projects.
71+
`canpy` provides a command-line interface for performing static analysis on Python projects.
7272

7373
### Basic Usage
7474

7575
```bash
76-
codeanalyzer --input /path/to/python/project
76+
canpy --input /path/to/python/project
7777
```
7878

7979
### Command Line Options
8080

81-
To view the available options and commands, run `codeanalyzer --help`. You should see output similar to the following:
81+
To view the available options and commands, run `canpy --help`. You should see output similar to the following:
8282

83-
```bash
84-
❯ codeanalyzer --help
83+
<!-- BEGIN canpy-help -->
8584

86-
Usage: codeanalyzer [OPTIONS] COMMAND [ARGS]...
85+
```text
86+
$ canpy --help
8787
88-
Static Analysis on Python source code using Jedi, CodeQL and Tree sitter.
88+
Usage: canpy [OPTIONS] COMMAND [ARGS]...
8989
90+
Static Analysis on Python source code using Jedi, CodeQL and Tree sitter.
9091
91-
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
92-
│ --input -i PATH Path to the project root directory (not required for --emit schema). │
93-
│ --output -o PATH Output directory for artifacts. [default: None] │
94-
│ --format -f [json|msgpack] Output format for --emit json: json or msgpack. [default: json] │
95-
│ --emit [json|neo4j| Output target: json (analysis.json) | neo4j (graph.cypher or live │
96-
│ schema] Bolt push) | schema (the Neo4j schema.json contract). [default: json]│
97-
│ --app-name TEXT Logical application name for the graph :PyApplication anchor. │
98-
│ --neo4j-uri TEXT Push the graph to a live Neo4j over Bolt. [env: NEO4J_URI] │
99-
│ --neo4j-user TEXT Neo4j username. [env: NEO4J_USERNAME] [default: neo4j] │
100-
│ --neo4j-password TEXT Neo4j password. [env: NEO4J_PASSWORD] [default: neo4j] │
101-
│ --neo4j-database TEXT Neo4j database name. [env: NEO4J_DATABASE] │
102-
│ --codeql --no-codeql Enable CodeQL-based analysis. [default: no-codeql] │
103-
│ --eager --lazy Enable eager or lazy analysis. Defaults to lazy. [default: lazy] │
104-
│ --cache-dir -c PATH Directory to store analysis cache. [default: None] │
105-
│ --clear-cache --keep-cache Clear cache after analysis. [default: keep-cache] │
106-
│ -v INTEGER Increase verbosity: -v, -vv, -vvv [default: 0] │
107-
│ --help Show this message and exit. │
108-
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
92+
╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮
93+
│ --input -i PATH Path to the project root directory │
94+
│ (not required for --emit schema). │
95+
│ --output -o PATH Output directory for artifacts. │
96+
│ --format -f [json|msgpack] Output format for --emit json: │
97+
│ json or msgpack. │
98+
│ [default: json] │
99+
│ --emit [json|neo4j|schema] Output target: json │
100+
│ (analysis.json, default) | neo4j │
101+
│ (graph.cypher or live Bolt push) | │
102+
│ schema (the Neo4j schema.json │
103+
│ contract). │
104+
│ [default: json] │
105+
│ --app-name TEXT Logical application name for the │
106+
│ graph :PyApplication anchor │
107+
│ (default: input dir name). │
108+
│ --neo4j-uri TEXT Push the graph to a live Neo4j │
109+
│ over Bolt (incremental); omit to │
110+
│ write graph.cypher. │
111+
│ [env var: NEO4J_URI] │
112+
│ --neo4j-user TEXT Neo4j username. │
113+
│ [env var: NEO4J_USERNAME] │
114+
│ [default: neo4j] │
115+
│ --neo4j-password TEXT Neo4j password. Prefer the env var │
116+
│ over the flag (the flag is visible │
117+
│ in shell history / process list). │
118+
│ [env var: NEO4J_PASSWORD] │
119+
│ [default: neo4j] │
120+
│ --neo4j-database TEXT Neo4j database name (default: │
121+
│ server default). │
122+
│ [env var: NEO4J_DATABASE] │
123+
│ --codeql --no-codeql Enable CodeQL-based analysis. │
124+
│ [default: no-codeql] │
125+
│ --ray --no-ray Enable Ray for distributed │
126+
│ analysis. │
127+
│ [default: no-ray] │
128+
│ --eager --lazy Enable eager or lazy analysis. │
129+
│ Defaults to lazy. │
130+
│ [default: lazy] │
131+
│ --skip-tests --include-tests Skip test files in analysis. │
132+
│ [default: skip-tests] │
133+
│ --file-name PATH Analyze only the specified file │
134+
│ (relative to input directory). │
135+
│ --cache-dir -c PATH Directory to store analysis cache. │
136+
│ Defaults to '.codeanalyzer' in the │
137+
│ input directory. │
138+
│ --clear-cache --keep-cache Clear cache after analysis. By │
139+
│ default, cache is retained. │
140+
│ [default: keep-cache] │
141+
│ -v INTEGER Increase verbosity: -v, -vv, -vvv │
142+
│ [default: 0] │
143+
│ --help Show this message and exit. │
144+
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
109145
```
110146

147+
<!-- END canpy-help -->
148+
111149
### Examples
112150

113151
1. **Basic analysis with symbol table:**
114152
```bash
115-
codeanalyzer --input ./my-python-project
153+
canpy --input ./my-python-project
116154
```
117155

118156
This will print the symbol table to stdout in JSON format. If you want to save the output, you can use the `--output` option.
119157

120158
```bash
121-
codeanalyzer --input ./my-python-project --output /path/to/analysis-results
159+
canpy --input ./my-python-project --output /path/to/analysis-results
122160
```
123161

124162
Now, you can find the analysis results in `analysis.json` in the specified directory.
125163

126164
2. **Change output format to msgpack:**
127165
```bash
128-
codeanalyzer --input ./my-python-project --output /path/to/analysis-results --format msgpack
166+
canpy --input ./my-python-project --output /path/to/analysis-results --format msgpack
129167
```
130168

131169
This will save the analysis results in `analysis.msgpack` in the specified directory.
132170

133171
3. **Analysis with CodeQL enabled:**
134172
```bash
135-
codeanalyzer --input ./my-python-project --codeql
173+
canpy --input ./my-python-project --codeql
136174
```
137175
Every run produces a symbol table **and** a call graph. By default, edges come from Jedi's lexical analysis. Adding `--codeql` resolves additional edges (including RPC / third-party / dynamically-dispatched targets) and merges them with the Jedi-derived edges. CodeQL also backfills resolved callees on Jedi-emitted call sites where Jedi couldn't resolve them.
138176

139177
***Note: CodeQL integration is experimental. The CLI is downloaded into `<cache_dir>/codeql/` on first use and reused thereafter.***
140178

141179
4. **Eager analysis with custom cache directory:**
142180
```bash
143-
codeanalyzer --input ./my-python-project --eager --cache-dir /path/to/custom-cache
181+
canpy --input ./my-python-project --eager --cache-dir /path/to/custom-cache
144182
```
145183
This will rebuild the analysis cache at every run and store it in `/path/to/custom-cache/.codeanalyzer`.
146184

147185
5. **Emit a Neo4j snapshot, or push to a live database:**
148186
```bash
149-
codeanalyzer --input ./my-python-project --emit neo4j --output ./out # → ./out/graph.cypher
150-
codeanalyzer --input ./my-python-project --emit neo4j \
187+
canpy --input ./my-python-project --emit neo4j --output ./out # → ./out/graph.cypher
188+
canpy --input ./my-python-project --emit neo4j \
151189
--neo4j-uri bolt://localhost:7687 --neo4j-user neo4j --neo4j-password secret
152190
```
153191

154192
6. **Emit the Neo4j schema contract:**
155193
```bash
156-
codeanalyzer --emit schema # print schema.json to stdout (no project needed)
157-
codeanalyzer --emit schema --output ./out # → ./out/schema.json
194+
canpy --emit schema # print schema.json to stdout (no project needed)
195+
canpy --emit schema --output ./out # → ./out/schema.json
158196
```
159197

160198
## Output targets
161199

162-
`codeanalyzer` builds one analysis in memory and can emit it three ways (`--emit`):
200+
`canpy` builds one analysis in memory and can emit it three ways (`--emit`):
163201

164202
### `analysis.json` (default)
165203

@@ -188,7 +226,7 @@ The connection options also read from the standard Neo4j environment variables
188226
```sh
189227
export NEO4J_URI=bolt://localhost:7687
190228
export NEO4J_PASSWORD=secret
191-
codeanalyzer -i ./my-project --emit neo4j # credentials picked up from the environment
229+
canpy -i ./my-project --emit neo4j # credentials picked up from the environment
192230
```
193231

194232
### Schema contract
@@ -220,8 +258,8 @@ This project uses [uv](https://docs.astral.sh/uv/) for dependency management dur
220258
### Running from Source
221259

222260
```bash
223-
uv run codeanalyzer --input /path/to/python/project
224-
uv run codeanalyzer --emit schema > schema.neo4j.json # regenerate the checked-in schema contract
261+
uv run canpy --input /path/to/python/project
262+
uv run canpy --emit schema > schema.neo4j.json # regenerate the checked-in schema contract
225263
```
226264

227265
### Running Tests

codeanalyzer/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def _write_output(artifacts, output_dir: Path, format: OutputFormat):
220220

221221
app = typer.Typer(
222222
callback=main,
223-
name="codeanalyzer",
223+
name="canpy",
224224
help="Static Analysis on Python source code using Jedi, CodeQL and Tree sitter.",
225225
invoke_without_command=True,
226226
no_args_is_help=True,

0 commit comments

Comments
 (0)