Skip to content

Commit a7d0d9f

Browse files
committed
Add cants console script; drop CodeQL level-2 enrichment; v0.2.1
pip install now puts a `cants` launcher on PATH: a __main__.py shim exec()s the bundled binary (passing argv + exit code through), wired via [project.scripts]. CLDK still locates the binary directly via bin_path(); this is purely for direct shell use. Remove the stubbed CodeQL level-2 enrichment entirely (it emitted zero edges, so this is behavior-preserving). Deleted src/semantic_analysis/codeql/ and scrubbed all CodeQL references from source and docs. The -a/--analysis-level flag stays accepted (1|2) for CLDK compatibility; every run produces the symbol table + tsc resolver call graph + RTA. Also include the Homebrew formula generator and release workflow updates.
1 parent 9f7d444 commit a7d0d9f

14 files changed

Lines changed: 172 additions & 75 deletions

File tree

.github/workflows/release.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,32 @@ jobs:
127127
uses: pypa/gh-action-pypi-publish@release/v1
128128
with:
129129
packages-dir: packaging/python/dist
130+
131+
# ----- Homebrew: regenerate the formula from the just-built binaries and
132+
# push it to the shared tap. This is the non-Rust equivalent of what
133+
# cargo-dist does for you; it is independent of the PyPI path above and
134+
# only consumes the raw bun binaries in release-bins/.
135+
- name: Generate Homebrew formula
136+
if: startsWith(github.ref, 'refs/tags/')
137+
env:
138+
REPO: ${{ github.repository }}
139+
VERSION: ${{ steps.ver.outputs.version }}
140+
run: |
141+
./packaging/homebrew/generate_formula.sh release-bins > codeanalyzer-typescript.rb
142+
cat codeanalyzer-typescript.rb
143+
144+
- name: Push formula to codellm-devkit/homebrew-tap
145+
if: startsWith(github.ref, 'refs/tags/')
146+
env:
147+
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} # PAT with write access to homebrew-tap
148+
VERSION: ${{ steps.ver.outputs.version }}
149+
run: |
150+
git clone "https://x-access-token:${TAP_TOKEN}@github.com/codellm-devkit/homebrew-tap.git" tap
151+
mkdir -p tap/Formula
152+
cp codeanalyzer-typescript.rb tap/Formula/codeanalyzer-typescript.rb
153+
cd tap
154+
git config user.name "github-actions[bot]"
155+
git config user.email "github-actions[bot]@users.noreply.github.com"
156+
git add Formula/codeanalyzer-typescript.rb
157+
git commit -m "codeanalyzer-typescript ${VERSION}" || { echo "no formula change"; exit 0; }
158+
git push

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ Options:
7373
-o, --output <dir> output directory for analysis.json
7474
(omit ⇒ compact JSON to stdout)
7575
-f, --format <fmt> output format: json | msgpack (default: "json")
76-
-a, --analysis-level <n> 1 = tsc resolver call graph + RTA (default);
77-
2 = + CodeQL enrichment (default: "1")
76+
-a, --analysis-level <n> analysis depth: 1 = symbol table + tsc resolver
77+
call graph + RTA (default); 2 = call graph
78+
(default: "1")
7879
-t, --target-files <paths...> restrict analysis to specific files (incremental)
7980
--skip-tests skip test trees (default)
8081
--include-tests include test trees
@@ -112,14 +113,14 @@ Options:
112113
This saves the results to `analysis.msgpack`, a binary format that is more compact for
113114
storage and transmission.
114115

115-
3. **Deeper analysis with CodeQL enrichment (experimental):**
116+
3. **Selecting an analysis level:**
116117
```bash
117118
cants --input ./my-ts-project --analysis-level 2
118119
```
119120

120-
Every run produces a symbol table **and** a call graph. At level 1, edges come from the
121-
TypeScript compiler's resolver plus Rapid Type Analysis (RTA), with phantom nodes for
122-
calls into imported libraries. Level 2 additionally merges in CodeQL-derived edges.
121+
Every run produces a symbol table **and** a call graph. Edges come from the TypeScript
122+
compiler's resolver plus Rapid Type Analysis (RTA), with phantom nodes for calls into
123+
imported libraries.
123124

