Skip to content

Conversation

@atwright147
Copy link

@atwright147 atwright147 commented Aug 28, 2025

Overview

This PR introduces a powerful Git-style hooks system for fnm, starting with comprehensive support for Node.js installation hooks. Users can now create custom scripts that automatically execute at key points during the installation process, enabling automation of environment setup, package management, notifications, and more.

Fixes #1060

Disclaimer

I am not a Rust developer, but I love Fnm and I feel that this feature would be extremely useful. So I used Claude Sonnet 4 to generate a Hooks system.

The following is the prompt I used to generate this code:

Add a hooks mechanism. It should work much like Git's hooks, in that it will look for a hook script file in specific locations, if the file exists it will execute it. The script file can be in any executable script format (shell, nodejs etc). It should look for the files in the Fnm install dir, in a subfolder called hooks.

First of all, analyse the codebase and suggest possible places to add hooks and I will decide which you should create.

I hope this PR is either good enough to merge or can act as a good starting point for discussion and implementation.

Features

Hook Types

pre-install: Executed before downloading and installing a Node.js version
post-install: Executed after successful installation of a Node.js version
install-failed`: Executed when Node.js installation fails

Hook Location

Hooks are stored in the hooks/ subdirectory of the fnm directory:

Default: ~/.fnm/hooks/
Custom: $FNM_DIR/hooks/ (when using custom FNM_DIR)

Environment Variables

All hooks receive rich context through environment variables:

FNM_VERSION: Node.js version being installed (e.g., "v18.17.0")
FNM_ARCH: Architecture being used (e.g., "x64", "arm64")
FNM_DIR: fnm installation directory
FNM_INSTALLATION_DIR: Specific installation directory for this version

Technical Implementation

Core Components

New hooks module hooks.rs: Complete hooks management system with cross-platform executable detection and error handling
Install command integration install.rs: Seamless hook execution at appropriate lifecycle points
Configuration support config.rs: Added base_dir() method for hooks directory resolution

Cross-Platform Support

  • Unix: Permission-based executable detection (chmod +x)
  • Windows: Extension-based detection (.exe, .bat, .cmd, .ps1)

Error Handling

  • Hook failures don't break the installation process
  • Comprehensive error logging and user feedback
  • Graceful fallback when hooks are missing or non-executable

Usage Examples

🔥 Auto-install global packages 🔥

This replicates a killer feature of NVM and is the main reason I wanted to add this feature

#!/bin/bash
# ~/.fnm/hooks/post-install
npm install -g pnpm yarn typescript eslint prettier
corepack enable

Environment setup

#!/bin/bash
# ~/.fnm/hooks/pre-install
echo "🚀 Installing Node.js $FNM_VERSION for $FNM_ARCH"
npm cache clean --force 2>/dev/null || true

Project dependency management

#!/bin/bash
# ~/.fnm/hooks/post-install
if [ -f "./package-lock.json" ]; then
    echo "📦 Installing project dependencies..."
    npm ci
fi

Error logging and notifications

#!/bin/bash
# ~/.fnm/hooks/install-failed
echo "$(date): Failed to install $FNM_VERSION" >> ~/.fnm/install-failures.log
osascript -e "display notification \"Failed to install Node.js $FNM_VERSION\" with title \"fnm Error\""

Testing

  • Unit tests: Hook context creation and detection logic
  • Integration tests: End-to-end hook execution validation
  • Cross-platform compatibility: Tested on Unix and Windows systems
  • Example scripts: Provided for common use cases

Documentation

HOOKS.md: Comprehensive documentation with examples, troubleshooting, and best practices
Hook templates: Ready-to-use examples for common scenarios
API reference: Complete environment variable and error handling documentation

Future Enhancements

This PR establishes the foundation for a comprehensive hooks system. Future enhancements could include:

  • Additional hook types (pre-use, post-use, pre-uninstall, post-uninstall, pre-exec, post-exec)
  • Hook configuration files and templates
  • Async hook execution
  • Hook dependency management

Testing Instructions

  1. Build fnm: cargo build --release
  2. Create hooks directory: mkdir -p ~/.fnm/hooks
  3. Add example hook:
echo '#!/bin/bash
echo "🪝 Hook executed for $FNM_VERSION"' > ~/.fnm/hooks/pre-install
chmod +x ~/.fnm/hooks/pre-install
  1. Test installation: fnm install 18.17.0
  2. Verify hook execution: Look for hook output during installation

This implementation provides a solid foundation for user automation and workflow integration while maintaining fnm's simplicity and performance characteristics.

@changeset-bot
Copy link

changeset-bot bot commented Aug 28, 2025

⚠️ No Changeset found

Latest commit: 9fa9ab5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Aug 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
fnm Ready Ready Preview Comment Aug 28, 2025 4:17pm

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.

Hooks

1 participant