| #!/usr/bin/env node | |
| /** | |
| * | |
| * Copyright (c) 2020 Silicon Labs | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /** | |
| * Generates ~/.config/husky/init.sh so that GUI git clients (Sourcetree, | |
| * Tower, GitHub Desktop, etc.) can find node/npx when running pre-commit | |
| * hooks. Husky v9 sources that file automatically before every hook. | |
| * | |
| * Uses `which node` to locate the stable symlink path of the node binary | |
| * (e.g. /opt/homebrew/opt/node@24/bin) rather than process.execPath, which | |
| * on Homebrew resolves to a versioned Cellar path that breaks on patch | |
| * upgrades. Falls back to process.execPath when `which` is unavailable. | |
| * | |
| * Skips write when the file already exists so manual customisations are | |
| * preserved. Skips entirely in CI environments where hooks do not run. | |
| */ | |
| 'use strict' | |
| const fs = require('fs') | |
| const path = require('path') | |
| const os = require('os') | |
| const { execSync } = require('child_process') | |
| // Husky itself skips hooks in CI; skip this script too. | |
| if (process.env.CI) { | |
| process.exit(0) | |
| } | |
| const huskyConfigDir = path.join(os.homedir(), '.config', 'husky') | |
| const initFile = path.join(huskyConfigDir, 'init.sh') | |
| if (fs.existsSync(initFile)) { | |
| console.log(`âšī¸ ~/.config/husky/init.sh already exists â skipping.`) | |
| process.exit(0) | |
| } | |
| // Prefer `which node` (the symlink / stable PATH entry, e.g. | |
| // /opt/homebrew/opt/node@24/bin) over process.execPath, which on Homebrew | |
| // resolves to a versioned Cellar path that breaks on patch upgrades. | |
| // Fall back to process.execPath for environments where `which` is unavailable. | |
| let nodeDir | |
| try { | |
| const whichNode = execSync('which node', { encoding: 'utf8' }).trim() | |
| nodeDir = path.dirname(whichNode) | |
| } catch (_) { | |
| nodeDir = path.dirname(process.execPath) | |
| } | |
| const content = `# Auto-generated by npm install via src-script/setup-husky-env.js | |
| # Sourced by Husky v9 before every hook to make node/npx available in | |
| # GUI git clients that launch git without a full shell environment. | |
| # Delete this file and re-run "npm install" to regenerate it. | |
| export PATH="${nodeDir}:$PATH" | |
| ` | |
| fs.mkdirSync(huskyConfigDir, { recursive: true }) | |
| fs.writeFileSync(initFile, content, { mode: 0o644 }) | |
| console.log(`â Created ~/.config/husky/init.sh (node dir: ${nodeDir})`) |