Shell Integration
govman provides intelligent shell integration for automatic Go version management.
Features
- Automatic version switching based on
.govman-goversionfiles - Smart PATH management for Go binaries
- Wrapper functions for seamless
govman useexecution - Project-aware version detection
- Non-intrusive configuration with easy removal
- Security hardened with input validation and command injection prevention (v1.1.0+)
- Robust YAML parsing with fallback mechanisms (v1.1.0+)
- Duplicate prevention for hook registration (v1.1.0+)
Supported Shells
| Shell | Platform | Auto-Switch | Wrapper | Hook Mechanism |
|---|---|---|---|---|
| Bash | Linux/macOS | ✅ | ✅ | PROMPT_COMMAND |
| Zsh | Linux/macOS | ✅ | ✅ | chpwd hook |
| Fish | Linux/macOS | ✅ | ✅ | Native events |
| PowerShell | Windows | ✅ | ✅ | Set-Location hook |
| Cmd | Windows | ❌ | Partial | Not supported |
Setup
Automatic Setup
govman init
This automatically:
- Detects your current shell
- Adds integration code to your shell config file
- Sets up PATH and environment variables
- Enables automatic version switching
Manual Shell Selection
# Specify a shell explicitly
govman init --shell bash
govman init --shell zsh
govman init --shell fish
govman init --shell powershell
Force Reinitialization
# Overwrites existing configuration
govman init --force
Shell Configuration Files
govman modifies these files based on your shell:
- Bash:
~/.bashrc,~/.bash_profile, or~/.profile - Zsh:
~/.zshrc - Fish:
~/.config/fish/config.fish - PowerShell:
$PROFILE(Microsoft.PowerShell_profile.ps1) - Cmd: Creates wrapper batch file (limited functionality)
How Auto-Switching Works
Directory Change Detection
When you navigate to a directory, govman:
- Checks for
.govman-goversionfile in current directory - Reads the required Go version from the file
- Compares with currently active version
- Automatically switches if different
- Updates PATH to use the correct Go binary
Activation Priority
govman resolves the active version in this order:
- Session-only: Temporary activation via
govman use - Project-local:
.govman-goversionfile in current/parent directory - System-default: Global version set via
govman use --default
Security & Reliability Improvements (v1.1.0+)
Command Injection Prevention
All shell integration code includes strict validation before executing commands:
- Bash/Zsh: Uses
printfinstead ofechoand validates export commands against regex pattern^export PATH="[^"]*"$beforeeval - PowerShell: Validates PATH commands match
^\$env:PATH\s*=\s*"[^"]+"\s*\+\s*\$env:PATH$beforeInvoke-Expression - Fish: Improved pattern matching for
fish_add_pathcommands
Robust YAML Parsing
Configuration parsing is now more reliable:
- Uses
awk-based parsing instead of fragilegrep -A 10approach - No dependency on hardcoded line limits
- Includes default values and fallback logic
- Handles edge cases in YAML structure
Duplicate Prevention
Hook registration prevents issues from multiple config sourcing:
- Bash: Checks if
__govman_check_dir_changealready exists inPROMPT_COMMAND - Zsh: Validates hook not in
chpwd_functionsarray before adding - Fish: Removes existing function before redefining
- PowerShell: Uses
$Global:GovmanPromptInjectedflag to track prompt injection
Version Validation
Go version extraction includes format validation:
- More precise regex patterns:
\d+\.\d+(?:\.\d+)? - Validates extracted version matches expected format
- Properly handles pre-release versions (rc, beta)
- Prevents issues from malformed version strings
Shell Integration Code
Note: The code examples below reflect the latest security improvements and robustness enhancements from v1.1.0.
Bash/Zsh
# GOVMAN - Go Version Manager
export PATH="$HOME/.govman/bin:$PATH"
# Ensure GOBIN and GOPATH/bin are available
if [ -n "$GOBIN" ]; then export PATH="$GOBIN:$PATH"; fi
if command -v go > /dev/null 2>&1; then export PATH="$(go env GOPATH)/bin:$PATH"; fi
export PATH="$HOME/go/bin:$PATH"
export GOTOOLCHAIN=local
# Wrapper function for automatic PATH execution
govman() {
local govman_bin="$HOME/.govman/bin/govman"
if [[ "$1" == "use" && "$#" -ge 2 && "$2" != "--help" && "$2" != "-h" ]]; then
local output
output="$("$govman_bin" "$@" 2>&1)"
local exit_code=$?
if [[ $exit_code -eq 0 ]]; then
# Security: Use printf instead of echo, validate format before eval
local export_cmd=$(printf '%s\n' "$output" | grep -E '^export PATH=' | head -n 1)
if [[ -n "$export_cmd" && "$export_cmd" =~ ^export\ PATH=\"[^\"]*\"$ ]]; then
eval "$export_cmd"
echo "✓ Go version switched successfully"
return 0
fi
else
echo "$output" >&2
return $exit_code
fi
fi
"$govman_bin" "$@"
}
# Auto-switch Go versions based on .govman-goversion file
govman_auto_switch() {
# Check if auto-switch is enabled in config (improved YAML parsing)
local config_file="$HOME/.govman/config.yaml"
local auto_switch_enabled="true"
if [[ -f "$config_file" ]]; then
auto_switch_enabled=$(awk '/^auto_switch:/,/^[^ ]/ {if (/^[[:space:]]*enabled:/) {print $2; exit}}' "$config_file" 2>/dev/null | tr -d '[:space:]')
[[ -z "$auto_switch_enabled" ]] && auto_switch_enabled="true"
fi
if [[ "$auto_switch_enabled" != "true" ]]; then
return 0
fi
if [[ -f .govman-goversion ]]; then
local required_version=$(cat .govman-goversion 2>/dev/null | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [[ -n "$required_version" ]]; then
if ! command -v go > /dev/null 2>&1; then
echo "Go not found. Switching to Go $required_version..."
govman use "$required_version" > /dev/null 2>&1 || {
echo "Warning: Failed to switch to Go $required_version. Install it with 'govman install $required_version'" >&2
}
return
fi
# Improved version extraction with validation
local current_version=$(go version 2>/dev/null | awk '{print $3}' | sed -E 's/^go//; s/([0-9]+\.[0-9]+(\.[0-9]+)?).*/\1/')
if [[ ! "$current_version" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then current_version=""; fi
if [[ -n "$current_version" && "$current_version" != "$required_version" ]]; then
echo "Auto-switching to Go $required_version (required by .govman-goversion)"
govman use "$required_version" > /dev/null 2>&1 || {
echo "Warning: Failed to switch to Go $required_version. Install it with 'govman install $required_version'" >&2
}
fi
fi
fi
}
# Bash-specific: Hook into PROMPT_COMMAND for directory changes
__govman_prev_pwd="$PWD"
__govman_check_dir_change() {
if [[ "$PWD" != "$__govman_prev_pwd" ]]; then
__govman_prev_pwd="$PWD"
govman_auto_switch
fi
}
# Add to PROMPT_COMMAND (prevents duplicates on re-source)
if [[ ! "$PROMPT_COMMAND" =~ __govman_check_dir_change ]]; then
if [[ -z "$PROMPT_COMMAND" ]]; then
PROMPT_COMMAND="__govman_check_dir_change"
else
PROMPT_COMMAND="__govman_check_dir_change;$PROMPT_COMMAND"
fi
fi
# Run auto-switch on shell startup
govman_auto_switch
# END GOVMAN
Zsh
Zsh uses the chpwd hook instead of PROMPT_COMMAND:
# Zsh-specific: Hook into chpwd for directory changes (prevents duplicates)
autoload -U add-zsh-hook
if [[ ! "${chpwd_functions[(r)govman_auto_switch]}" ]]; then
add-zsh-hook chpwd govman_auto_switch
fi
Fish
# GOVMAN - Go Version Manager
fish_add_path -p "$HOME/.govman/bin"
set -gx GOTOOLCHAIN local
# Ensure GOBIN and GOPATH/bin are available
if test -n "$GOBIN"; and test -d "$GOBIN"; fish_add_path -p "$GOBIN"; end
if type -q go; set -l gopath (go env GOPATH 2>/dev/null); if test -n "$gopath"; and test -d "$gopath/bin"; fish_add_path -p "$gopath/bin"; end; end
set -l homegobin "$HOME/go/bin"; if test -d "$homegobin"; fish_add_path -p "$homegobin"; end
# Wrapper function for automatic PATH execution
function govman
set govman_bin "$HOME/.govman/bin/govman"
if test "$argv[1]" = "use"; and test (count $argv) -ge 2; and test "$argv[2]" != "--help"; and test "$argv[2]" != "-h"
set output ($govman_bin $argv 2>&1)
set exit_code $status
if test $exit_code -eq 0
for line in $output
if string match -qr '^fish_add_path' -- $line
eval $line
echo "✓ Go version switched successfully"
return 0
end
end
else
for line in $output
echo $line >&2
end
return $exit_code
end
end
$govman_bin $argv
end
# Auto-switch Go versions based on .govman- version file
function govman_auto_switch
# Implementation follows same logic as bash
end
# Fish event for directory changes
function __govman_cd_hook --on-variable PWD
govman_auto_switch
end
# Run auto-switch on shell startup
govman_auto_switch
# END GOVMAN
PowerShell
# GOVMAN - Go Version Manager
$env:PATH = "$env:USERPROFILE\.govman\bin;$env:PATH"
$env:GOTOOLCHAIN = "local"
# Wrapper function for automatic PATH execution
function govman {
$govmanBin = "$env:USERPROFILE\.govman\bin\govman.exe"
if ($args.Count -ge 2 -and $args[0] -eq "use" -and $args[1] -ne "--help" -and $args[1] -ne "-h") {
$output = & $govmanBin $args 2>&1 | Out-String
if ($LASTEXITCODE -eq 0) {
$exportCmd = $output -split "`n" | Where-Object { $_ -match '^\$env:PATH' }
if ($exportCmd) {
Invoke-Expression $exportCmd
Write-Host "✓ Go version switched successfully"
return
}
} else {
Write-Error $output
return
}
}
& $govmanBin $args
}
# Auto-switch function
function Invoke-GovmanAutoSwitch {
# Implementation follows same logic as bash
}
# Hook into Set-Location (cd)
$global:__GovmanPreviousLocation = Get-Location
function prompt {
$currentLocation = Get-Location
if ($currentLocation.Path -ne $global:__GovmanPreviousLocation.Path) {
$global:__GovmanPreviousLocation = $currentLocation
Invoke-GovmanAutoSwitch
}
# Call original prompt
}
# Run auto-switch on shell startup
Invoke-GovmanAutoSwitch
# END GOVMAN
Disabling Auto-Switch
Temporarily
# Edit config
nano ~/.govman/config.yaml
# Set enabled to false
auto_switch:
enabled: false
Remove Shell Integration
# Edit your shell config file
nano ~/.bashrc # or ~/.zshrc, etc.
# Remove the GOVMAN section (between # GOVMAN and # END GOVMAN markers)
Then restart your shell or run:
source ~/.bashrc # or appropriate config file
Troubleshooting
Auto-Switch Not Working
-
Check shell integration:
type govman_auto_switchShould show the function definition.
-
Verify config:
cat ~/.govman/config.yaml | grep -A 3 auto_switchEnsure
enabled: true. -
Check .govman-goversion file:
cat .govman-goversionShould contain only the version number (e.g.,
1.25.1). -
Test manually:
govman_auto_switch
PATH Not Updated
If govman use doesn't update PATH in current session:
-
Use the wrapper function: Ensure you're calling
govman(the shell function), not the binary directly. -
Check wrapper function:
type govmanShould show it's a function, not an alias or binary.
-
Reinitialize shell integration:
govman init --force source ~/.bashrc
Command Prompt (cmd.exe) Limitations
Windows Command Prompt has limited shell integration:
- No automatic version switching
- No wrapper function for PATH updates
- Manual
govman userequired in each session
Recommendation: Use PowerShell or Git Bash for full functionality.
Best Practices
-
Commit .govman-goversion to Git:
echo "1.25.1" > .govman-goversion git add .govman-goversion git commit -m "Set Go version to 1.25.1" -
Use .govman-goversion for Projects: Ensure consistent Go versions across team members and CI/CD.
-
Set System Default for Personal Projects:
govman use 1.25.1 --default -
Verify Integration After Updates: After updating shell config or govman, verify integration works:
cd /tmp echo "1.24.0" > .govman-goversion cd . # Trigger auto-switch go version