Skip to content

Instantly share code, notes, and snippets.

@rmkane
Created March 28, 2026 02:55
Show Gist options
  • Select an option

  • Save rmkane/5454d0f00e943985ddb6f279f6dff8b0 to your computer and use it in GitHub Desktop.

Select an option

Save rmkane/5454d0f00e943985ddb6f279f6dff8b0 to your computer and use it in GitHub Desktop.
JDK Manager

JDK Manager

The jdk-manager.sh script automates managing JDK versions on macOS using Homebrew and jenv. It can install or remove JDK 17, 21, and 25 (or a subset you specify), link them so /usr/libexec/java_home can detect them, and register them with jenv for version switching. It also cleans up stale jenv entries and old Homebrew versions, ensuring only the latest patch of each JDK is kept.

#!/usr/bin/env bash
set -euo pipefail
BREW_PREFIX="${BREW_PREFIX:-/opt/homebrew}"
DEFAULT_VERSIONS=(17 21 25)
usage() {
cat <<EOF
Usage:
$(basename "$0") install [versions...]
$(basename "$0") remove [versions...]
Examples:
$(basename "$0") install
$(basename "$0") install 17 21
$(basename "$0") remove
$(basename "$0") remove 25
If no versions are provided, defaults to: ${DEFAULT_VERSIONS[*]}
Behavior:
install
- installs the requested JDKs with Homebrew
- links them into /Library/Java/JavaVirtualMachines for java_home
- removes stale jenv entries for those major versions
- adds the current JDK homes to jenv
- runs brew cleanup for the requested formulas
remove
- removes matching jenv entries for those major versions
- removes the java_home symlinks
- uninstalls the requested Homebrew formulas
- runs brew cleanup for the requested formulas
EOF
exit 1
}
log() {
printf '\n==> %s\n' "$*"
}
die() {
echo "Error: $*" >&2
exit 1
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}
brew_formula() {
case "$1" in
17|21) echo "openjdk@$1" ;;
25) echo "openjdk" ;;
*) die "unsupported JDK version: $1 (supported: 17, 21, 25)" ;;
esac
}
jdk_opt_path() {
echo "$BREW_PREFIX/opt/$(brew_formula "$1")"
}
jdk_bundle() {
echo "$(jdk_opt_path "$1")/libexec/openjdk.jdk"
}
jdk_home() {
echo "$(jdk_bundle "$1")/Contents/Home"
}
jdk_link() {
echo "/Library/Java/JavaVirtualMachines/openjdk-$1.jdk"
}
ensure_jenv() {
if ! command -v jenv >/dev/null 2>&1; then
log "Installing jenv"
brew install jenv
fi
export PATH="$HOME/.jenv/bin:$PATH"
command -v jenv >/dev/null 2>&1 || die "jenv is not available on PATH"
# Do not run: eval "$(jenv init -)"
# The script only needs the jenv executable, not the interactive shell hooks.
}
set_versions() {
if [[ $# -eq 0 ]]; then
log "No JDK versions provided. Defaulting to: ${DEFAULT_VERSIONS[*]}"
VERSIONS=("${DEFAULT_VERSIONS[@]}")
else
VERSIONS=("$@")
fi
}
validate_versions() {
local v
for v in "$@"; do
case "$v" in
17|21|25) ;;
*) die "unsupported JDK version: $v (supported: 17, 21, 25)" ;;
esac
done
}
cleanup_jenv_version() {
local version="$1"
local entries=()
while IFS= read -r entry; do
[[ -n "$entry" ]] && entries+=("$entry")
done < <(jenv versions --bare 2>/dev/null | grep -E "^(openjdk64-${version}\.|${version}(\.|$))" || true)
[[ ${#entries[@]} -eq 0 ]] && return 0
log "Removing stale jenv entries for JDK $version"
local entry
for entry in "${entries[@]}"; do
jenv remove -f "$entry" >/dev/null 2>&1 || jenv remove "$entry" >/dev/null 2>&1 || true
done
}
brew_cleanup_versions() {
local formulas=()
local v
for v in "$@"; do
formulas+=("$(brew_formula "$v")")
done
log "Running brew cleanup"
brew cleanup "${formulas[@]}" || true
}
install_versions() {
local versions=("$@")
local v formula bundle home link
ensure_jenv
for v in "${versions[@]}"; do
formula="$(brew_formula "$v")"
log "Installing $formula"
brew install "$formula"
bundle="$(jdk_bundle "$v")"
home="$(jdk_home "$v")"
link="$(jdk_link "$v")"
[[ -d "$bundle" ]] || die "missing JDK bundle: $bundle"
[[ -d "$home" ]] || die "missing JDK home: $home"
log "Linking JDK $v for java_home"
sudo mkdir -p /Library/Java/JavaVirtualMachines
sudo ln -sfn "$bundle" "$link"
cleanup_jenv_version "$v"
log "Adding JDK $v to jenv"
jenv add "$home"
done
log "Enabling jenv export plugin"
jenv enable-plugin export || true
jenv rehash
brew_cleanup_versions "${versions[@]}"
log "Done. Verify:"
/usr/libexec/java_home -V || true
jenv versions
}
remove_versions() {
local versions=("$@")
local v formula link
ensure_jenv
for v in "${versions[@]}"; do
formula="$(brew_formula "$v")"
link="$(jdk_link "$v")"
cleanup_jenv_version "$v"
log "Removing java_home link for JDK $v"
sudo rm -f "$link"
if brew list --versions "$formula" >/dev/null 2>&1; then
log "Uninstalling $formula"
brew uninstall "$formula" || true
else
log "$formula is not installed"
fi
done
jenv rehash
brew_cleanup_versions "${versions[@]}"
log "Done. Remaining:"
/usr/libexec/java_home -V || true
jenv versions
}
main() {
require_cmd brew
require_cmd sudo
[[ $# -eq 0 ]] && usage
local cmd="$1"
shift
VERSIONS=()
set_versions "$@"
validate_versions "${VERSIONS[@]}"
case "$cmd" in
install) install_versions "${VERSIONS[@]}" ;;
remove) remove_versions "${VERSIONS[@]}" ;;
*) usage ;;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment