Skip to content

feat(release): publish a linux-arm64 engine - #20

Merged
anvanster merged 7 commits into
mainfrom
feat/linux-arm64-engine
Aug 9, 2026
Merged

feat(release): publish a linux-arm64 engine#20
anvanster merged 7 commits into
mainfrom
feat/linux-arm64-engine

Conversation

@anvanster

Copy link
Copy Markdown
Member

Intent

The developer's overall goal in this session was to ship CodeGraph 0.20.1, and in the later part specifically to add a linux-arm64 engine binary as a supported release platform. They were initially reluctant to build aarch64 because of disk space on the build VM, then decided to proceed once ARM server offerings were shown to be mainstream and the space cost was shown to be small. They cared that the ARM binary match the existing x86_64 compatibility floor rather than shipping an asymmetric support story, motivated by conservative corporate Linux users, and they asked whether telemetry could show how many ARM systems actually run CodeGraph before committing. After merging the two open PRs, they asked to prepare the ARM build for inclusion in the release, to correct the support-matrix documentation, and to build and confirm all remaining 0.20.1 binaries start on their own platforms. Their final instruction was to merge the linux-arm64 branch, do a clean rebuild of all five binaries from a single merge commit so provenance is consistent (the ARM one had been stamped -dirty and the assets disagreed on source commit), and update the docs.

What Changed

  • Adds linux-arm64 as a published engine platform: scripts/build-linux-arm64.sh builds it in an Ubuntu 20.04 container and asserts the same floors as the x64 build (glibc 2.30, GLIBCXX_3.4.29, the writable __libc_single_threaded shim, and that the binary runs), while the clients now resolve arm64 Linux by exact platform-arch match - fetch-engine.js gains an exported PUBLISHED_BINARIES list and CodeGraphServerResolver maps arm64 Linux to the new asset instead of returning null.
  • Tightens release provenance so the five assets cannot disagree on their source: stamp-binary.sh now records the commit beside the version, measured from the binary's own --info wherever the staging host can run it and rejected when it is unknown or -dirty; publish-release-assets.sh refuses a set with a missing or disagreeing commit; and both it and package-npm.sh read the asset list from fetch-engine.js rather than keeping their own copies. build-linux-arm64.sh gates its own container build against the host HEAD before staging.
  • Documents the platform support matrix in the README (both Linux architectures, their shared glibc/libstdc++ floors, and the GLIBCXX_3.4.29 failure mode), corrects stale per-platform and download-size claims across the client docs, and restores vscode/.eslintrc.json so npm run lint runs again - clearing the resulting errors in the touched TypeScript.

Risk Assessment

✅ Low: The round-2 items were implemented as instructed and the new manifest format, self-report fallback, and prefix-based agreement logic all hold up under the five-asset release path I traced; what remains is one cheap gate tightening and one stale comment.

Testing

Exercised the change as an arm64 Linux end user and as the release manager: a simulated linux/arm64 install now fetches, checksum-verifies and installs an AArch64 codegraph-server-linux-arm64 where the base commit gave that machine no engine at all, and the real stamp-binary.sh / publish-release-assets.sh pair was run over a five-asset staging directory holding this host's genuine darwin-arm64 engine - it measures commits from --info, refuses -dirty and contradicting stamps, blocks a set whose assets disagree on their source commit, and passes with all five binaries traced to one commit. Both client test suites pass (JS suite green; 24 JetBrains tests, 0 failures), and I added one focused install test per channel because both previously covered only the name mapping and not the arm64 download itself. The README's new support matrix is included as a rendered screenshot. Not covered: the arm64 binary was not built or run here, so its glibc/GLIBCXX floors and the #15 startup guard remain unverified locally - reported as a warning.

Evidence: arm64 Linux install, before vs after (CLI transcript)

--- BEFORE (base commit 71f0f0f) --- simulated machine: linux / arm64 (uname -m: aarch64) ensureEngine threw: no CodeGraph engine is published RESULT: no engine on this machine. --- AFTER (feat/linux-arm64-engine @ ef37289) --- simulated machine: linux / arm64 (uname -m: aarch64) downloading codegraph-server-linux-arm64 ... installed engine: codegraph-server-linux-arm64 ELF machine: AArch64 (arm64) mode: 755 version marker: 0.20.1 directory: .engine-version, codegraph-server-linux-arm64 RESULT: the arm64 Linux machine has a working, verified arm64 engine.

--- BEFORE (base commit 71f0f0f) ---
simulated machine:  linux / arm64 (uname -m: aarch64)
engine version:     0.20.1
install dir:        /var/folders/dk/62tyv1993n5dy5jqms827nlc0000gn/T/cg-arm64-iqEIsj

  ensureEngine threw: no CodeGraph engine is published for darwin-arm64

RESULT: no engine on this machine.

--- AFTER (feat/linux-arm64-engine @ ef37289) ---
simulated machine:  linux / arm64 (uname -m: aarch64)
engine version:     0.20.1
install dir:        /var/folders/dk/62tyv1993n5dy5jqms827nlc0000gn/T/cg-arm64-8HKGlX

  downloading codegraph-server-linux-arm64 ...

  installed engine:  codegraph-server-linux-arm64
  ELF machine:       AArch64 (arm64)
  mode:              755
  version marker:    0.20.1
  directory:         .engine-version, codegraph-server-linux-arm64

RESULT: the arm64 Linux machine has a working, verified arm64 engine.

NOTE: the BEFORE message names "darwin-arm64" because that unsupported-platform
error interpolates the host's own platform rather than the one asked about
(pre-existing wording, unchanged by this branch). The call being made is
linux/arm64 in both runs.
Evidence: Release gate: provenance stamping and one-commit agreement (CLI transcript)

ERROR: the staged binaries were not all built from the same commit. codegraph-server-darwin-arm64 10a6e76 codegraph-server-darwin-x64 10a6e76 codegraph-server-linux-arm64 deadbeef1234 codegraph-server-linux-x64 10a6e76 codegraph-server-win32-x64.exe 10a6e76 --- after re-stamping linux-arm64 from the release commit --- ✓ codegraph-server-darwin-arm64 116M (0.20.1 @ 10a6e76) ✓ codegraph-server-linux-arm64 4.0K (0.20.1 @ 10a6e76fbb00) ... all 5 engine binaries built from 10a6e76



================ staging directory ================

$ ls -1 /var/folders/dk/62tyv1993n5dy5jqms827nlc0000gn/T//cg-release-stage
codegraph-server-darwin-arm64
codegraph-server-darwin-x64
codegraph-server-linux-arm64
codegraph-server-linux-x64
codegraph-server-win32-x64.exe
onnxruntime.dll
[exit 0]

the darwin-arm64 asset is the real engine and can be run here:
$ /var/folders/dk/62tyv1993n5dy5jqms827nlc0000gn/T//cg-release-stage/codegraph-server-darwin-arm64 --info
codegraph-server v0.20.1 (10a6e76)
Author: Andrey Vasilevsky <anvanster@gmail.com>
License: Apache-2.0
Repository: https://github.com/codegraph-ai/CodeGraph
Built: 2026-08-09T19:36:39Z with rustc 1.93.1 (01f6ddf75 2026-02-11)
[exit 0]


================ 1. a runnable asset is measured, not asked ================

$ /Users/anvanster/.no-mistakes/worktrees/b8216ba13cd2/01KZM184HCMTW5K3JF0HM2SQRT/scripts/stamp-binary.sh codegraph-server-darwin-arm64 0.20.1
Stamped codegraph-server-darwin-arm64 as 0.20.1 (10a6e76)
0.20.1  10a6e76  codegraph-server-darwin-arm64
[exit 0]


================ 2. the binary overrules a commit that contradicts it ================

$ /Users/anvanster/.no-mistakes/worktrees/b8216ba13cd2/01KZM184HCMTW5K3JF0HM2SQRT/scripts/stamp-binary.sh codegraph-server-darwin-arm64 0.20.1 abc1234def56
ERROR: codegraph-server-darwin-arm64 reports commit 10a6e76, but abc1234def56 was given.
The binary is the only witness of what actually built it, so the
argument cannot override it. Stage the binary you meant to stage.
[exit 1]


================ 3. a cross-built asset must state its commit ================

$ /Users/anvanster/.no-mistakes/worktrees/b8216ba13cd2/01KZM184HCMTW5K3JF0HM2SQRT/scripts/stamp-binary.sh codegraph-server-linux-arm64 0.20.1
ERROR: no commit recorded for codegraph-server-linux-arm64.
This host cannot run it, so the commit it was built from has to be
stated. From the checkout it was built on:

  stamp-binary.sh codegraph-server-linux-arm64 0.20.1 $(git rev-parse --short=12 HEAD)
[exit 1]


================ 4. a -dirty build is refused (the defect this release hit) ================

$ /Users/anvanster/.no-mistakes/worktrees/b8216ba13cd2/01KZM184HCMTW5K3JF0HM2SQRT/scripts/stamp-binary.sh codegraph-server-linux-arm64 0.20.1 10a6e76fbb00-dirty
ERROR: codegraph-server-linux-arm64 reports commit '10a6e76fbb00-dirty' - it was built from a modified
tree and cannot be traced back to a released commit.
[exit 1]


================ 5. release gate: assets disagreeing on their source commit ================

BUILD-MANIFEST:
  0.20.1  10a6e76  codegraph-server-darwin-arm64
  0.20.1  10a6e76  codegraph-server-darwin-x64
  0.20.1  deadbeef1234  codegraph-server-linux-arm64
  0.20.1  10a6e76  codegraph-server-linux-x64
  0.20.1  10a6e76  codegraph-server-win32-x64.exe
$ /Users/anvanster/.no-mistakes/worktrees/b8216ba13cd2/01KZM184HCMTW5K3JF0HM2SQRT/scripts/publish-release-assets.sh
CodeGraph release assets
  version: 0.20.1
  tag:     v0.20.1
  repo:    codegraph-ai/CodeGraph

  ✓ npm + VS Code  pins engine 0.20.1
  ✓ JetBrains      pins engine 0.20.1

  ✓ codegraph-server-darwin-arm64        116M  (0.20.1 @ 10a6e76)
  ✓ codegraph-server-darwin-x64          4.0K  (0.20.1 @ 10a6e76)
  ✓ codegraph-server-linux-arm64         4.0K  (0.20.1 @ deadbeef1234)
  ✓ codegraph-server-linux-x64           4.0K  (0.20.1 @ 10a6e76)
  ✓ codegraph-server-win32-x64.exe       4.0K  (0.20.1 @ 10a6e76)
  ✓ onnxruntime.dll                      4.0K

ERROR: the staged binaries were not all built from the same commit.

  codegraph-server-darwin-arm64        10a6e76
  codegraph-server-darwin-x64          10a6e76
  codegraph-server-linux-arm64         deadbeef1234
  codegraph-server-linux-x64           10a6e76
  codegraph-server-win32-x64.exe       10a6e76

Everything in one release has to come from one tree, or the tag names a build
that never existed as a whole. Rebuild the assets that disagree from the commit
being released - see cross-platform-builds.md for the per-platform hosts - and
re-stamp them with ./scripts/stamp-binary.sh.
[exit 1]


================ 6. release gate: the linux-arm64 asset rebuilt from the release commit ================

BUILD-MANIFEST:
  0.20.1  10a6e76  codegraph-server-darwin-arm64
  0.20.1  10a6e76  codegraph-server-darwin-x64
  0.20.1  10a6e76fbb00  codegraph-server-linux-arm64
  0.20.1  10a6e76  codegraph-server-linux-x64
  0.20.1  10a6e76  codegraph-server-win32-x64.exe
$ /Users/anvanster/.no-mistakes/worktrees/b8216ba13cd2/01KZM184HCMTW5K3JF0HM2SQRT/scripts/publish-release-assets.sh
CodeGraph release assets
  version: 0.20.1
  tag:     v0.20.1
  repo:    codegraph-ai/CodeGraph

  ✓ npm + VS Code  pins engine 0.20.1
  ✓ JetBrains      pins engine 0.20.1

  ✓ codegraph-server-darwin-arm64        116M  (0.20.1 @ 10a6e76)
  ✓ codegraph-server-darwin-x64          4.0K  (0.20.1 @ 10a6e76)
  ✓ codegraph-server-linux-arm64         4.0K  (0.20.1 @ 10a6e76fbb00)
  ✓ codegraph-server-linux-x64           4.0K  (0.20.1 @ 10a6e76)
  ✓ codegraph-server-win32-x64.exe       4.0K  (0.20.1 @ 10a6e76)
  ✓ onnxruntime.dll                      4.0K

  all 5 engine binaries built from 10a6e76

Staging with checksums in target/release-assets ...
  a87e2e687d56b553  codegraph-server-darwin-arm64
  c953d74203d7a428  codegraph-server-darwin-x64
  c953d74203d7a428  codegraph-server-linux-arm64
  c953d74203d7a428  codegraph-server-linux-x64
  c953d74203d7a428  codegraph-server-win32-x64.exe
  c953d74203d7a428  onnxruntime.dll

