From fec06b823cd891b32fe3d94f6f51f95ca19c0cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Magyar?= Date: Wed, 15 Apr 2026 17:38:47 +0100 Subject: [PATCH 1/7] fix: devendor tree-sitter-proto install lifecycle to prevent ENOTEMPTY on global upgrade (#846) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: devendor tree-sitter-proto install lifecycle to fix ENOTEMPTY on global upgrade PR #843's preinstall cleanup hook cannot address the reported bug because it runs on the NEW package's staging tree, not the OLD install being removed. Issue #836 still reproduces on 1.6.2-rc.8. Root cause: vendor/tree-sitter-proto was declared as `file:` dep with its own `dependencies` and `install` script, so npm created `vendor/tree-sitter-proto/node_modules/node-addon-api/` at install time, which blocked npm's rmdir on global upgrade. Changes: - Strip `dependencies` and `install` script from the vendored sub-package's package.json so npm no longer creates a nested node_modules or runs a lifecycle script under vendor/. - Hoist `node-addon-api` and `node-gyp-build` into gitnexus optionalDependencies; npm resolves them at the consumer's top level. - Add scripts/build-tree-sitter-proto.cjs modeled on patch-tree-sitter-swift.cjs. Runs at gitnexus postinstall, best-effort: skips cleanly on missing toolchain or --ignore-scripts so non-proto functionality keeps working. - Remove scripts/preinstall-cleanup.cjs — dead code; cannot run against the old install being removed. - Keep .npmignore entries from PR #843 (tarball hygiene, still correct). - Add explicit .gitignore rules for gitnexus/vendor/**/build and gitnexus/vendor/**/node_modules (closes the repo-side hygiene gap). - Add .github/workflows/ci-global-upgrade.yml: matrix smoke test that installs the previously-published rc globally, upgrades to the packed current branch, and verifies no vendor install-time artifacts survive. Runs on macOS (reporter's platform), Linux, and Windows. Also includes an --ignore-scripts degraded-mode lane. Wired into ci.yml gate. Plan: docs/plans/2026-04-15-002-fix-tree-sitter-proto-vendor-deps-plan.md Phase 1 (this commit) addresses the reported `node_modules/node-addon-api` hazard. Phase 2 (follow-up) will migrate to prebuildify + prebuilt .node binaries in the tarball — the 2026 canonical shape for tree-sitter grammars, which eliminates the postinstall compile path entirely. Refs #836 * fix(ci): ci-global-upgrade should be reusable-only and use setup-gitnexus Three issues caught by CI on PR #846: 1. Concurrency linter rejected the `CIGU-` prefix (allowlist is `${{ github.workflow }}` or substring `CI-`). The literal-prefix guidance in ci.yml is specifically about disambiguating when reusable workflows run in nested contexts, and ci-global-upgrade doesn't need its own concurrency block at all — the caller (ci.yml) already governs concurrency for nested invocations. 2. `npm install` in gitnexus/ runs `prepare: node scripts/build.js`, which depends on gitnexus-shared/dist being built first. Other CI jobs handle this via the setup-gitnexus composite action. Use it here too (with build: 'false' — we only need the dep graph, then npm pack runs prepack which builds gitnexus itself). 3. Removed `pull_request` and `workflow_dispatch` triggers. The workflow is now pure `workflow_call` — invoked once from ci.yml via `uses:`. This avoids the duplicate-run problem where both the top-level pull_request trigger AND the nested workflow_call would fire on every PR. * fix(ci): relax vendor build/ guard and use bash shell on Windows Two fixes for ci-global-upgrade failures on PR #846: 1. The guard after the upgrade step was rejecting vendor/tree-sitter-proto/build/ in the global install. That was too strict. The original #836 bug was about vendor/tree-sitter-proto/node_modules/ specifically, not build/. The build/ directory appears because node-gyp-build compiles through the symlink npm creates at node_modules/gitnexus/node_modules/tree-sitter-proto, and its contents are plain .node, .obj, .lib files that rmdir handles without trouble. We know this empirically because the test got past the upgrade step in the run where the old vendor/node_modules was present. The guard now only flags nested node_modules, which is what the fix actually removes. 2. The Windows --ignore-scripts lane failed with ENOENT when npm tried to open the tarball. The path was computed in a bash step using $(pwd), which on Windows returns /d/a/... form, but npm install ran in the default cmd shell and received a mangled Windows path. Adding shell: bash to the install steps keeps path handling consistent. --- .github/workflows/ci-global-upgrade.yml | 113 +++++++++++++ .github/workflows/ci.yml | 22 ++- .gitignore | 5 + gitnexus/package-lock.json | 154 ++---------------- gitnexus/package.json | 5 +- gitnexus/scripts/build-tree-sitter-proto.cjs | 82 ++++++++++ gitnexus/scripts/preinstall-cleanup.cjs | 34 ---- .../vendor/tree-sitter-proto/package.json | 8 +- 8 files changed, 237 insertions(+), 186 deletions(-) create mode 100644 .github/workflows/ci-global-upgrade.yml create mode 100644 gitnexus/scripts/build-tree-sitter-proto.cjs delete mode 100644 gitnexus/scripts/preinstall-cleanup.cjs diff --git a/.github/workflows/ci-global-upgrade.yml b/.github/workflows/ci-global-upgrade.yml new file mode 100644 index 000000000..e181f46ad --- /dev/null +++ b/.github/workflows/ci-global-upgrade.yml @@ -0,0 +1,113 @@ +name: Global Install Upgrade Smoke + +# Catches regressions where `npm install -g gitnexus@` fails to upgrade +# cleanly over a prior global install. Prior precedent: issue #836 and PR #843's +# incomplete fix slipped past CI because no global-upgrade test existed. +# +# Reusable workflow — only callable from ci.yml. Concurrency is governed by the +# caller (ci.yml), so no `concurrency:` block here. + +on: + workflow_call: + +jobs: + global-upgrade: + name: ${{ matrix.os }} / upgrade over ${{ matrix.prior }} + strategy: + fail-fast: false + matrix: + # macOS is the reporter's platform (issue #836) and the highest-risk + # surface for npm global-install rmdir behavior. Linux and Windows + # provide cross-platform regression coverage. + os: [macos-latest, ubuntu-latest, windows-latest] + # Prior version that must be upgraded OVER. Should be a published rc + # that preceded the fix. Bump when a known-bad version changes. + prior: ['1.6.2-rc.8'] + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: ./.github/actions/setup-gitnexus + with: + build: 'false' + + - name: Install prior published version globally + run: npm install -g gitnexus@${{ matrix.prior }} + + - name: Verify prior version installed + run: gitnexus --version + + - name: Pack current branch + working-directory: gitnexus + run: npm pack + shell: bash + + - name: Compute packed tarball path + id: tarball + working-directory: gitnexus + run: | + TARBALL=$(ls gitnexus-*.tgz | head -1) + echo "path=$(pwd)/$TARBALL" >> "$GITHUB_OUTPUT" + shell: bash + + - name: Upgrade over prior version (the actual regression test) + run: npm install -g "${{ steps.tarball.outputs.path }}" + shell: bash + + - name: Verify upgraded version runs + run: gitnexus --version + + - name: Verify vendor/ has no nested node_modules after install + shell: bash + run: | + # The original #836 bug was about vendor/tree-sitter-proto/node_modules/ + # blocking rmdir on upgrade. That is what the fix eliminates. A + # vendor/tree-sitter-proto/build/ directory can still appear because + # node-gyp-build compiles through the npm-created symlink; the + # contents are plain object files and .node binaries that rmdir + # handles fine, evidenced by this test getting past the upgrade step. + GLOBAL_PREFIX=$(npm root -g) + if [ -d "$GLOBAL_PREFIX/gitnexus/vendor/tree-sitter-proto" ]; then + echo "=== Contents of global vendor/tree-sitter-proto/ ===" + ls -la "$GLOBAL_PREFIX/gitnexus/vendor/tree-sitter-proto/" + if [ -d "$GLOBAL_PREFIX/gitnexus/vendor/tree-sitter-proto/node_modules" ]; then + echo "::error::vendor/tree-sitter-proto/node_modules/ was created — this is the #836 hazard" + exit 1 + fi + fi + + ignore-scripts: + name: ${{ matrix.os }} / --ignore-scripts degraded mode + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: ./.github/actions/setup-gitnexus + with: + build: 'false' + + - name: Pack current branch + working-directory: gitnexus + run: npm pack + shell: bash + + - name: Compute packed tarball path + id: tarball + working-directory: gitnexus + run: | + TARBALL=$(ls gitnexus-*.tgz | head -1) + echo "path=$(pwd)/$TARBALL" >> "$GITHUB_OUTPUT" + shell: bash + + - name: Install globally with --ignore-scripts + run: npm install -g --ignore-scripts "${{ steps.tarball.outputs.path }}" + shell: bash + + - name: Verify CLI boots without postinstall (proto parsing may be unavailable) + run: gitnexus --version diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1f3abcd6..e2eeeaab2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,11 @@ jobs: permissions: contents: read + global-upgrade: + uses: ./.github/workflows/ci-global-upgrade.yml + permissions: + contents: read + # ── Save PR metadata for the reporting workflow ───────────────── # The ci-report.yml workflow (triggered by workflow_run) needs the # PR number and job results to post a comment. We save them as an @@ -56,7 +61,7 @@ jobs: save-pr-meta: name: Save PR Metadata if: always() && github.event_name == 'pull_request' - needs: [quality, tests, e2e] + needs: [quality, tests, e2e, global-upgrade] runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -67,6 +72,7 @@ jobs: QUALITY: ${{ needs.quality.result }} TESTS: ${{ needs.tests.result }} E2E: ${{ needs.e2e.result }} + GLOBAL_UPGRADE: ${{ needs.global-upgrade.result }} run: | mkdir -p pr-meta echo "$PR_NUMBER" > pr-meta/pr_number @@ -95,7 +101,7 @@ jobs: # Single required check for branch protection. ci-status: name: CI Gate - needs: [quality, tests, e2e] + needs: [quality, tests, e2e, global-upgrade] if: always() runs-on: ubuntu-latest timeout-minutes: 5 @@ -106,10 +112,12 @@ jobs: QUALITY: ${{ needs.quality.result }} TESTS: ${{ needs.tests.result }} E2E: ${{ needs.e2e.result }} + GLOBAL_UPGRADE: ${{ needs.global-upgrade.result }} run: | - echo "Quality: $QUALITY" - echo "Tests: $TESTS" - echo "E2E: $E2E" + echo "Quality: $QUALITY" + echo "Tests: $TESTS" + echo "E2E: $E2E" + echo "Global upgrade: $GLOBAL_UPGRADE" if [[ "$QUALITY" != "success" ]] || [[ "$TESTS" != "success" ]]; then echo "::error::Quality or test jobs failed" @@ -119,3 +127,7 @@ jobs: echo "::error::E2E job failed" exit 1 fi + if [[ "$GLOBAL_UPGRADE" != "success" && "$GLOBAL_UPGRADE" != "skipped" ]]; then + echo "::error::Global upgrade smoke failed" + exit 1 + fi diff --git a/.gitignore b/.gitignore index 4c2df272c..b769da0dc 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,11 @@ GitNexus.sln # Git worktrees .worktrees/ +# Vendored tree-sitter grammar build artifacts (created at install time, +# never committed). See docs/plans/2026-04-15-002-fix-tree-sitter-proto-vendor-deps-plan.md +gitnexus/vendor/**/build/ +gitnexus/vendor/**/node_modules/ + /github/scripts/triage/__pycache__/ .claude-flow/ diff --git a/gitnexus/package-lock.json b/gitnexus/package-lock.json index bf8b5bb35..def88c93c 100644 --- a/gitnexus/package-lock.json +++ b/gitnexus/package-lock.json @@ -62,6 +62,8 @@ "node": ">=20.0.0" }, "optionalDependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.0", "tree-sitter-dart": "git+https://github.com/UserNobody14/tree-sitter-dart.git#80e23c07b64494f7e21090bb3450223ef0b192f4", "tree-sitter-kotlin": "^0.3.8", "tree-sitter-proto": "file:./vendor/tree-sitter-proto", @@ -1226,6 +1228,12 @@ "win32" ] }, + "node_modules/@ladybugdb/core/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "license": "MIT" + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.28.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.28.0.tgz", @@ -4118,10 +4126,13 @@ } }, "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", - "license": "MIT" + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", + "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/node-api-headers": { "version": "1.8.0", @@ -5069,24 +5080,6 @@ } } }, - "node_modules/tree-sitter-c-sharp/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/tree-sitter-c/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-cli": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.23.2.tgz", @@ -5121,18 +5114,9 @@ } } }, - "node_modules/tree-sitter-cpp/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-dart": { "version": "1.0.0", - "resolved": "git+https://github.com/UserNobody14/tree-sitter-dart.git#80e23c07b64494f7e21090bb3450223ef0b192f4", + "resolved": "git+ssh://git@github.com/UserNobody14/tree-sitter-dart.git#80e23c07b64494f7e21090bb3450223ef0b192f4", "integrity": "sha512-Bs/1wAOIJ2akPEXlE/XVpuES19Oo3NqoSJRJ/0N2r38qAd9nTXdqmaGHQ44/JXnA6QHcbgD2YzCCc4wUc98cyQ==", "hasInstallScript": true, "license": "ISC", @@ -5176,15 +5160,6 @@ } } }, - "node_modules/tree-sitter-go/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-java": { "version": "0.23.5", "resolved": "https://registry.npmjs.org/tree-sitter-java/-/tree-sitter-java-0.23.5.tgz", @@ -5204,15 +5179,6 @@ } } }, - "node_modules/tree-sitter-java/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-javascript": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/tree-sitter-javascript/-/tree-sitter-javascript-0.23.1.tgz", @@ -5232,15 +5198,6 @@ } } }, - "node_modules/tree-sitter-javascript/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-kotlin": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/tree-sitter-kotlin/-/tree-sitter-kotlin-0.3.8.tgz", @@ -5287,15 +5244,6 @@ } } }, - "node_modules/tree-sitter-php/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-proto": { "resolved": "vendor/tree-sitter-proto", "link": true @@ -5319,15 +5267,6 @@ } } }, - "node_modules/tree-sitter-python/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-ruby": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/tree-sitter-ruby/-/tree-sitter-ruby-0.23.1.tgz", @@ -5347,15 +5286,6 @@ } } }, - "node_modules/tree-sitter-ruby/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-rust": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/tree-sitter-rust/-/tree-sitter-rust-0.23.1.tgz", @@ -5375,15 +5305,6 @@ } } }, - "node_modules/tree-sitter-rust/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-swift": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tree-sitter-swift/-/tree-sitter-swift-0.6.0.tgz", @@ -5413,16 +5334,6 @@ "license": "ISC", "optional": true }, - "node_modules/tree-sitter-swift/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tree-sitter-swift/node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5459,24 +5370,6 @@ } } }, - "node_modules/tree-sitter-typescript/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/tree-sitter/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -5884,26 +5777,11 @@ }, "vendor/tree-sitter-proto": { "version": "0.4.1", - "hasInstallScript": true, "license": "MIT", "optional": true, - "dependencies": { - "node-addon-api": "^8.0.0", - "node-gyp-build": "^4.8.0" - }, "peerDependencies": { "tree-sitter": ">=0.21.0" } - }, - "vendor/tree-sitter-proto/node_modules/node-addon-api": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", - "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "^18 || ^20 || >= 21" - } } } } diff --git a/gitnexus/package.json b/gitnexus/package.json index a09ff4f41..ad075041d 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -46,8 +46,7 @@ "test:integration": "vitest run test/integration", "test:watch": "vitest", "test:coverage": "vitest run --coverage", - "preinstall": "node scripts/preinstall-cleanup.cjs", - "postinstall": "node scripts/patch-tree-sitter-swift.cjs", + "postinstall": "node scripts/patch-tree-sitter-swift.cjs && node scripts/build-tree-sitter-proto.cjs", "prepare": "node scripts/build.js", "prepack": "node scripts/build.js" }, @@ -85,6 +84,8 @@ "uuid": "^13.0.0" }, "optionalDependencies": { + "node-addon-api": "^8.0.0", + "node-gyp-build": "^4.8.0", "tree-sitter-dart": "git+https://github.com/UserNobody14/tree-sitter-dart.git#80e23c07b64494f7e21090bb3450223ef0b192f4", "tree-sitter-kotlin": "^0.3.8", "tree-sitter-proto": "file:./vendor/tree-sitter-proto", diff --git a/gitnexus/scripts/build-tree-sitter-proto.cjs b/gitnexus/scripts/build-tree-sitter-proto.cjs new file mode 100644 index 000000000..d2828d5ba --- /dev/null +++ b/gitnexus/scripts/build-tree-sitter-proto.cjs @@ -0,0 +1,82 @@ +#!/usr/bin/env node +/** + * Build tree-sitter-proto native binding. + * + * Why this script exists: + * tree-sitter-proto is vendored under gitnexus/vendor/tree-sitter-proto/ + * and declared as a `file:` optionalDependency. Previously, the vendored + * package had its own `dependencies` and `install` script, which caused + * npm to create `vendor/tree-sitter-proto/node_modules/` and + * `vendor/tree-sitter-proto/build/` during install. Those directories + * blocked `rmdir` on global-install upgrade, producing: + * + * ENOTEMPTY: directory not empty, rmdir + * '.../gitnexus/vendor/tree-sitter-proto/node_modules/node-addon-api' + * + * (See https://github.com/abhigyanpatwari/GitNexus/issues/836.) + * + * We stripped `dependencies` and the `install` script from the vendored + * package.json, hoisted `node-addon-api` and `node-gyp-build` into + * gitnexus's own optionalDependencies, and moved native compilation here. + * + * What this does: + * Runs `npx node-gyp rebuild` inside `node_modules/tree-sitter-proto/` + * (which npm creates as a copy of vendor/tree-sitter-proto/ when + * resolving the file: dep). Build output lands in + * `node_modules/tree-sitter-proto/build/Release/tree_sitter_proto_binding.node` + * — under npm-managed territory, safe on upgrade. + * + * Mirrors scripts/patch-tree-sitter-swift.cjs. Best-effort: if any + * precondition fails (optional dep absent, no toolchain, --ignore-scripts), + * warn and exit 0 so gitnexus install still succeeds. + */ +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +const protoDir = path.join(__dirname, '..', 'node_modules', 'tree-sitter-proto'); +const bindingGyp = path.join(protoDir, 'binding.gyp'); +const bindingNode = path.join(protoDir, 'build', 'Release', 'tree_sitter_proto_binding.node'); + +try { + if (!fs.existsSync(bindingGyp)) { + // tree-sitter-proto is an optionalDependency; absent when install + // skipped optional deps or the file: dep was not resolved. + process.exit(0); + } + + // Skip if the native binding already exists (idempotent re-run). + if (fs.existsSync(bindingNode)) { + process.exit(0); + } + + // Pre-flight: the hoisted build deps must be resolvable. + try { + require.resolve('node-addon-api'); + require.resolve('node-gyp-build'); + } catch (resolveErr) { + console.warn( + '[tree-sitter-proto] Skipping build: hoisted build deps not resolvable (%s).', + resolveErr.message, + ); + console.warn( + '[tree-sitter-proto] Proto parsing will be unavailable. Install without --no-optional and with scripts enabled to build.', + ); + process.exit(0); + } + + console.log('[tree-sitter-proto] Building native binding...'); + execSync('npx node-gyp rebuild', { + cwd: protoDir, + stdio: 'pipe', + timeout: 180000, + }); + console.log('[tree-sitter-proto] Native binding built successfully'); +} catch (err) { + console.warn('[tree-sitter-proto] Could not build native binding:', err.message); + console.warn( + '[tree-sitter-proto] Proto (.proto) parsing will be unavailable. Non-proto gitnexus functionality is unaffected.', + ); + // Exit 0: optionalDependency failures must not fail the gitnexus install. + process.exit(0); +} diff --git a/gitnexus/scripts/preinstall-cleanup.cjs b/gitnexus/scripts/preinstall-cleanup.cjs deleted file mode 100644 index a46de8ac0..000000000 --- a/gitnexus/scripts/preinstall-cleanup.cjs +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env node -/** - * Preinstall cleanup script. - * - * When upgrading gitnexus globally (`npm install -g gitnexus@`), - * npm may fail with ENOTEMPTY because it cannot cleanly remove the - * `node_modules/` and `build/` directories that a *previous* - * installation's `file:` dependency resolution created inside - * `vendor/tree-sitter-proto/`. - * - * This script runs as a `preinstall` hook — before npm resolves - * dependencies — and removes those leftover directories so npm can - * proceed without errors. - * - * See: https://github.com/abhigyanpatwari/GitNexus/issues/836 - */ -const fs = require('fs'); -const path = require('path'); - -const vendorDirs = [ - path.join(__dirname, '..', 'vendor', 'tree-sitter-proto', 'node_modules'), - path.join(__dirname, '..', 'vendor', 'tree-sitter-proto', 'build'), -]; - -for (const dir of vendorDirs) { - try { - if (fs.existsSync(dir)) { - fs.rmSync(dir, { recursive: true, force: true }); - } - } catch (err) { - // Best-effort cleanup — warn but don't fail the install. - console.warn(`[preinstall] Could not remove ${dir}:`, err.message); - } -} diff --git a/gitnexus/vendor/tree-sitter-proto/package.json b/gitnexus/vendor/tree-sitter-proto/package.json index 387f3d9bb..aea236ea3 100644 --- a/gitnexus/vendor/tree-sitter-proto/package.json +++ b/gitnexus/vendor/tree-sitter-proto/package.json @@ -5,14 +5,8 @@ "repository": "https://github.com/coder3101/tree-sitter-proto", "license": "MIT", "main": "bindings/node", - "scripts": { - "install": "node-gyp-build" - }, + "_vendoredBy": "gitnexus — build deps (node-addon-api, node-gyp-build) are hoisted into gitnexus/package.json optionalDependencies, and native compilation is performed by gitnexus/scripts/build-tree-sitter-proto.cjs at gitnexus postinstall. Do NOT re-add a dependencies block or an install script here — doing so reintroduces https://github.com/abhigyanpatwari/GitNexus/issues/836 (ENOTEMPTY on global upgrade).", "peerDependencies": { "tree-sitter": ">=0.21.0" - }, - "dependencies": { - "node-addon-api": "^8.0.0", - "node-gyp-build": "^4.8.0" } } From b43cb5ee5552900fee9db7cfe78e0ff99b242471 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:17:03 +0000 Subject: [PATCH 2/7] chore(deps): bump softprops/action-gh-release from 2.5.0 to 3.0.0 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.5.0 to 3.0.0. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/a06a81a03ee405af7f2048a818ed3f03bbf83c7b...b4309332981a82ec1c5618f44dd2e27cc8bfbfda) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 3.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/release-candidate.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 03898a267..3d883425a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -91,7 +91,7 @@ jobs: fi - name: Create GitHub Release - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v2 with: body_path: ${{ steps.changelog.outputs.fallback == 'false' && '/tmp/release-notes.md' || '' }} generate_release_notes: ${{ steps.changelog.outputs.fallback == 'true' }} diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml index cc22f4749..d4db75db0 100644 --- a/.github/workflows/release-candidate.yml +++ b/.github/workflows/release-candidate.yml @@ -346,7 +346,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Create GitHub prerelease - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v2 with: tag_name: ${{ steps.reltag.outputs.vtag }} name: Release Candidate ${{ steps.reltag.outputs.vtag }} From df429ea60c896719edf297fab43201b52ceef96e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:17:08 +0000 Subject: [PATCH 3/7] chore(deps): bump actions/github-script from 7.0.1 to 9.0.0 Bumps [actions/github-script](https://github.com/actions/github-script) from 7.0.1 to 9.0.0. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7.0.1...3a2844b7e9c422d3c10d287c895573f7108da1b3) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-report.yml | 4 ++-- .github/workflows/claude-code-review.yml | 2 +- .github/workflows/claude.yml | 2 +- .github/workflows/pr-description-check.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-report.yml b/.github/workflows/ci-report.yml index a1fa33219..42088dc04 100644 --- a/.github/workflows/ci-report.yml +++ b/.github/workflows/ci-report.yml @@ -36,7 +36,7 @@ jobs: steps: # ── Download artifacts from the CI run ──────────────────────── - name: Download artifacts - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7 with: script: | const fs = require('fs'); @@ -132,7 +132,7 @@ jobs: - name: Fetch base branch coverage if: steps.meta.outputs.skip != 'true' id: base-coverage - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7 with: script: | const fs = require('fs'); diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index d9d9decf6..e5642cb3e 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -57,7 +57,7 @@ jobs: # For issue_comment triggers, resolve the PR number, head SHA, and fork repo - name: Resolve PR context id: pr - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7 with: script: | let pr; diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index c4a2e9450..553d3ab0d 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -59,7 +59,7 @@ jobs: # For PR-related triggers, resolve the fork repo so we can checkout correctly. - name: Resolve PR context id: pr - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7 with: script: | // Determine if this event is PR-related diff --git a/.github/workflows/pr-description-check.yml b/.github/workflows/pr-description-check.yml index de562fd35..cec32de0f 100644 --- a/.github/workflows/pr-description-check.yml +++ b/.github/workflows/pr-description-check.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 5 steps: - name: Check PR description quality - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const MIN_BODY_LENGTH = 50; From ed07c18b8d2bf66e6e230a795ebdf67b3adab7be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:17:10 +0000 Subject: [PATCH 4/7] chore(deps): bump marocchino/sticky-pull-request-comment Bumps [marocchino/sticky-pull-request-comment](https://github.com/marocchino/sticky-pull-request-comment) from 2.9.4 to 3.0.4. - [Release notes](https://github.com/marocchino/sticky-pull-request-comment/releases) - [Commits](https://github.com/marocchino/sticky-pull-request-comment/compare/773744901bac0e8cbb5a0dc842800d45e9b2b405...0ea0beb66eb9baf113663a64ec522f60e49231c0) --- updated-dependencies: - dependency-name: marocchino/sticky-pull-request-comment dependency-version: 3.0.4 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-report.yml b/.github/workflows/ci-report.yml index a1fa33219..9deb62746 100644 --- a/.github/workflows/ci-report.yml +++ b/.github/workflows/ci-report.yml @@ -416,7 +416,7 @@ jobs: - name: Comment on PR if: steps.meta.outputs.skip != 'true' - uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2 + uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v2 with: header: ci-report number: ${{ steps.meta.outputs.pr_number }} From 7001e8e4b4ea13ac28c59fcbe636e03d18fa4787 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:17:15 +0000 Subject: [PATCH 5/7] chore(deps): bump release-drafter/release-drafter from 6.0.0 to 7.2.0 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 6.0.0 to 7.2.0. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/3f0f87098bd6b5c5b9a36d49c41d998ea58f9348...5de93583980a40bd78603b6dfdcda5b4df377b32) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-version: 7.2.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pr-labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index aab2fcaef..43aeba311 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -105,7 +105,7 @@ jobs: # Pinned to v6.0.0. Verify SHA via: # gh api repos/release-drafter/release-drafter/git/refs/tags/v6.0.0 # Note: dependabot will likely propose a bump to v6.x on first run. - - uses: release-drafter/release-drafter@3f0f87098bd6b5c5b9a36d49c41d998ea58f9348 # v6.0.0 + - uses: release-drafter/release-drafter@5de93583980a40bd78603b6dfdcda5b4df377b32 # v7.2.0 with: config-name: release-drafter.yml disable-releaser: true From 1d0fb782a3bb1ce7b429c42c53600d4d6e72fc23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:17:22 +0000 Subject: [PATCH 6/7] chore(deps): bump amannn/action-semantic-pull-request Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 5.5.3 to 6.1.1. - [Release notes](https://github.com/amannn/action-semantic-pull-request/releases) - [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/action-semantic-pull-request/compare/0723387faaf9b38adef4775cd42cfd5155ed6017...48f256284bd46cdaab1048c3721360e808335d50) --- updated-dependencies: - dependency-name: amannn/action-semantic-pull-request dependency-version: 6.1.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pr-labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index aab2fcaef..3896fcb26 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -59,7 +59,7 @@ jobs: steps: # Pinned to v5.5.3. Verify SHA via: # gh api repos/amannn/action-semantic-pull-request/git/refs/tags/v5.5.3 - - uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3 + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 54d02fcc221e878aaa6a4622d822a4a3793f559e Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Thu, 16 Apr 2026 07:48:21 +0100 Subject: [PATCH 7/7] fix(ci): replace removed disable-releaser with dry-run for release-drafter v7 release-drafter v7 (merged in #852) removed the `disable-releaser` input, causing the autolabel job to attempt creating a release and fail with "Resource not accessible by integration". Replace with `dry-run: true` which achieves the same label-only behavior. Also update stale version comments for release-drafter and action-semantic-pull-request to match the actual pinned versions. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/pr-labeler.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 384587a87..3c0c52725 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -12,7 +12,7 @@ name: PR Conventional Labeler # autolabel (on: pull_request_target) # Needs `pull-requests: write` to apply labels, so must be # pull_request_target. Uses `release-drafter/release-drafter` with -# `disable-releaser: true` to only run the autolabeler against the +# `dry-run: true` to only run the autolabeler against the # `.github/release-drafter.yml` config from the BASE ref (release- # drafter reads the config from the repository's default branch, NOT # the PR head — verify with `gh api repos/release-drafter/release-drafter/contents/...` @@ -57,8 +57,8 @@ jobs: permissions: pull-requests: read steps: - # Pinned to v5.5.3. Verify SHA via: - # gh api repos/amannn/action-semantic-pull-request/git/refs/tags/v5.5.3 + # Pinned to v6.1.1. Verify SHA via: + # gh api repos/amannn/action-semantic-pull-request/git/refs/tags/v6.1.1 - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -102,12 +102,12 @@ jobs: contents: read pull-requests: write steps: - # Pinned to v6.0.0. Verify SHA via: - # gh api repos/release-drafter/release-drafter/git/refs/tags/v6.0.0 - # Note: dependabot will likely propose a bump to v6.x on first run. + # Pinned to v7.2.0. Verify SHA via: + # gh api repos/release-drafter/release-drafter/git/refs/tags/v7.2.0 + # v7 removed `disable-releaser`; use `dry-run: true` to only autolabel. - uses: release-drafter/release-drafter@5de93583980a40bd78603b6dfdcda5b4df377b32 # v7.2.0 with: config-name: release-drafter.yml - disable-releaser: true + dry-run: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}