diff --git a/build.sh b/build.sh index cae65fa..8475350 100755 --- a/build.sh +++ b/build.sh @@ -39,6 +39,8 @@ OUT="devboost.sh" echo "" cat modules/module_mise.sh echo "" + cat modules/module_corepack.sh + echo "" cat modules/module_direnv.sh echo "" cat modules/module_git.sh @@ -59,6 +61,7 @@ db_load_modules() { db_module_starship_register db_module_tmux_register db_module_mise_register + db_module_corepack_register db_module_direnv_register db_module_git_register db_module_services_register diff --git a/core/core_files.sh b/core/core_files.sh index 1aead3f..f5e10d7 100644 --- a/core/core_files.sh +++ b/core/core_files.sh @@ -100,6 +100,22 @@ db_upsert_block() { fi } +db_confirm() { + local prompt="$1" + local default="${2:-y}" + local hint + if [[ "$default" == "y" ]]; then + hint="[Y/n]" + else + hint="[y/N]" + fi + local reply + read -r -p "$(echo -e "${YELLOW}?${NC} ${prompt} ${hint} ")" reply + reply="${reply:-$default}" + reply=$(echo "$reply" | tr '[:upper:]' '[:lower:]') + [[ "$reply" == "y" ]] +} + db_remove_block() { local file="$1" local start_marker="$2" diff --git a/devboost.sh b/devboost.sh index efa81b4..d1eaa53 100755 --- a/devboost.sh +++ b/devboost.sh @@ -328,6 +328,22 @@ db_upsert_block() { fi } +db_confirm() { + local prompt="$1" + local default="${2:-y}" + local hint + if [[ "$default" == "y" ]]; then + hint="[Y/n]" + else + hint="[y/N]" + fi + local reply + read -r -p "$(echo -e "${YELLOW}?${NC} ${prompt} ${hint} ")" reply + reply="${reply:-$default}" + reply=$(echo "$reply" | tr '[:upper:]' '[:lower:]') + [[ "$reply" == "y" ]] +} + db_remove_block() { local file="$1" local start_marker="$2" @@ -1180,39 +1196,121 @@ db_module_mise_plan() { if [[ "$enable" != "true" ]]; then return 0 fi - + db_log_info "Would configure mise toolchains" } +# Returns a space-separated list of globally installed npm packages (excluding npm itself). +_db_mise_npm_globals() { + local node_bin + node_bin=$(command -v node 2>/dev/null) || return 0 + local npm_bin + npm_bin=$(dirname "$node_bin")/npm + [[ -x "$npm_bin" ]] || return 0 + "$npm_bin" list -g --depth=0 --parseable 2>/dev/null \ + | awk -F/ 'NF>1 && $NF!="npm" && $NF!="corepack" {print $NF}' +} + +# Returns the currently active mise-managed node version, or empty string. +_db_mise_current_node_version() { + mise current node 2>/dev/null | tr -d '[:space:]' || true +} + db_module_mise_apply() { local enable=$(db_yaml_get '.toolchains.enable_mise' 'true') if [[ "$enable" != "true" ]]; then return 0 fi - + if ! db_command_exists mise; then db_log_warn "mise not found, skipping toolchain setup" return 0 fi - + db_log_info "Configuring mise toolchains..." - + local node=$(db_yaml_get '.toolchains.globals.node' 'lts') local python=$(db_yaml_get '.toolchains.globals.python' '3.12') local go=$(db_yaml_get '.toolchains.globals.go' '1.23') local rust=$(db_yaml_get '.toolchains.globals.rust' 'stable') local deno=$(db_yaml_get '.toolchains.globals.deno' 'latest') - + if [[ "${DB_DRY_RUN:-false}" == "true" ]]; then db_log_info "Would run: mise use -g node@${node} python@${python} go@${go} rust@${rust} deno@${deno}" db_log_info "Would run: mise install" - else - mise use -g "node@${node}" "python@${python}" "go@${go}" "rust@${rust}" "deno@${deno}" 2>/dev/null || true - mise install 2>/dev/null || db_log_warn "Some toolchains may not be available" - db_log_success "Configured mise toolchains" + return 0 fi + + # Capture current node version and its global npm packages before any upgrade. + local prev_node_version + prev_node_version=$(_db_mise_current_node_version) + local npm_globals=() + if [[ -n "$prev_node_version" ]] && db_command_exists node; then + while IFS= read -r pkg; do + [[ -n "$pkg" ]] && npm_globals+=("$pkg") + done < <(_db_mise_npm_globals) + fi + + mise use -g "node@${node}" "python@${python}" "go@${go}" "rust@${rust}" "deno@${deno}" 2>/dev/null || true + mise install 2>/dev/null || db_log_warn "Some toolchains may not be available" + db_log_success "Configured mise toolchains" + + # After upgrade, offer to migrate npm globals to the new node version. + if [[ ${#npm_globals[@]} -gt 0 ]]; then + local new_node_version + new_node_version=$(_db_mise_current_node_version) + if [[ "$prev_node_version" != "$new_node_version" ]]; then + db_log_warn "Node upgraded: ${prev_node_version} → ${new_node_version}" + db_log_warn "The following global npm packages were present in the old version:" + for pkg in "${npm_globals[@]}"; do + db_log_warn " - $pkg" + done + db_log_warn "Note: any version pins or custom configuration for these packages will NOT be migrated." + if db_confirm "Reinstall these packages into node@${new_node_version}?"; then + local npm_bin + npm_bin=$(dirname "$(command -v node)")/npm + for pkg in "${npm_globals[@]}"; do + db_log_info "Installing $pkg..." + "$npm_bin" install -g "$pkg" 2>/dev/null \ + && db_log_success " ✓ $pkg" \ + || db_log_warn " ✗ $pkg (failed — install manually if needed)" + done + fi + fi + fi +} + +# Corepack module — enables pnpm/yarn shims bundled with Node.js + +db_module_corepack_register() { + db_register_module "corepack" \ + "db_module_corepack_plan" \ + "db_module_corepack_apply" +} + +db_module_corepack_plan() { + local enable=$(db_yaml_get '.toolchains.enable_mise' 'true') + [[ "$enable" == "true" ]] || return 0 + db_log_info "Would run: corepack enable" } +db_module_corepack_apply() { + local enable=$(db_yaml_get '.toolchains.enable_mise' 'true') + [[ "$enable" == "true" ]] || return 0 + + if ! db_command_exists corepack; then + db_log_warn "corepack not found (expected inside Node.js install), skipping" + return 0 + fi + + if [[ "${DB_DRY_RUN:-false}" == "true" ]]; then + db_log_info "Would run: corepack enable" + return 0 + fi + + corepack enable 2>/dev/null && db_log_success "corepack: pnpm/yarn shims enabled" \ + || db_log_warn "corepack enable failed — pnpm/yarn shims may be missing" +} # Direnv module @@ -1504,6 +1602,7 @@ db_load_modules() { db_module_starship_register db_module_tmux_register db_module_mise_register + db_module_corepack_register db_module_direnv_register db_module_git_register db_module_services_register diff --git a/modules/module_corepack.sh b/modules/module_corepack.sh new file mode 100644 index 0000000..ec30485 --- /dev/null +++ b/modules/module_corepack.sh @@ -0,0 +1,31 @@ +# Corepack module — enables pnpm/yarn shims bundled with Node.js + +db_module_corepack_register() { + db_register_module "corepack" \ + "db_module_corepack_plan" \ + "db_module_corepack_apply" +} + +db_module_corepack_plan() { + local enable=$(db_yaml_get '.toolchains.enable_mise' 'true') + [[ "$enable" == "true" ]] || return 0 + db_log_info "Would run: corepack enable" +} + +db_module_corepack_apply() { + local enable=$(db_yaml_get '.toolchains.enable_mise' 'true') + [[ "$enable" == "true" ]] || return 0 + + if ! db_command_exists corepack; then + db_log_warn "corepack not found (expected inside Node.js install), skipping" + return 0 + fi + + if [[ "${DB_DRY_RUN:-false}" == "true" ]]; then + db_log_info "Would run: corepack enable" + return 0 + fi + + corepack enable 2>/dev/null && db_log_success "corepack: pnpm/yarn shims enabled" \ + || db_log_warn "corepack enable failed — pnpm/yarn shims may be missing" +} diff --git a/modules/module_mise.sh b/modules/module_mise.sh index 673ed00..8c01cf7 100644 --- a/modules/module_mise.sh +++ b/modules/module_mise.sh @@ -11,36 +11,86 @@ db_module_mise_plan() { if [[ "$enable" != "true" ]]; then return 0 fi - + db_log_info "Would configure mise toolchains" } +# Returns a space-separated list of globally installed npm packages (excluding npm itself). +_db_mise_npm_globals() { + local node_bin + node_bin=$(command -v node 2>/dev/null) || return 0 + local npm_bin + npm_bin=$(dirname "$node_bin")/npm + [[ -x "$npm_bin" ]] || return 0 + "$npm_bin" list -g --depth=0 --parseable 2>/dev/null \ + | awk -F/ 'NF>1 && $NF!="npm" && $NF!="corepack" {print $NF}' +} + +# Returns the currently active mise-managed node version, or empty string. +_db_mise_current_node_version() { + mise current node 2>/dev/null | tr -d '[:space:]' || true +} + db_module_mise_apply() { local enable=$(db_yaml_get '.toolchains.enable_mise' 'true') if [[ "$enable" != "true" ]]; then return 0 fi - + if ! db_command_exists mise; then db_log_warn "mise not found, skipping toolchain setup" return 0 fi - + db_log_info "Configuring mise toolchains..." - + local node=$(db_yaml_get '.toolchains.globals.node' 'lts') local python=$(db_yaml_get '.toolchains.globals.python' '3.12') local go=$(db_yaml_get '.toolchains.globals.go' '1.23') local rust=$(db_yaml_get '.toolchains.globals.rust' 'stable') local deno=$(db_yaml_get '.toolchains.globals.deno' 'latest') - + if [[ "${DB_DRY_RUN:-false}" == "true" ]]; then db_log_info "Would run: mise use -g node@${node} python@${python} go@${go} rust@${rust} deno@${deno}" db_log_info "Would run: mise install" - else - mise use -g "node@${node}" "python@${python}" "go@${go}" "rust@${rust}" "deno@${deno}" 2>/dev/null || true - mise install 2>/dev/null || db_log_warn "Some toolchains may not be available" - db_log_success "Configured mise toolchains" + return 0 fi -} + # Capture current node version and its global npm packages before any upgrade. + local prev_node_version + prev_node_version=$(_db_mise_current_node_version) + local npm_globals=() + if [[ -n "$prev_node_version" ]] && db_command_exists node; then + while IFS= read -r pkg; do + [[ -n "$pkg" ]] && npm_globals+=("$pkg") + done < <(_db_mise_npm_globals) + fi + + mise use -g "node@${node}" "python@${python}" "go@${go}" "rust@${rust}" "deno@${deno}" 2>/dev/null || true + mise install 2>/dev/null || db_log_warn "Some toolchains may not be available" + db_log_success "Configured mise toolchains" + + # After upgrade, offer to migrate npm globals to the new node version. + if [[ ${#npm_globals[@]} -gt 0 ]]; then + local new_node_version + new_node_version=$(_db_mise_current_node_version) + if [[ "$prev_node_version" != "$new_node_version" ]]; then + db_log_warn "Node upgraded: ${prev_node_version} → ${new_node_version}" + db_log_warn "The following global npm packages were present in the old version:" + for pkg in "${npm_globals[@]}"; do + db_log_warn " - $pkg" + done + db_log_warn "Note: any version pins or custom configuration for these packages will NOT be migrated." + if db_confirm "Reinstall these packages into node@${new_node_version}?"; then + local npm_bin + npm_bin=$(dirname "$(command -v node)")/npm + for pkg in "${npm_globals[@]}"; do + db_log_info "Installing $pkg..." + "$npm_bin" install -g "$pkg" 2>/dev/null \ + && db_log_success " ✓ $pkg" \ + || db_log_warn " ✗ $pkg (failed — install manually if needed)" + done + fi + fi + fi +}