Staged only. Re-run with --publish to upload to codegraph-ai/CodeGraph.
[exit 0]
- Evidence: README "Supported platforms" section as a reader sees it (local file: /var/folders/dk/62tyv1993n5dy5jqms827nlc0000gn/T/no-mistakes-evidence/01KZM184HCMTW5K3JF0HM2SQRT/readme-supported-platforms.png)
Evidence: Rendered README section (HTML source of the screenshot)
<!doctype html><meta charset="utf-8"><title>CodeGraph README - Supported platforms</title>
<style>
body{font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
color:#1f2328;background:#fff;margin:0;padding:32px 40px;max-width:900px}
h2{font-size:24px;font-weight:600;padding-bottom:.3em;border-bottom:1px solid #d1d9e0;margin-top:0}
table{border-collapse:collapse;margin:16px 0;display:block;width:max-content;max-width:100%;overflow:auto}
th,td{border:1px solid #d1d9e0;padding:6px 13px;text-align:left}
tr:nth-child(2n){background:#f6f8fa}
code{background:#eff1f3;border-radius:6px;padding:.2em .4em;font-size:85%;
font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
pre{background:#f6f8fa;border-radius:6px;padding:16px;overflow:auto}
pre code{background:none;padding:0;font-size:85%}
p{margin:0 0 16px}
</style>
<h2>Supported platforms</h2>
<p>The engine is a native binary, downloaded for your platform on first run.</p>
<table><thead><tr><th>Platform</th><th>Architectures</th></tr></thead><tbody><tr><td>macOS</td><td>Apple Silicon (arm64) and Intel (x64)</td></tr><tr><td>Linux</td><td>x64 and arm64</td></tr><tr><td>Windows</td><td>x64 (Windows on ARM runs the x64 build under emulation)</td></tr></tbody></table>
<p><strong>Linux requires glibc 2.30 or newer <em>and</em> a libstdc++ from GCC 11 or newer (<code>GLIBCXX_3.4.29</code>).</strong> The second requirement is the binding one, and it is not implied by the first — the engine embeds ONNX Runtime, which is built with GCC 11.</p>
<table><thead><tr><th>Runs</th><th>Does not run</th></tr></thead><tbody><tr><td>SLES 15 SP4</td><td>Ubuntu 20.04</td></tr><tr><td>Ubuntu 22.04 and newer</td><td>Debian 11</td></tr><tr><td>Debian 12 and newer</td><td>RHEL / CentOS 8</td></tr><tr><td>RHEL 9 and newer</td><td>Amazon Linux 2</td></tr><tr><td>Amazon Linux 2023</td><td></td></tr></tbody></table>
<p>Both Linux architectures have identical requirements. If the engine exits immediately with a message like</p>
<pre><code>version `GLIBCXX_3.4.29&#x27; not found (required by codegraph-server)</code></pre>
<p>the distribution&#x27;s C++ runtime is older than the engine needs; installing a newer <code>libstdc++</code> (for example RHEL 8&#x27;s <code>gcc-toolset-11</code>) resolves it without upgrading the distribution.</p>
Evidence: Targeted test runs, both client channels

$ node mcp-package/test/fetch-engine.test.js # npm + VS Code channel PASS an arm64 linux install fetches the arm64 engine PASS and it is the arm64 build PASS linux-arm64 resolves to its own engine, not the x64 one PASS linux-arm64 resolves to a published asset (codegraph-server-linux-arm64) 0 failure(s) $ ./gradlew test --tests "*CodeGraphServerResolverTest" --tests "*EngineDownloaderTest" testcase name="test an arm64 linux ide installs the arm64 engine" testcase name="test arm64 linux gets its own engine and arm64 windows emulates x64" tests run: 24, failures: 0

$ node mcp-package/test/fetch-engine.test.js   # npm + VS Code channel
PASS codegraph-server-darwin-arm64 is installed
PASS an arm64 linux install fetches the arm64 engine
PASS and it is the arm64 build
PASS linux-arm64 resolves to its own engine, not the x64 one
PASS win32-arm64 uses the x64 engine, which Windows emulates
PASS darwin-arm64 does
PASS linux-arm64 needs the engine and no sidecar
PASS darwin-arm64 resolves to a published asset (codegraph-server-darwin-arm64)
PASS linux-arm64 resolves to a published asset (codegraph-server-linux-arm64)
0 failure(s)

$ ./gradlew test --tests "*CodeGraphServerResolverTest" --tests "*EngineDownloaderTest"   # JetBrains channel
testcase name="test an arm64 linux ide installs the arm64 engine"
testcase name="test arm64 linux gets its own engine and arm64 windows emulates x64"
tests run: 24, failures: 0
Evidence: arm64 build script refuses a dirty tree before building

$ ./scripts/build-linux-arm64.sh ERROR: the working tree is dirty, so this build would be stamped '-dirty' and would disagree with the assets built for the same release. Commit or stash first, or set CODEGRAPH_ALLOW_DIRTY=1 for a throwaway build. [exit 1]

$ git status --porcelain   # the tree carries the two test files added by this phase
 M jetbrains/src/test/kotlin/ai/codegraph/jetbrains/server/EngineDownloaderTest.kt
 M mcp-package/test/fetch-engine.test.js

$ ./scripts/build-linux-arm64.sh
ERROR: the working tree is dirty, so this build would be stamped '-dirty'
and would disagree with the assets built for the same release. Commit or
stash first, or set CODEGRAPH_ALLOW_DIRTY=1 for a throwaway build.
[exit 1]
- Outcome: ⚠️ 1 warning across 1 run (11m2s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 infos
  • ⚠️ scripts/build-linux-arm64.sh:135 - The arm64 build has no provenance gate, so the exact failure that motivated the rebuild (an asset stamped -dirty / disagreeing on source commit) is still reachable. crates/codegraph-server/build.rs:20-31 shells out to git rev-parse HEAD and git status --porcelain from inside the container, and both failure modes are silent: run_cmd swallows a nonzero exit and substitutes the literal string &#34;unknown&#34;. Two concrete triggers: (1) when REPO_ROOT is a git worktree, .git is a pointer file naming a gitdir path that is not mounted into the container, so every git call fails and the binary ships GIT_HASH=&#34;unknown&#34;; (2) any uncommitted change in the live bind-mounted tree stamps -dirty. The script verifies the glibc/GLIBCXX floors, the shim section class, and that the binary runs, but never checks what commit it claims. stamp-binary.sh records only the version, so publish-release-assets.sh will happily publish it. Recommend asserting provenance at the same boundary as the other floors: pass the host's git rev-parse --short HEAD in via -e, and after the build compare it against &#34;$BIN&#34; --info (metadata.rs prints GIT_HASH_SHORT), failing on unknown, on a mismatch, or on a -dirty suffix.
  • ⚠️ jetbrains/src/main/kotlin/ai/codegraph/jetbrains/server/CodeGraphServerResolver.kt:86 - The KDoc directly above the changed platformBinaryNameOrNull still documents the pre-change behavior: "Only macOS is built for both architectures... Linux has no such layer, and falling back to x64 there installs ~30 MB that cannot execute". The body four lines below now returns codegraph-server-linux-arm64, so the doc contradicts the code it describes and would mislead the next person deciding whether Linux arm64 is published. Same stale claim at jetbrains/src/test/kotlin/ai/codegraph/jetbrains/server/EngineDownloaderTest.kt:67 ("Windows and Linux are published for x64 only"). Also note the fetch-engine.js comment at line 118 calls PUBLISHED_BINARIES "the single list" with "three hand-kept copies", which undercounts: this Kotlin mapping is a fourth copy that cannot import it, and is what actually had to be edited in lockstep here.
  • ℹ️ scripts/build-linux-arm64.sh:113 - The issue-codegraph-server SIGSEGVs at startup on Linux/aarch64 (before main, even --version) #15 shim guard fails open when the symbol is absent: case &#34;$SHIM_CLASS&#34; in B|D|b|d|&#34;&#34;) accepts the empty string, and nm ... 2&gt;/dev/null yields empty on any tooling failure (nm missing, a future stripped profile, an unexpected output format), so the aarch64 SIGSEGV regression check silently passes. Separately, awk's $2 is only the section class for defined symbols - for an undefined U __libc_single_threaded nm omits the address column, so $2 is the symbol name and the guard reports "shim is '__libc_single_threaded' (read-only)". It still fails closed in that case, but the message is nonsense. Recommend treating absent as an error and matching the class positionally (e.g. nm output's second-to-last field, or nm -C --defined-only).
  • ℹ️ scripts/build-linux-arm64.sh:20 - The header comment says the binary "does not run on Ubuntu 20.04 ... not even on its own build host", but line 135 runs &#34;$BIN&#34; --version inside the ubuntu:20.04 container and aborts the build if it fails. Both can only be true because ppa:ubuntu-toolchain-r/test upgrades libstdc++6 past GLIBCXX_3.4.29 in the container; a reader debugging a failure at line 135 would reasonably conclude the check itself is wrong. Recommend qualifying the comment as "stock Ubuntu 20.04, before the toolchain PPA upgrades libstdc++6".
  • ℹ️ scripts/build-linux-arm64.sh:70 - The container runs as root against two bind mounts inside the repo ($REPO_ROOT:/src and $BUILD_DIR:/target, which defaults to $REPO_ROOT/target/linux-arm64). On the natively-supported arm64 Linux host path this leaves root-owned build artifacts in the developer's tree (and a root-owned Cargo.lock if the lock needs updating), which later host-side cargo/rm -rf target invocations cannot clean without sudo. Docker Desktop on macOS hides this via uid mapping. Recommend a chown -R &#34;$HOST_UID:$HOST_GID&#34; /target at the end of the container recipe, with the ids passed in via -e (running the whole container as the host user is not an option, since apt-get needs root).
  • ℹ️ mcp-package/test/fetch-engine.test.js:436 - The added assertions duplicate ones already present a few lines above: platformBinaryName(&#34;linux&#34;, &#34;arm64&#34;) is asserted at 418, 436 and again inside the loop at 465, and platformBinaryName(&#34;win32&#34;, &#34;arm64&#34;) at 423 and 445. Dropping the redundant checks at 435-438 and 444-448 leaves the block asserting each rule once, with the new riscv64 case and the PUBLISHED_BINARIES cross-check carrying the genuinely new coverage.

🔧 Fix: gate arm64 build provenance, harden shim check, fix stale docs
3 issues (1 warning, 2 infos) still open:

  • ⚠️ scripts/build-linux-arm64.sh:253 - The new provenance gate stops at the arm64 asset, so "the release assets disagree on their source commit" - the failure the gate names in its own error text at line 227 - is still reachable for the other four. The gate proves the arm64 binary matches the host HEAD, then discards that fact: stamp-binary.sh takes only (binary, version) and writes &lt;version&gt; &lt;binary&gt; (stamp-binary.sh:44), and publish-release-assets.sh accepts an asset on grep -qxF &#34;$VERSION $bin&#34; alone (publish-release-assets.sh:146). Concrete path: build darwin-x64 natively at commit A and run this script at commit B, both from clean trees; both stamp as 0.20.1, every gate passes, and the release ships assets from two commits. The four native builds have no equivalent check at all. The earliest shared boundary that would make the invariant hold for all five is the manifest itself - have stamp-binary.sh take the commit (or read it from &lt;binary&gt; --info when the binary is native to the staging host) and record &lt;version&gt; &lt;commit&gt; &lt;binary&gt;, and have publish-release-assets.sh refuse when the recorded commits disagree - rather than repeating this script's check in each per-platform builder. Note this would change the BUILD-MANIFEST format the other four builds already write, so it is a decision rather than a mechanical fix.
  • ℹ️ scripts/build-linux-arm64.sh:244 - CODEGRAPH_ALLOW_DIRTY=1 disables the provenance gate but still stages and stamps. Lines 74-75 set CHECK_PROVENANCE=0, line 98 prints "do not publish this build", and then lines 244-253 copy the binary into vscode/bin/ and call stamp-binary.sh with $VERSION anyway - producing a BUILD-MANIFEST entry indistinguishable from a gated one, which is exactly the "stale binary looks as valid as a fresh one" case the manifest exists to prevent (stamp-binary.sh:7-15). The script should enforce the rule it states: when CHECK_PROVENANCE is 0, take the --no-stage path regardless of argv. The env var is also absent from the Usage block at lines 43-45, which documents only the two argv forms.
  • ℹ️ scripts/build-linux-arm64.sh:225 - The provenance comparison is an exact string match between two independently abbreviated short hashes: EXPECTED_GIT_SHORT comes from the host's git rev-parse --short HEAD (line 77) while GOT_GIT comes from build.rs's git rev-parse --short HEAD (build.rs:21) run by the container's git 2.25. Both default to auto-abbreviation over the same object store, so they normally agree, but a core.abbrev set in the host user's gitconfig (the container has none) makes the two differ in length for the same commit and the build fails with "the binary claims commit X, but the host is at Y" - where X is a prefix of Y. Comparing by prefix instead (case &#34;$EXPECTED_GIT_SHORT&#34; in &#34;$GOT_GIT&#34;*), guarded by a minimum length) removes the dependency on both sides picking the same abbreviation.

🔧 Fix: record source commit per asset and gate release on agreement
2 infos still open:

  • ℹ️ scripts/stamp-binary.sh:61 - The script now runs the binary and parses the exact line that carries both facts - name vX.Y.Z (commit) from metadata.rs:26 - but extracts only the commit, leaving the version an unverified claim. That leaves one realistic slip uncaught: bump Cargo.toml to 0.20.2, forget to rebuild, then re-stamp the staged binaries as 0.20.2 with no commit argument. Each self-reports the old commit, so the mismatch check at line 64 never fires (nothing was given to contradict), all five agree with each other, and publish-release-assets.sh's agreement gate passes - shipping v0.20.2 assets that are all v0.20.1. Extending the same sed to capture the version and asserting it equals $VERSION when SELF_REPORTED is non-empty closes it for free on every host that can run its own asset. Note the header at lines 9-11 dismisses version scraping, but that referred to strings-style scraping of the image; running --info is the same measurement this commit already trusts for the commit.
  • ℹ️ scripts/build-linux-arm64.sh:74 - This comment is stale as of this commit: "stamp-binary.sh records only the version, so nothing downstream would notice". stamp-binary.sh now records the commit beside the version (stamp-binary.sh:111) and publish-release-assets.sh refuses a set whose commits disagree (publish-release-assets.sh:243), which is precisely what would notice. The block reads as the justification for this script's local gate, so leaving it in place understates what the release path now enforces.
⚠️ **Test** - 1 warning
  • ⚠️ scripts/build-linux-arm64.sh:61 - The linux-arm64 engine binary itself was never built or executed during this phase, so the support claims that motivate the change are unverified locally: the glibc 2.30 / GLIBCXX_3.4.29 floor assertions, the issue codegraph-server SIGSEGVs at startup on Linux/aarch64 (before main, even --version) #15 __libc_single_threaded writable-section guard, and the README's "both Linux architectures have identical requirements" statement all live inside scripts/build-linux-arm64.sh and only run during a full container build. Two things block it here: a full cargo build --release -p codegraph-server under Ubuntu 20.04 is far beyond a targeted test step, and this gate runs from a git worktree whose .git is a pointer file, so build.rs cannot read git inside the container and the script's own provenance check would fail (the script's comments anticipate exactly this). I did confirm the script's pre-Docker provenance guard refuses a dirty tree, which is the guard that prevents the -dirty stamp from recurring. The author needs to decide whether the arm64 build evidence from their own build host is sufficient, or whether it should be reproduced in CI before release.
  • node mcp-package/test/fetch-engine.test.js - full JS channel suite including the new an arm64 linux install fetches the arm64 engine case I added
  • jetbrains/gradlew test --tests &#34;*CodeGraphServerResolverTest&#34; --tests &#34;*EngineDownloaderTest&#34; - 24 tests, 0 failures, including the new test an arm64 linux ide installs the arm64 engine case I added (run with JAVA_HOME=/opt/homebrew/opt/openjdk@21; no JDK is on PATH by default)
  • Manual before/after: drove ensureEngine with platform=linux, arch=arm64 against a local release server, once with the base commit's fetch-engine.js (throws, no engine) and once with the branch's (installs a verified AArch64 ELF, mode 755, .engine-version = 0.20.1)
  • Manual: scripts/stamp-binary.sh codegraph-server-darwin-arm64 0.20.1 against the real engine built on this host - commit measured from --info rather than asserted
  • Manual: scripts/stamp-binary.sh codegraph-server-darwin-arm64 0.20.1 abc1234def56 - refused, binary overrules the argument
  • Manual: scripts/stamp-binary.sh codegraph-server-linux-arm64 0.20.1 and ... 0.20.1 10a6e76fbb00-dirty - refused for a missing commit and for a -dirty build
  • Manual: scripts/publish-release-assets.sh over a five-asset staging dir (CODEGRAPH_BIN_DIR) - refused when linux-arm64 was stamped deadbeef1234, passed with checksums once re-stamped to the release commit
  • Manual: scripts/build-linux-arm64.sh - dirty-tree provenance guard fires before Docker is touched
  • Manual: rendered README's new ## Supported platforms section from source and screenshotted it headless (chrome-devtools-axi)
  • Probed https://github.com/codegraph-ai/CodeGraph/releases/download/v0.20.1/* - all six assets 404 today, which is why the client flows were exercised against a local stand-in release
🔧 **Document** - 2 issues found → auto-fixed ✅
  • ⚠️ scripts/publish-release-assets.sh:16 - The release scripts point readers at cross-platform-builds.md in three places (lines 16, 199, and 257 - the last added by this change's commit-agreement error path), but no such file exists anywhere in the repository and none ever has. It is the maintainer's private build-host notes, which contain LAN IPs and SSH key names and so must not be committed to a public repo. I could not resolve this without either creating a new documentation surface (disallowed here, and unsafe with that content) or deleting pointers that are genuinely useful to the maintainer. Suggested resolution: reword the three references to name it as private maintainer notes rather than a repo document, or add a repo-safe build doc that omits host addresses and credentials.
  • ℹ️ vscode/src/engineDownload.ts:110 - Follow-up, deliberately left out of scope: the repo's bundling-era rationale comments disagree about how large one engine is. This change settled on ~120 MB (fetch-engine.js:155, CodeGraphServerResolver.kt:88, consistent with the "100-126 MB each" and "116 MB binary" claims elsewhere), but vscode/src/engineDownload.ts:110 and jetbrains/.../IndexingStartupActivity.kt:41 still call it a ~30 MB download, and EngineDownloader.kt:23 plus scripts/publish-release-assets.sh:8 still argue against a 498 MB npm package that no longer bundles anything. These predate this change and live in files it never touched, so sweeping them here would multiply edits well beyond the arm64 work; they are worth one consolidating pass of their own.

🔧 Fix: replace dangling build-notes pointers, correct engine download sizes
✅ Re-checked - no issues remain.

⚠️ **Lint** - 2 infos
  • ⚠️ vscode/package.json:1797 - The VS Code extension declares &#34;lint&#34;: &#34;eslint src --ext ts&#34; and depends on eslint ^8.55.0, but no eslint configuration exists anywhere in the repository - no .eslintrc*, no eslint.config.*, and no eslintConfig key in vscode/package.json (verified by find across the tree and by parsing the package.json). Under eslint 8 that script fails with "No ESLint configuration found", so the repo's only configured JS/TS linter cannot run, and I could not lint the two TypeScript files this pass touched (vscode/src/engineDownload.ts, vscode/src/extension.ts). That the code already carries an // eslint-disable-next-line @typescript-eslint/no-var-requires comment at engineDownload.ts:24 suggests a config existed once and was lost. This is pre-existing and not introduced by the arm64 change, and I did not author a config myself: choosing a ruleset is a judgment call that would likely flag many pre-existing files and amounts to a new configuration surface well outside this change's scope. Suggested resolution: restore an .eslintrc.json for vscode/ with @typescript-eslint, or drop the lint script and the eslint devDependency if linting is no longer intended.

🔧 Fix: restore vscode eslint config, clear resulting lint errors
2 infos still open:

  • ℹ️ vscode/.eslintrc.json:48 - The restored ESLint config sets @typescript-eslint/no-explicit-any to "warn" in extension source and "off" in **/*.test.ts, rather than the recommended default of "error". This is a deliberate, documented choice, not an oversight, and it is disclosed here so the severity is the user's call rather than silently mine. At "error" the rule produced 206 of the 268 pre-existing failures (186 in test files, 20 in source), which would have left npm run lint permanently red and therefore unrunnable - a config nobody can adopt is no better than the missing one this replaces. The 20 source-level sites are concentrated in boundary code handling untyped language-server protocol payloads (extension.ts, reporter.ts, toolManager.ts, commands/index.ts); typing them properly means pinning down the LSP payload surface, which is a design change rather than a lint fix and is unsafe to do mechanically. The 186 test-file sites are in mocks that must mirror the shape of the real vscode API, including its wide types, so enforcing the rule there would push the doubles away from the API they imitate. Net effect: npm run lint now exits 0 with 20 visible warnings as a typing-debt backlog. No action needed unless the user wants the source-level any usages tightened to "error" as a follow-up task.
  • ℹ️ vscode/package-lock.json:3 - vscode/package-lock.json records &#34;version&#34;: &#34;0.15.0&#34; (in both the root object and packages[&#34;&#34;]) while vscode/package.json is at 0.20.1. I discovered this only because installing the dev dependencies needed to run ESLint caused npm to auto-sync the field, and I reverted that rewrite to keep this lint phase's diff scoped to lint fixes. It is not a lint, format, or static-analysis issue and has no effect on builds or on the extension, but the drift is real and will re-appear as an unrelated diff in the next person's npm install. Reported as information only; deliberately not fixed here since a lockfile change belongs in its own commit rather than smuggled into a lint pass.
✅ **Push** - passed

✅ No issues found.

anvanster and others added 7 commits August 9, 2026 12:33
ARM Linux users had no engine at all: platformBinaryName() returned null
for linux-arm64, so npm's postinstall printed "unsupported platform" and
exited, and the JetBrains resolver refused too. Issue #15 came from one of
the few people who worked around that by building from source.

Adds the asset to both client mappings and to the release, and pins how it
is built:

scripts/build-linux-arm64.sh builds in a container because there is no
arm64 Linux build machine, and the compatibility floor is easy to get
wrong. It does not only build - it refuses to produce a binary that would
narrow support:

  - __libc_single_threaded must land in writable memory. An immutable
    definition is read-only and, on aarch64, also exported, so glibc's
    startup write to it faults before main(). That was issue #15, and this
    is its regression guard.
  - the glibc floor must stay <= 2.30 and GLIBCXX <= 3.4.29, the floors
    measured from the shipped x64 asset.

Ubuntu 20.04 + gcc-11 is the only combination measured to give both a
working link and those floors. The alternatives failed and the reasons are
recorded in the script: Debian 11 (gcc 10) cannot resolve ONNX Runtime's
__throw_bad_array_new_length or __aarch64_cas8_sync; SLES 15 SP4's own
gcc11 ships no outline atomics; Ubuntu 22.04 links but floors at glibc
2.34, which would have given ARM a narrower support range than x64.

Verified by running the built binary: it works on SLES 15 SP4, Ubuntu
22.04+, Debian 12+, RHEL 9+ and Amazon Linux 2023, and not on Ubuntu
20.04, Debian 11, RHEL 8 or Amazon Linux 2 - identical to the x64 asset.
Note the binding constraint is GLIBCXX_3.4.29, not glibc, which is why it
does not run on its own build host.

Also collapses three hand-kept copies of the platform list into one.
PUBLISHED_BINARIES in bin/fetch-engine.js is now the source of truth, read
by publish-release-assets.sh and package-npm.sh, so a sixth platform
cannot be published without every client resolving it - the drift that
made this change touch five files. A `while read` loop rather than
mapfile: these scripts run under macOS's bash 3.2, where mapfile does not
exist and would have silently yielded an empty list.

The two tests asserting linux-arm64 was unsupported are inverted rather
than deleted; both languages now assert it resolves to its own asset and
never to the x64 one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017rVbt7rENTwXkdHt3Bpgb5
The project published no platform requirements anywhere users could read
them, while the internal build notes claimed a Linux range that was wrong.
A user on RHEL 8 hitting `GLIBCXX_3.4.29 not found` had nothing to consult.

Two floors matter and only one of them is obvious. glibc 2.30 is not the
binding constraint: the engine embeds ONNX Runtime, built with GCC 11, so
it also needs libstdc++ >= GLIBCXX_3.4.29. Checking only glibc is what
produced the earlier claim that Debian 11 and Ubuntu 20.04 were supported;
both ship 3.4.28 and have never been able to start the engine.

The runs / does-not-run table is measured by executing the 0.20.1 binaries
on each distribution, not inferred from symbol versions, and is identical
for linux-x64 and linux-arm64. Also documents linux-arm64 as supported,
and gives the GLIBCXX error the remedy that does not require a distro
upgrade.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017rVbt7rENTwXkdHt3Bpgb5
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

🔍 CodeGraph PR Review

23 files changed (+761/−97, 12 functions) · Risk: 🔴 high

Blast radius

4 direct callers affected (2 breaking) across CodeGraph/CodeGraph/scripts, CodeGraph/vscode/src

⚠️ Test gaps (12 functions, 0 coverage)

  • execute (jetbrains/src/main/kotlin/ai/codegraph/jetbrains/indexing/IndexingStartupActivity.kt) — body_changed
  • test arm64 linux gets its own engine and arm64 windows emulates x64 (jetbrains/src/test/kotlin/ai/codegraph/jetbrains/server/CodeGraphServerResolverTest.kt) — signature_changed
  • test downloads and installs the engine for this platform (jetbrains/src/test/kotlin/ai/codegraph/jetbrains/server/EngineDownloaderTest.kt) — body_changed
  • test an arm64 linux ide installs the arm64 engine (jetbrains/src/test/kotlin/ai/codegraph/jetbrains/server/EngineDownloaderTest.kt) — signature_changed
  • run (mcp-package/test/fetch-engine.test.js) — body_changed
  • manifest_commit (scripts/publish-release-assets.sh) — signature_changed
  • manifest_line (scripts/publish-release-assets.sh) — signature_changed
  • manifest_version (scripts/publish-release-assets.sh) — signature_changed
  • same_commit (scripts/stamp-binary.sh) — signature_changed
  • registerTools (vscode/src/ai/toolManager.ts) — body_changed
  • …and 2 more

Suggested reviewers

Andrey Vasilevsky (75 lines), anvanster (22 lines)

Suggested commit: feat(scripts): <describe the change> · 0 tests cover the changes
🤖 Generated by CodeGraph

@anvanster
anvanster merged commit 489ccf1 into main Aug 9, 2026
1 check passed
@anvanster
anvanster deleted the feat/linux-arm64-engine branch August 9, 2026 20:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant