callix 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- callix-0.1.0/.github/workflows/CI.yml +53 -0
- callix-0.1.0/.github/workflows/bench.yml +167 -0
- callix-0.1.0/.github/workflows/release.yml +172 -0
- callix-0.1.0/.gitignore +75 -0
- callix-0.1.0/CHANGELOG.md +29 -0
- callix-0.1.0/CLAUDE.md +172 -0
- callix-0.1.0/Cargo.lock +3225 -0
- callix-0.1.0/Cargo.toml +69 -0
- callix-0.1.0/PKG-INFO +358 -0
- callix-0.1.0/README.md +344 -0
- callix-0.1.0/benchmarks/README.md +65 -0
- callix-0.1.0/benchmarks/Taskfile.yaml +67 -0
- callix-0.1.0/benchmarks/projects.json +61 -0
- callix-0.1.0/benchmarks/results/gin-gonic__gin.json +23 -0
- callix-0.1.0/benchmarks/run_benchmarks.py +581 -0
- callix-0.1.0/build.rs +146 -0
- callix-0.1.0/cliff.toml +95 -0
- callix-0.1.0/example/test.py +132 -0
- callix-0.1.0/go/bridge.go +142 -0
- callix-0.1.0/go/bridge_go.go +229 -0
- callix-0.1.0/go/go.mod +26 -0
- callix-0.1.0/go/go.sum +26 -0
- callix-0.1.0/pyproject.toml +26 -0
- callix-0.1.0/python/callix/__init__.py +184 -0
- callix-0.1.0/python/callix/_core/__init__.pyi +994 -0
- callix-0.1.0/python/callix/py.typed +0 -0
- callix-0.1.0/python/callix/ty.py +50 -0
- callix-0.1.0/src/bin/stub_gen.rs +37 -0
- callix-0.1.0/src/boundaries.rs +168 -0
- callix-0.1.0/src/diffing.rs +146 -0
- callix-0.1.0/src/error.rs +32 -0
- callix-0.1.0/src/golang/adapter.rs +542 -0
- callix-0.1.0/src/golang/boundary.rs +320 -0
- callix-0.1.0/src/golang/deps.rs +159 -0
- callix-0.1.0/src/golang/mod.rs +39 -0
- callix-0.1.0/src/golang/resolver.rs +217 -0
- callix-0.1.0/src/golang/visitor.rs +469 -0
- callix-0.1.0/src/graph.rs +448 -0
- callix-0.1.0/src/ids.rs +34 -0
- callix-0.1.0/src/lib.rs +145 -0
- callix-0.1.0/src/metrics.rs +93 -0
- callix-0.1.0/src/node.rs +165 -0
- callix-0.1.0/src/occurrence.rs +113 -0
- callix-0.1.0/src/python/adapter.rs +990 -0
- callix-0.1.0/src/python/boundary.rs +520 -0
- callix-0.1.0/src/python/deps.rs +200 -0
- callix-0.1.0/src/python/detector.rs +119 -0
- callix-0.1.0/src/python/mod.rs +120 -0
- callix-0.1.0/src/python/module_resolver.rs +83 -0
- callix-0.1.0/src/python/ty_embedded.rs +195 -0
- callix-0.1.0/src/python/visitor.rs +917 -0
- callix-0.1.0/src/relation.rs +138 -0
- callix-0.1.0/src/roots.rs +220 -0
- callix-0.1.0/src/rustlang/adapter.rs +567 -0
- callix-0.1.0/src/rustlang/boundary.rs +327 -0
- callix-0.1.0/src/rustlang/deps.rs +145 -0
- callix-0.1.0/src/rustlang/mod.rs +40 -0
- callix-0.1.0/src/rustlang/resolver.rs +378 -0
- callix-0.1.0/src/rustlang/scip.rs +175 -0
- callix-0.1.0/src/rustlang/visitor.rs +394 -0
- callix-0.1.0/src/serde.rs +148 -0
- callix-0.1.0/src/serialization.rs +153 -0
- callix-0.1.0/src/span.rs +53 -0
- callix-0.1.0/src/span_index.rs +120 -0
- callix-0.1.0/src/status.rs +94 -0
- callix-0.1.0/src/ts.rs +130 -0
- callix-0.1.0/src/typescript/adapter.rs +412 -0
- callix-0.1.0/src/typescript/boundary.rs +352 -0
- callix-0.1.0/src/typescript/detector.rs +214 -0
- callix-0.1.0/src/typescript/mod.rs +109 -0
- callix-0.1.0/src/typescript/module_resolver.rs +247 -0
- callix-0.1.0/src/typescript/resolver.rs +181 -0
- callix-0.1.0/src/typescript/visitor.rs +1309 -0
- callix-0.1.0/taskfile.dist.yaml +186 -0
- callix-0.1.0/uv.lock +7 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
env:
|
|
13
|
+
CARGO_TERM_COLOR: always
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
# The ty/ruff tree is about 200 crates; a cold build takes a long time,
|
|
19
|
+
# a warm one finishes in a few minutes.
|
|
20
|
+
timeout-minutes: 60
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v6
|
|
23
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
24
|
+
# Go is only needed at build time: the typescript-go bridge is linked
|
|
25
|
+
# statically, and users get a prebuilt wheel.
|
|
26
|
+
- uses: actions/setup-go@v5
|
|
27
|
+
with:
|
|
28
|
+
go-version: "1.26"
|
|
29
|
+
cache-dependency-path: go/go.sum
|
|
30
|
+
- uses: astral-sh/setup-uv@v7
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.13"
|
|
33
|
+
- uses: arduino/setup-task@v2
|
|
34
|
+
|
|
35
|
+
# Without the cache every run would rebuild all of ty from scratch.
|
|
36
|
+
- uses: Swatinem/rust-cache@v2
|
|
37
|
+
with:
|
|
38
|
+
cache-all-crates: true
|
|
39
|
+
|
|
40
|
+
- name: Install
|
|
41
|
+
run: task install
|
|
42
|
+
|
|
43
|
+
- name: Lint
|
|
44
|
+
run: task lint
|
|
45
|
+
|
|
46
|
+
- name: Build
|
|
47
|
+
run: task build
|
|
48
|
+
|
|
49
|
+
- name: Stubs are up to date
|
|
50
|
+
run: task stubs:check
|
|
51
|
+
|
|
52
|
+
- name: Example
|
|
53
|
+
run: task example
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
name: Benchmark
|
|
2
|
+
|
|
3
|
+
# A load benchmark on real-world projects — report only, it never gates a
|
|
4
|
+
# release. The matrix runs per language: one hung resolver neither blocks the
|
|
5
|
+
# others nor eats their time.
|
|
6
|
+
#
|
|
7
|
+
# - workflow_dispatch — every language runs, and the README is refreshed and
|
|
8
|
+
# committed; run it by hand after a release when all the numbers are wanted.
|
|
9
|
+
# - pull_request — only the languages whose adapter the PR touches. The result
|
|
10
|
+
# goes to the job summary; the README is left alone.
|
|
11
|
+
on:
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: ["*"]
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
changes:
|
|
21
|
+
if: github.event_name == 'pull_request'
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
outputs:
|
|
24
|
+
# paths-filter's `changes` output is already a JSON array of the filters
|
|
25
|
+
# that matched — exactly what the matrix below needs.
|
|
26
|
+
langs: ${{ steps.filter.outputs.changes }}
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v6
|
|
29
|
+
- uses: dorny/paths-filter@v3
|
|
30
|
+
id: filter
|
|
31
|
+
with:
|
|
32
|
+
filters: |
|
|
33
|
+
python:
|
|
34
|
+
- "src/python/**"
|
|
35
|
+
- "benchmarks/**"
|
|
36
|
+
- ".github/workflows/bench.yml"
|
|
37
|
+
typescript:
|
|
38
|
+
- "src/typescript/**"
|
|
39
|
+
- "go/bridge.go"
|
|
40
|
+
- "benchmarks/**"
|
|
41
|
+
- ".github/workflows/bench.yml"
|
|
42
|
+
go:
|
|
43
|
+
- "src/golang/**"
|
|
44
|
+
- "go/bridge_go.go"
|
|
45
|
+
- "benchmarks/**"
|
|
46
|
+
- ".github/workflows/bench.yml"
|
|
47
|
+
rust:
|
|
48
|
+
- "src/rustlang/**"
|
|
49
|
+
- "benchmarks/**"
|
|
50
|
+
- ".github/workflows/bench.yml"
|
|
51
|
+
|
|
52
|
+
benchmark:
|
|
53
|
+
if: >-
|
|
54
|
+
github.event_name == 'workflow_dispatch' ||
|
|
55
|
+
(needs.changes.result == 'success' && needs.changes.outputs.langs != '[]')
|
|
56
|
+
needs: [changes]
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
timeout-minutes: 120
|
|
59
|
+
strategy:
|
|
60
|
+
fail-fast: false
|
|
61
|
+
matrix:
|
|
62
|
+
lang: >-
|
|
63
|
+
${{ github.event_name == 'workflow_dispatch'
|
|
64
|
+
&& fromJSON('["python","typescript","go","rust"]')
|
|
65
|
+
|| fromJSON(needs.changes.outputs.langs) }}
|
|
66
|
+
defaults:
|
|
67
|
+
run:
|
|
68
|
+
shell: bash
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v6
|
|
71
|
+
|
|
72
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
73
|
+
# Go is needed both to build the bridge and as the toolchain the Go
|
|
74
|
+
# resolver drives.
|
|
75
|
+
- uses: actions/setup-go@v5
|
|
76
|
+
with:
|
|
77
|
+
go-version: "1.26"
|
|
78
|
+
cache-dependency-path: go/go.sum
|
|
79
|
+
- uses: astral-sh/setup-uv@v7
|
|
80
|
+
with:
|
|
81
|
+
python-version: "3.13"
|
|
82
|
+
- uses: arduino/setup-task@v2
|
|
83
|
+
with:
|
|
84
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
85
|
+
- uses: Swatinem/rust-cache@v2
|
|
86
|
+
with:
|
|
87
|
+
cache-all-crates: true
|
|
88
|
+
|
|
89
|
+
- name: Install rust-analyzer (the Rust resolver)
|
|
90
|
+
if: matrix.lang == 'rust'
|
|
91
|
+
run: |
|
|
92
|
+
rustup component add rust-analyzer
|
|
93
|
+
echo "$(dirname "$(rustup which rust-analyzer)")" >> "$GITHUB_PATH"
|
|
94
|
+
|
|
95
|
+
- name: Build
|
|
96
|
+
run: |
|
|
97
|
+
task install
|
|
98
|
+
task build
|
|
99
|
+
|
|
100
|
+
- name: Benchmark ${{ matrix.lang }}
|
|
101
|
+
run: task bench:run BENCH_LANG=${{ matrix.lang }}
|
|
102
|
+
|
|
103
|
+
- name: Report
|
|
104
|
+
if: always()
|
|
105
|
+
run: |
|
|
106
|
+
{
|
|
107
|
+
echo "## callix benchmark — ${{ matrix.lang }}"
|
|
108
|
+
echo
|
|
109
|
+
task bench:show VERSION="${{ github.sha }}"
|
|
110
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
111
|
+
|
|
112
|
+
- uses: actions/upload-artifact@v4
|
|
113
|
+
if: always()
|
|
114
|
+
with:
|
|
115
|
+
name: benchmark-results-${{ matrix.lang }}
|
|
116
|
+
path: benchmarks/results/
|
|
117
|
+
|
|
118
|
+
# Aggregates every language's results and refreshes the README. Manual runs
|
|
119
|
+
# only: a PR never commits anything.
|
|
120
|
+
render:
|
|
121
|
+
if: always() && github.event_name == 'workflow_dispatch'
|
|
122
|
+
needs: benchmark
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
permissions:
|
|
125
|
+
contents: write
|
|
126
|
+
concurrency:
|
|
127
|
+
group: benchmark-render
|
|
128
|
+
cancel-in-progress: false
|
|
129
|
+
steps:
|
|
130
|
+
- uses: actions/checkout@v6
|
|
131
|
+
with:
|
|
132
|
+
fetch-depth: 0
|
|
133
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
134
|
+
- uses: arduino/setup-task@v2
|
|
135
|
+
with:
|
|
136
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
137
|
+
|
|
138
|
+
- name: Download results
|
|
139
|
+
uses: actions/download-artifact@v4
|
|
140
|
+
with:
|
|
141
|
+
pattern: benchmark-results-*
|
|
142
|
+
path: benchmarks/results
|
|
143
|
+
merge-multiple: true
|
|
144
|
+
|
|
145
|
+
- name: Render into README
|
|
146
|
+
# benchmarks/results/ holds the committed results of the previous run
|
|
147
|
+
# overlaid with the fresh ones: languages that did not run this time
|
|
148
|
+
# keep their last known numbers.
|
|
149
|
+
run: task bench:render VERSION="${{ github.ref_name }}"
|
|
150
|
+
|
|
151
|
+
- name: Commit
|
|
152
|
+
run: |
|
|
153
|
+
if git diff --quiet -- README.md benchmarks/results; then
|
|
154
|
+
echo "Nothing changed"
|
|
155
|
+
exit 0
|
|
156
|
+
fi
|
|
157
|
+
git config user.name "github-actions[bot]"
|
|
158
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
159
|
+
git add README.md benchmarks/results
|
|
160
|
+
git commit -m "docs(bench): refresh benchmark metrics [skip ci]"
|
|
161
|
+
for i in 1 2 3 4; do
|
|
162
|
+
git push origin HEAD:main && exit 0
|
|
163
|
+
git pull --rebase origin main || true
|
|
164
|
+
sleep "$((2 ** i))"
|
|
165
|
+
done
|
|
166
|
+
echo "::error::could not push the README update"
|
|
167
|
+
exit 1
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# 1. Version, changelog, tag. Wheels are built from that tag.
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
version:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
env:
|
|
18
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
19
|
+
outputs:
|
|
20
|
+
tag: ${{ steps.version.outputs.tag }}
|
|
21
|
+
version: ${{ steps.version.outputs.version }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
fetch-tags: true
|
|
27
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
28
|
+
|
|
29
|
+
- name: Configure git
|
|
30
|
+
run: |
|
|
31
|
+
git config user.name "github-actions[bot]"
|
|
32
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
33
|
+
|
|
34
|
+
- uses: arduino/setup-task@v2
|
|
35
|
+
with:
|
|
36
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
37
|
+
- uses: kenji-miyake/setup-git-cliff@v2
|
|
38
|
+
|
|
39
|
+
- name: Determine next version
|
|
40
|
+
id: version
|
|
41
|
+
run: task release:version
|
|
42
|
+
|
|
43
|
+
- name: Bump version
|
|
44
|
+
run: task release:bump VERSION=${{ steps.version.outputs.version }}
|
|
45
|
+
|
|
46
|
+
- name: Generate changelog
|
|
47
|
+
run: task release:changelog TAG=${{ steps.version.outputs.tag }}
|
|
48
|
+
|
|
49
|
+
- name: Commit and tag
|
|
50
|
+
run: task release:commit TAG=${{ steps.version.outputs.tag }}
|
|
51
|
+
|
|
52
|
+
- name: Push
|
|
53
|
+
run: task release:push TAG=${{ steps.version.outputs.tag }}
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# 2. Wheels. One wheel cannot cover every platform: ty and the Go bridge to
|
|
57
|
+
# typescript-go are linked inside. Thanks to abi3-py313 the matrix runs
|
|
58
|
+
# over platforms only, not over Python versions.
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
wheels:
|
|
61
|
+
needs: version
|
|
62
|
+
runs-on: ${{ matrix.os }}
|
|
63
|
+
timeout-minutes: 90
|
|
64
|
+
strategy:
|
|
65
|
+
fail-fast: false
|
|
66
|
+
matrix:
|
|
67
|
+
include:
|
|
68
|
+
- os: ubuntu-latest
|
|
69
|
+
target: x86_64
|
|
70
|
+
# A native ARM runner rather than QEMU inside the container: the ty
|
|
71
|
+
# tree alone is ~200 crates, and emulating that would not fit the
|
|
72
|
+
# timeout.
|
|
73
|
+
- os: ubuntu-24.04-arm
|
|
74
|
+
target: aarch64
|
|
75
|
+
- os: macos-latest
|
|
76
|
+
target: aarch64
|
|
77
|
+
# macos-latest is Apple Silicon, so the Intel wheel is cross-built;
|
|
78
|
+
# build.rs passes the architecture on to Go and clang.
|
|
79
|
+
- os: macos-latest
|
|
80
|
+
target: x86_64
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/checkout@v6
|
|
83
|
+
with:
|
|
84
|
+
ref: ${{ needs.version.outputs.tag }}
|
|
85
|
+
|
|
86
|
+
- uses: actions/setup-go@v5
|
|
87
|
+
with:
|
|
88
|
+
go-version: "1.26"
|
|
89
|
+
cache-dependency-path: go/go.sum
|
|
90
|
+
|
|
91
|
+
- name: Build wheel
|
|
92
|
+
uses: PyO3/maturin-action@v1
|
|
93
|
+
with:
|
|
94
|
+
target: ${{ matrix.target }}
|
|
95
|
+
args: --release --out dist
|
|
96
|
+
# The manylinux container carries no Go, and build.rs cannot build
|
|
97
|
+
# the bridge without it, so Go is installed into the container first.
|
|
98
|
+
manylinux: ${{ startsWith(matrix.os, 'ubuntu') && '2_28' || 'auto' }}
|
|
99
|
+
before-script-linux: |
|
|
100
|
+
case "$(uname -m)" in
|
|
101
|
+
x86_64) goarch=amd64 ;;
|
|
102
|
+
aarch64) goarch=arm64 ;;
|
|
103
|
+
*) echo "unsupported container arch $(uname -m)" >&2; exit 1 ;;
|
|
104
|
+
esac
|
|
105
|
+
curl -sSL "https://go.dev/dl/go1.26.0.linux-${goarch}.tar.gz" \
|
|
106
|
+
| tar -C /usr/local -xz
|
|
107
|
+
export PATH=$PATH:/usr/local/go/bin
|
|
108
|
+
go version
|
|
109
|
+
|
|
110
|
+
- uses: actions/upload-artifact@v4
|
|
111
|
+
with:
|
|
112
|
+
name: wheel-${{ matrix.os }}-${{ matrix.target }}
|
|
113
|
+
path: dist/
|
|
114
|
+
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# 3. The source distribution, so platforms outside the matrix can build.
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
sdist:
|
|
119
|
+
needs: version
|
|
120
|
+
runs-on: ubuntu-latest
|
|
121
|
+
steps:
|
|
122
|
+
- uses: actions/checkout@v6
|
|
123
|
+
with:
|
|
124
|
+
ref: ${{ needs.version.outputs.tag }}
|
|
125
|
+
- uses: PyO3/maturin-action@v1
|
|
126
|
+
with:
|
|
127
|
+
command: sdist
|
|
128
|
+
args: --out dist
|
|
129
|
+
- uses: actions/upload-artifact@v4
|
|
130
|
+
with:
|
|
131
|
+
name: sdist
|
|
132
|
+
path: dist/
|
|
133
|
+
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
# 4. Publishing and the GitHub Release.
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
publish:
|
|
138
|
+
needs: [version, wheels, sdist]
|
|
139
|
+
runs-on: ubuntu-latest
|
|
140
|
+
environment: release
|
|
141
|
+
permissions:
|
|
142
|
+
contents: write
|
|
143
|
+
env:
|
|
144
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
145
|
+
steps:
|
|
146
|
+
- uses: actions/checkout@v6
|
|
147
|
+
with:
|
|
148
|
+
ref: ${{ needs.version.outputs.tag }}
|
|
149
|
+
fetch-depth: 0
|
|
150
|
+
fetch-tags: true
|
|
151
|
+
|
|
152
|
+
- uses: arduino/setup-task@v2
|
|
153
|
+
with:
|
|
154
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
155
|
+
- uses: astral-sh/setup-uv@v7
|
|
156
|
+
- uses: kenji-miyake/setup-git-cliff@v2
|
|
157
|
+
|
|
158
|
+
- name: Collect artifacts
|
|
159
|
+
uses: actions/download-artifact@v4
|
|
160
|
+
with:
|
|
161
|
+
path: dist
|
|
162
|
+
merge-multiple: true
|
|
163
|
+
|
|
164
|
+
- name: Publish to PyPI
|
|
165
|
+
env:
|
|
166
|
+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
167
|
+
run: task release:publish
|
|
168
|
+
|
|
169
|
+
- name: Create GitHub Release
|
|
170
|
+
env:
|
|
171
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
172
|
+
run: task release:github TAG=${{ needs.version.outputs.tag }}
|
callix-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/target
|
|
2
|
+
|
|
3
|
+
# The typescript-go clone for the Go bridge: fetched by build.rs, ~370 MB
|
|
4
|
+
/.ts-go
|
|
5
|
+
|
|
6
|
+
# Byte-compiled / optimized / DLL files
|
|
7
|
+
__pycache__/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
*.py[cod]
|
|
10
|
+
|
|
11
|
+
# C extensions
|
|
12
|
+
*.so
|
|
13
|
+
|
|
14
|
+
# Distribution / packaging
|
|
15
|
+
.Python
|
|
16
|
+
.venv/
|
|
17
|
+
env/
|
|
18
|
+
/bin/
|
|
19
|
+
/build/
|
|
20
|
+
develop-eggs/
|
|
21
|
+
dist/
|
|
22
|
+
eggs/
|
|
23
|
+
/lib/
|
|
24
|
+
/lib64/
|
|
25
|
+
/parts/
|
|
26
|
+
sdist/
|
|
27
|
+
/var/
|
|
28
|
+
/include/
|
|
29
|
+
/man/
|
|
30
|
+
venv/
|
|
31
|
+
*.egg-info/
|
|
32
|
+
.installed.cfg
|
|
33
|
+
*.egg
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
pip-selfcheck.json
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.coverage
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
|
|
48
|
+
# Translations
|
|
49
|
+
*.mo
|
|
50
|
+
|
|
51
|
+
# Mr Developer
|
|
52
|
+
.mr.developer.cfg
|
|
53
|
+
.project
|
|
54
|
+
.pydevproject
|
|
55
|
+
|
|
56
|
+
# Rope
|
|
57
|
+
.ropeproject
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
*.pot
|
|
62
|
+
|
|
63
|
+
.DS_Store
|
|
64
|
+
|
|
65
|
+
# Sphinx documentation
|
|
66
|
+
docs/_build/
|
|
67
|
+
|
|
68
|
+
# PyCharm
|
|
69
|
+
.idea/
|
|
70
|
+
|
|
71
|
+
# VSCode
|
|
72
|
+
.vscode/
|
|
73
|
+
|
|
74
|
+
# Pyenv
|
|
75
|
+
.python-version
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-07-29
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- ci
|
|
9
|
+
- ci release
|
|
10
|
+
- **build**: unbreak the linux wheel and widen the platform matrix
|
|
11
|
+
|
|
12
|
+
### Documentation
|
|
13
|
+
|
|
14
|
+
- **bench**: refresh benchmark metrics [skip ci] by @github-actions[bot]
|
|
15
|
+
- add CLAUDE.md
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
- reinit
|
|
20
|
+
- update docs
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### New Contributors
|
|
24
|
+
|
|
25
|
+
- @ made their first contribution
|
|
26
|
+
- @github-actions[bot] made their first contribution
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
<!-- generated by git-cliff -->
|
callix-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Language
|
|
6
|
+
|
|
7
|
+
All code, comments, docs, commit messages and error strings in this repository
|
|
8
|
+
are **English**. Keep it that way when adding code.
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
Everything goes through [Task](https://taskfile.dev) — `taskfile.dist.yaml`.
|
|
13
|
+
Run cargo through the tasks rather than directly (see *Build gotchas* below).
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
task # build → regenerate .pyi → run example/test.py
|
|
17
|
+
task install # create .venv (Python 3.13) + maturin
|
|
18
|
+
task build # maturin develop, release profile
|
|
19
|
+
task lint # clippy with -D warnings (CI gate)
|
|
20
|
+
task check # cargo check, no module build
|
|
21
|
+
task test # cargo test + example smoke run
|
|
22
|
+
task stubs / task stubs:check # regenerate / verify committed .pyi
|
|
23
|
+
task example -- example/foo.py # run a different script
|
|
24
|
+
DEBUG=1 task ... # switch every cargo task to the debug profile
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
There is no test suite yet: `cargo test` compiles but has no tests, and
|
|
28
|
+
correctness is currently established by parity runs against graphlens (below).
|
|
29
|
+
|
|
30
|
+
Benchmarks (`benchmarks/Taskfile.yaml`, included as `bench:`):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
task bench:list [BENCH_LANG=rust]
|
|
34
|
+
task bench:one PROJECT=gin-gonic/gin
|
|
35
|
+
task bench:run [BENCH_LANG=go]
|
|
36
|
+
task bench:render VERSION=0.1.0 # splices the table into README.md
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
callix is a Rust rewrite of [graphlens](https://github.com/Neko1313/graphlens).
|
|
42
|
+
Everything of substance lives in the Rust extension module `callix._core`;
|
|
43
|
+
`python/callix/` is a facade of re-exports plus a thin `TyResolver` wrapper.
|
|
44
|
+
|
|
45
|
+
### The graph contract
|
|
46
|
+
|
|
47
|
+
`src/node.rs`, `src/relation.rs`, `src/graph.rs`, `src/ids.rs`, `src/span.rs`.
|
|
48
|
+
14 `NodeKind`s, 12 `RelationKind`s, 1-based spans, and IDs that must stay
|
|
49
|
+
deterministic: `sha256("{project}::{kind}::{qualified_name}")[:16]`. Boundary
|
|
50
|
+
IDs deliberately depend only on `mechanism` + normalized key, which is what
|
|
51
|
+
makes a server in one language and a client in another collapse into one node.
|
|
52
|
+
|
|
53
|
+
Two ordering constraints are load-bearing and easy to break:
|
|
54
|
+
|
|
55
|
+
- `Graph` stores nodes in an `IndexMap`, not a `HashMap` — insertion order is
|
|
56
|
+
part of the output and a `HashMap` silently reshuffles `CONTAINS` edges.
|
|
57
|
+
- `src/roots.rs::sort_paths` compares paths **component by component**, the
|
|
58
|
+
way Python's `sorted()` over `pathlib.Path` does. Sorting by the whole
|
|
59
|
+
string diverges on neighbours like `src/v4-mini/…` vs `src/v4/…`.
|
|
60
|
+
|
|
61
|
+
### Anatomy of a language adapter
|
|
62
|
+
|
|
63
|
+
Each of `src/python/`, `src/typescript/`, `src/golang/`, `src/rustlang/`
|
|
64
|
+
follows the same layout:
|
|
65
|
+
|
|
66
|
+
| File | Role |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `mod.rs` | thread-local tree-sitter parser, re-exports |
|
|
69
|
+
| `detector.rs` / `deps.rs` | project roots, manifest parsing, import classification |
|
|
70
|
+
| `visitor.rs` | CST walk → nodes, `DECLARES`/`CONTAINS`/`RESOLVES_TO` edges, occurrences |
|
|
71
|
+
| `boundary.rs` | cross-language ports via tree-sitter queries |
|
|
72
|
+
| `resolver.rs` | symbol resolution backend |
|
|
73
|
+
| `adapter.rs` | orchestration of `analyze()` |
|
|
74
|
+
|
|
75
|
+
**Visitors never create CALLS / REFERENCES / HAS_TYPE / INHERITS_FROM edges.**
|
|
76
|
+
They emit `OccurrenceRef`s (`src/occurrence.rs`) with a role — `call`, `read`,
|
|
77
|
+
`write`, `annotation`, `base` — which the resolution pass turns into edges.
|
|
78
|
+
|
|
79
|
+
`analyze()` always runs three phases, and the order is not cosmetic:
|
|
80
|
+
|
|
81
|
+
1. **Structure** for every sub-root, no resolution. The `SpanIndex` built next
|
|
82
|
+
has to span the whole workspace, or cross-root definitions have nothing to
|
|
83
|
+
bind to.
|
|
84
|
+
2. **One resolver for the whole call.** Per-root resolvers would both lose
|
|
85
|
+
cross-root references and re-index the workspace once per root.
|
|
86
|
+
3. **Boundaries**, so BOUNDARY nodes land in the graph after resolver edges.
|
|
87
|
+
|
|
88
|
+
Resolution maps a definition position back to a node through
|
|
89
|
+
`SpanIndex::lookup_name` (name spans, not full extents). Anything that misses
|
|
90
|
+
falls through to an `EXTERNAL_SYMBOL` so the edge is never lost.
|
|
91
|
+
|
|
92
|
+
### Node schemes differ per language
|
|
93
|
+
|
|
94
|
+
Do not assume Python's shape elsewhere. `MODULE` is a dotted name in Python and
|
|
95
|
+
TypeScript, a **package directory** in Go (its `qualified_name` equals the
|
|
96
|
+
import path, so internal imports bind by direct lookup), and a Rust module path
|
|
97
|
+
in `rustlang`. Rust dispatches `impl` blocks in a second pass because a type may
|
|
98
|
+
be declared below its `impl`.
|
|
99
|
+
|
|
100
|
+
### Resolvers: what is linked in and what is not
|
|
101
|
+
|
|
102
|
+
| Language | Backend | External requirement |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| Python | `ty` linked as a library (`src/python/ty_embedded.rs`) | none |
|
|
105
|
+
| TypeScript | typescript-go via a Go c-archive (`go/bridge.go`) | none |
|
|
106
|
+
| Go | `go/packages` + `go/types` via the same c-archive (`go/bridge_go.go`) | Go toolchain |
|
|
107
|
+
| Rust | `rust-analyzer scip` subprocess + own SCIP decoder (`src/rustlang/scip.rs`) | rust-analyzer + Cargo |
|
|
108
|
+
|
|
109
|
+
Go and Rust cannot be compiled in: both need their standard library's sources
|
|
110
|
+
and a real workspace load. `build.rs` clones typescript-go into `.ts-go/`
|
|
111
|
+
(~370 MB, survives `cargo clean`) and builds `go/` into a c-archive. That
|
|
112
|
+
c-archive is why **Windows is unsupported** — cgo emits a mingw-w64 GNU
|
|
113
|
+
archive that will not link into the MSVC target; `build.rs` refuses up front.
|
|
114
|
+
It also derives GOOS/GOARCH from the Rust target so an Apple Intel wheel can
|
|
115
|
+
be cross-built on an Apple Silicon runner, and passes `-buildvcs=false`
|
|
116
|
+
because `go build` otherwise fails where git will not read the checkout (a
|
|
117
|
+
manylinux container mounts the workspace under another uid).
|
|
118
|
+
|
|
119
|
+
Resolvers are swappable from Python — any object with `prepare`/`resolve_all`/
|
|
120
|
+
`status` — as are dependency parsers (`can_parse`/`parse`). Rust calls them
|
|
121
|
+
through the ordinary Python protocol.
|
|
122
|
+
|
|
123
|
+
## Parity with graphlens
|
|
124
|
+
|
|
125
|
+
This is the project's correctness harness. graphlens lives at
|
|
126
|
+
`~/project/graphlens` with its own venv at `~/project/graphlens/.venv/bin/python`
|
|
127
|
+
(callix's venv does not have graphlens's tree-sitter grammars installed).
|
|
128
|
+
|
|
129
|
+
Method: build the same graph both ways → `to_dict()` →
|
|
130
|
+
`json.dumps(sort_keys=True)` → `diff`. Every subtle bug found so far (key
|
|
131
|
+
ordering, HashMap vs IndexMap, path sorting) surfaced only this way. When
|
|
132
|
+
comparing structure only, use `Adapter(resolve=False)` on the callix side and a
|
|
133
|
+
stub resolver on graphlens's; the metrics block will differ because callix skips
|
|
134
|
+
the pass entirely, and that is expected.
|
|
135
|
+
|
|
136
|
+
Re-snapshot graphlens baselines before trusting a diff — a stale dump once
|
|
137
|
+
produced a fake 448k-line regression.
|
|
138
|
+
|
|
139
|
+
**One intentional divergence, applied to all four languages.** When the resolver
|
|
140
|
+
finds a definition it cannot name, the synthetic `EXTERNAL_SYMBOL` key includes
|
|
141
|
+
the project-relative file path (`{role}@{path}:{line}:{col}`). graphlens omits
|
|
142
|
+
the path, which merges distinct sites that share coordinates — 98% of external
|
|
143
|
+
nodes on apache/superset. Expect diffs confined to those nodes' ids/names.
|
|
144
|
+
|
|
145
|
+
## Build gotchas
|
|
146
|
+
|
|
147
|
+
- **`PYO3_PYTHON`** is set globally in the Taskfile to `.venv/bin/python`.
|
|
148
|
+
`abi3-py313` refuses anything older than 3.13, and bare `cargo` otherwise
|
|
149
|
+
picks the first `python3` on PATH.
|
|
150
|
+
- **All cargo tasks share one profile**, release by default. The ty/ruff tree is
|
|
151
|
+
~200 crates; linting in debug while building in release compiles it twice.
|
|
152
|
+
- **`get-size2` is pinned to `=0.10.0`** with a hand-written feature list.
|
|
153
|
+
Outside ruff's workspace the features are not inherited, and 0.10.3 moved to
|
|
154
|
+
`compact_str` 0.10 while ruff is on 0.9 — without the pin `ruff_python_ast`
|
|
155
|
+
does not compile at all.
|
|
156
|
+
- **ty comes from a git `rev`** pinned across several `Cargo.toml` entries;
|
|
157
|
+
updating means changing all of them together and re-verifying.
|
|
158
|
+
- Large SCIP indexes and Go builds respect `TMPDIR`; a small `/tmp` will fill.
|
|
159
|
+
- `cargo fmt` is deliberately not a task — formatting is hand-tuned in places.
|
|
160
|
+
|
|
161
|
+
## Stubs and release
|
|
162
|
+
|
|
163
|
+
`python/callix/_core/__init__.pyi` is generated by `src/bin/stub_gen.rs` from
|
|
164
|
+
the Rust doc comments and **is committed**; CI fails on drift. Module constants
|
|
165
|
+
and an exception-base-class fixup are appended manually there because
|
|
166
|
+
pyo3-stub-gen 0.23 does not emit them correctly.
|
|
167
|
+
|
|
168
|
+
Versions live **only** in `Cargo.toml` (`pyproject.toml` declares it `dynamic`).
|
|
169
|
+
`cliff.toml` drives both conventional-commit parsing and the changelog template;
|
|
170
|
+
its `[remote.github]` owner/repo must match the real repository or git-cliff
|
|
171
|
+
panics on a 404. Wheels are built by a per-platform matrix in `release.yml` —
|
|
172
|
+
one wheel cannot cover every platform with ty and the Go bridge linked inside.
|