diff --git a/.github/workflows/ci-global-upgrade.yml b/.github/workflows/ci-global-upgrade.yml new file mode 100644 index 0000000000..e181f46ad2 --- /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 a1f3abcd69..e2eeeaab2c 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 4c2df272c1..b769da0dc8 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 bf8b5bb356..def88c93cd 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 a09ff4f41d..ad075041d0 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 0000000000..d2828d5baf --- /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 a46de8ac0d..0000000000 --- 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 387f3d9bb1..aea236ea3c 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" } }