124125
4. **Incremental analysis of specific files:**
125126
```bash

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codeanalyzer-typescript",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"description": "CLDK TypeScript analyzer — emits the canonical CLDK analysis.json (symbol table + resolver-based call graph) via ts-morph.",
55
"type": "module",
66
"module": "src/index.ts",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Generate the Homebrew formula for the `cants` (codeanalyzer-typescript) binary.
4+
#
5+
# This is the non-Rust equivalent of what cargo-dist does automatically: it takes
6+
# the per-platform binaries that the release already publishes as GitHub Release
7+
# assets, computes their sha256, and emits a formula that downloads + installs the
8+
# matching binary for the user's platform.
9+
#
10+
# It reads the binaries straight from the release-bins/ dir built by release.yml,
11+
# so the checksums are guaranteed to match the bytes that were uploaded. The
12+
# download URLs point at the GitHub Release assets of the SAME tag.
13+
#
14+
# Usage:
15+
# REPO=codellm-devkit/codeanalyzer-ts VERSION=0.2.0 \
16+
# ./generate_formula.sh ../../release-bins > codeanalyzer-ts.rb
17+
#
18+
set -euo pipefail
19+
20+
BINS_DIR="${1:?usage: generate_formula.sh <release-bins-dir>}"
21+
REPO="${REPO:?set REPO, e.g. codellm-devkit/codeanalyzer-ts}"
22+
VERSION="${VERSION:?set VERSION, e.g. 0.2.0}"
23+
BASE_URL="https://github.com/${REPO}/releases/download/v${VERSION}"
24+
25+
sha() { shasum -a 256 "$1" | cut -d' ' -f1; }
26+
27+
# Map each release asset (named by Python wheel platform tag) to its Homebrew
28+
# os/arch block. Windows is intentionally omitted -- Homebrew is macOS/Linux only.
29+
asset_macos_arm="cants-macosx_11_0_arm64"
30+
asset_macos_intel="cants-macosx_10_12_x86_64"
31+
asset_linux_intel="cants-manylinux2014_x86_64"
32+
asset_linux_arm="cants-manylinux2014_aarch64"
33+
34+
for a in "$asset_macos_arm" "$asset_macos_intel" "$asset_linux_intel" "$asset_linux_arm"; do
35+
[[ -f "$BINS_DIR/$a" ]] || { echo "missing expected binary: $BINS_DIR/$a" >&2; exit 1; }
36+
done
37+
38+
cat <<EOF
39+
# This file is auto-generated by packaging/homebrew/generate_formula.sh on release.
40+
# Do not edit by hand -- changes will be overwritten on the next tag.
41+
class CodeanalyzerTypescript < Formula
42+
desc "CLDK TypeScript analyzer (cants) -- emits canonical CLDK analysis.json"
43+
homepage "https://github.com/${REPO}"
44+
version "${VERSION}"
45+
license "Apache-2.0"
46+
47+
on_macos do
48+
on_arm do
49+
url "${BASE_URL}/${asset_macos_arm}"
50+
sha256 "$(sha "$BINS_DIR/$asset_macos_arm")"
51+
end
52+
on_intel do
53+
url "${BASE_URL}/${asset_macos_intel}"
54+
sha256 "$(sha "$BINS_DIR/$asset_macos_intel")"
55+
end
56+
end
57+
58+
on_linux do
59+
on_arm do
60+
url "${BASE_URL}/${asset_linux_arm}"
61+
sha256 "$(sha "$BINS_DIR/$asset_linux_arm")"
62+
end
63+
on_intel do
64+
url "${BASE_URL}/${asset_linux_intel}"
65+
sha256 "$(sha "$BINS_DIR/$asset_linux_intel")"
66+
end
67+
end
68+
69+
def install
70+
# The release asset is a bare executable; Homebrew stages it under its
71+
# original (platform-tagged) name. Install it as the canonical "cants".
72+
bin.install Dir["cants-*"].first => "cants"
73+
end
74+
75+
test do
76+
# cants has no --version flag and requires -i; --help is handled first and
77+
# exits 0, so it is the safe smoke test that the binary runs on this platform.
78+
assert_match "Usage: cants", shell_output("#{bin}/cants --help")
79+
end
80+
end
81+
EOF

packaging/python/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ Options:
3535
-o, --output <dir> output directory for analysis.json
3636
(omit ⇒ compact JSON to stdout)
3737
-f, --format <fmt> output format: json | msgpack (default: "json")
38-
-a, --analysis-level <n> 1 = tsc resolver call graph + RTA (default);
39-
2 = + CodeQL enrichment (default: "1")
38+
-a, --analysis-level <n> analysis depth: 1 = symbol table + tsc resolver
39+
call graph + RTA (default); 2 = call graph
40+
(default: "1")
4041
-t, --target-files <paths...> restrict analysis to specific files (incremental)
4142
--skip-tests skip test trees (default)
4243
--include-tests include test trees
@@ -74,14 +75,14 @@ Options:
7475
This saves the results to `analysis.msgpack`, a binary format that is more compact for
7576
storage and transmission.
7677

77-
3. **Deeper analysis with CodeQL enrichment (experimental):**
78+
3. **Selecting an analysis level:**
7879
```bash
7980
cants --input ./my-ts-project --analysis-level 2
8081
```
8182

82-
Every run produces a symbol table **and** a call graph. At level 1, edges come from the
83-
TypeScript compiler's resolver plus Rapid Type Analysis (RTA), with phantom nodes for
84-
calls into imported libraries. Level 2 additionally merges in CodeQL-derived edges.
83+
Every run produces a symbol table **and** a call graph. Edges come from the TypeScript
84+
compiler's resolver plus Rapid Type Analysis (RTA), with phantom nodes for calls into
85+
imported libraries.
8586

8687
4. **Incremental analysis of specific files:**
8788
```bash

packaging/python/pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ classifiers = [
2424
Homepage = "https://codellm-devkit.info"
2525
Repository = "https://github.com/codellm-devkit/codeanalyzer-ts"
2626

27+
# Installs a `cants` launcher on PATH that exec()s the bundled binary
28+
# (see src/codeanalyzer_typescript/__main__.py). CLDK still locates the binary
29+
# directly via bin_path(); this is purely for direct shell use.
30+
[project.scripts]
31+
cants = "codeanalyzer_typescript.__main__:main"
32+
2733
# Single source of truth for the package version: __init__.py's __version__.
2834
# build_wheels.sh overwrites it from $PKG_VERSION (set to the git tag in CI), so a
2935
# tag of vX.Y.Z always produces a codeanalyzer-typescript==X.Y.Z wheel.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Console-script entry point: run the bundled ``cants`` binary.
2+
3+
``pip install codeanalyzer-typescript`` installs a ``cants`` launcher (see
4+
``[project.scripts]`` in ``pyproject.toml``) that calls :func:`main`. We locate
5+
the platform binary via :func:`bin_path` and hand off the process to it, passing
6+
through every CLI argument and the exit code unchanged.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import os
12+
import subprocess
13+
import sys
14+
15+
from . import bin_path
16+
17+
18+
def main() -> "int | None":
19+
"""Exec the bundled ``cants`` binary with the current argv (minus argv[0])."""
20+
binary = str(bin_path())
21+
args = [binary, *sys.argv[1:]]
22+
23+
if os.name == "posix":
24+
# Replace this process with the binary: no extra Python process lingers,
25+
# and signals/exit codes are handled by cants directly.
26+
os.execv(binary, args)
27+
# os.execv never returns on success.
28+
29+
# Windows has no execv that behaves like POSIX; spawn and propagate the code.
30+
return subprocess.call(args)
31+
32+
33+
if __name__ == "__main__":
34+
sys.exit(main())

src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export function parseArgs(argv: string[]): AnalysisOptions {
1111
.requiredOption("-i, --input <path>", "project root to analyze")
1212
.option("-o, --output <dir>", "output directory for analysis.json (omit ⇒ compact JSON to stdout)")
1313
.option("-f, --format <fmt>", "output format: json | msgpack", "json")
14-
.option("-a, --analysis-level <n>", "1 = tsc resolver call graph + RTA (default); 2 = + CodeQL enrichment", "1")
14+
.option("-a, --analysis-level <n>", "analysis depth: 1 = symbol table + tsc resolver call graph + RTA (default); 2 = call graph", "1")
1515
.option("-t, --target-files <paths...>", "restrict analysis to specific files (incremental)")
1616
.option("--skip-tests", "skip test trees (default)")
1717
.option("--include-tests", "include test trees")

src/core.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as path from "node:path";
22
import { buildCallGraph } from "./semantic_analysis";
33
import { loadCache, saveCache } from "./utils";
4-
import { buildCodeqlCallGraph, mergeEdges } from "./semantic_analysis";
54
import { materialize } from "./build";
65
import type { AnalysisOptions } from "./options";
76
import type { TSApplication } from "./schema";
@@ -10,7 +9,7 @@ import { Logger } from "./utils";
109

1110
/**
1211
* The orchestrator. Order mirrors the reference analyzers: materialize deps → build the symbol
13-
* table → (level ≥ 2) build the resolver call graph → cache the base → return the Application.
12+
* table → build the resolver call graph → cache the base → return the Application.
1413
*/
1514
export function analyze(opts: AnalysisOptions): TSApplication {
1615
const log = new Logger(opts.verbosity);
@@ -23,13 +22,9 @@ export function analyze(opts: AnalysisOptions): TSApplication {
2322
const cached = opts.eager ? null : loadCache(cacheDir);
2423
const { project, symbol_table } = buildSymbolTable(opts, mat, cached?.symbol_table ?? null, log);
2524

26-
// Level 1: the tsc (ts-morph checker) resolver call graph + RTA + phantom external nodes.
25+
// The tsc (ts-morph checker) resolver call graph + RTA + phantom external nodes.
2726
const cg = buildCallGraph(project, symbol_table, opts.input, log, opts.phantoms);
28-
let call_graph = cg.edges;
29-
// Level 2: enrich with CodeQL (merged by (source,target); stubbed for now).
30-
if (opts.analysisLevel >= 2) {
31-
call_graph = mergeEdges(call_graph, buildCodeqlCallGraph(opts, symbol_table, log));
32-
}
27+
const call_graph = cg.edges;
3328

3429
const app: TSApplication = {
3530
symbol_table,

src/options/options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export interface AnalysisOptions {
77
/** Output directory for analysis.json; null ⇒ print compact JSON to stdout. */
88
output: string | null;
99
format: OutputFormat;
10-
/** 1 = tsc resolver call graph (default); 2 = + CodeQL enrichment. */
10+
/** Analysis depth requested by the caller (1 = symbol table + call graph [default]; 2 = call graph). */
1111
analysisLevel: 1 | 2;
1212
/** Restrict analysis to these files (project-relative or absolute). null ⇒ whole project. */
1313
targetFiles: string[] | null;

0 commit comments

Comments
 (0)