`npm.use_sudo = true` doesn't work on Windows
Erroneous Behavior
Npm errors out about permissions (err -4048):
── 23:28:34 - Node Package Manager ─────────────────────────────────────────────
npm error code EPERM
npm error syscall unlink
npm error path C:\ProgramData\nvm\v22.3.0\node_modules\npm\LICENSE
npm error errno -4048
npm error [Error: EPERM: operation not permitted, unlink 'C:\ProgramData\nvm\v22.3.0\node_modules\npm\LICENSE'] {
npm error errno: -4048,
npm error code: 'EPERM',
npm error syscall: 'unlink',
npm error path: 'C:\\ProgramData\\nvm\\v22.3.0\\node_modules\\npm\\LICENSE'
npm error }
npm error
npm error The operation was rejected by your operating system.
npm error It's possible that the file was already in use (by a text editor or antivirus),
npm error or that you lack permissions to access it.
npm error
npm error If you believe this might be a permissions issue, please double-check the
npm error permissions of the file and its containing directories, or try running
npm error the command again as root/Administrator.
npm error A complete log of this run can be found in: C:\Users\u6\AppData\Local\npm-cache\_logs\2024-07-12T21_28_34_689Z-debug-0.log
npm failed:
0: Command failed: `C:\Program Files\nodejs\npm.cmd update '--location=global'`
1: `C:\Program Files\nodejs\npm.cmd` failed: exit code: 0xfffff030
Location:
src\steps\node.rs:102
Retry? (y)es/(N)o/(s)hell/(q)uit
Expected Behavior
topgrade should use (g)sudo to elevate the operation automatically
Steps to reproduce / Possible Cause (Optional)
simply, i think i had installed things while running as administrator, while now i'm trying to use topgrade NOT as admin
Problem persists without calling from topgrade
- [x] Yes
- [ ] No
Did you run topgrade through Remote Execution
- [ ] Yes
- [x] No
If yes, does the issue still occur when you run topgrade directlly in your remote host
- [ ] Yes
- [ ] No
Configuration file (Optional)
# Include any additional configuration file(s)
# [include] sections are processed in the order you write them
# Files in $CONFIG_DIR/topgrade.d/ are automatically included before this file
[include]
# paths = ["/etc/topgrade.toml"]
[misc]
# Run `sudo -v` to cache credentials at the start of the run
# This avoids a blocking password prompt in the middle of an unattended run
# (default: false)
# pre_sudo = false
# Sudo command to be used
# sudo_command = "sudo"
# Disable specific steps - same options as the command line flag
# disable = ["system", "emacs"]
disable = ["pnpm", "system"]
# Ignore failures for these steps
# ignore_failures = ["powershell"]
# List of remote machines with Topgrade installed on them
# remote_topgrades = ["toothless", "pi", "parnas"]
# Path to Topgrade executable on remote machines
# remote_topgrade_path = ".cargo/bin/topgrade"
# Arguments to pass to SSH when upgrading remote systems
# ssh_arguments = "-o ConnectTimeout=2"
# Arguments to pass tmux when pulling Repositories
# tmux_arguments = "-S /var/tmux.sock"
# Do not set the terminal title (default: true)
# set_title = true
# Display the time in step titles (default: true)
display_time = true
# Don't ask for confirmations (no default value)
assume_yes = true
# Do not ask to retry failed steps (default: false)
# no_retry = true
# Run inside tmux (default: false)
run_in_tmux = true
# Cleanup temporary or old files (default: false)
cleanup = true
# Send a notification for every step (default: false)
notify_each_step = true
# Skip sending a notification at the end of a run (default: false)
# skip_notify = true
# The Bash-it branch to update (default: "stable")
# bashit_branch = "stable"
# Run specific steps - same options as the command line flag
# only = ["system", "emacs"]
# Whether to self update
#
# this will be ignored if the binary is built without self update support
#
# available also via setting the environment variable TOPGRADE_NO_SELF_UPGRADE)
# no_self_update = true
# Extra tracing filter directives
# These are prepended to the `--log-filter` argument
# See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
# log_filters = ["topgrade::command=debug", "warn"]
# Commands to run before anything
[pre_commands]
# "Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak"
# Commands to run after anything
[post_commands]
# "Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak"
# Custom commands
[commands]
# "Python Environment" = "~/dev/.env/bin/pip install -i https://pypi.python.org/simple -U --upgrade-strategy eager jupyter"
# "Custom command using interactive shell (unix)" = "-i vim_upgrade"
[python]
enable_pip_review = true ###disabled by default
enable_pip_review_local = true ###disabled by default
enable_pipupgrade = true ###disabled by default
# pipupgrade_arguments = "-y -u --pip-path pip" ###disabled by default
[composer]
# self_update = true
[brew]
# greedy_cask = true
# autoremove = true
[linux]
# Arch Package Manager to use.
# Allowed values:
# autodetect, aura, garuda_update, pacman, pamac, paru, pikaur, trizen, yay
# arch_package_manager = "pacman"
# Arguments to pass yay (or paru) when updating packages
# yay_arguments = "--nodevel"
# Arguments to pass dnf when updating packages
# dnf_arguments = "--refresh"
# aura_aur_arguments = "-kx"
# aura_pacman_arguments = ""
# garuda_update_arguments = ""
# show_arch_news = true
# trizen_arguments = "--devel"
# pikaur_arguments = ""
# pamac_arguments = "--no-devel"
# enable_tlmgr = true
# emerge_sync_flags = "-q"
# emerge_update_flags = "-uDNa --with-bdeps=y world"
# redhat_distro_sync = false
# suse_dup = false
# rpm_ostree = false
# nix_arguments = "--flake"
# nix_env_arguments = "--prebuilt-only"
# Extra Home Manager arguments
# home_manager_arguments = ["--flake", "file"]
[git]
# How many repos to pull at max in parallel
# max_concurrency = 5
# Additional git repositories to pull
# repos = [
# "~/src/*/",
# "~/.config/something"
# ]
# Don't pull the predefined git repos
# pull_predefined = false
# Arguments to pass Git when pulling Repositories
# arguments = "--rebase --autostash"
[windows]
# Manually select Windows updates
accept_all_updates = true
open_remotes_in_new_terminal = true
wsl_update_pre_release = true
wsl_update_use_web_download = true
# Causes Topgrade to rename itself during the run to allow package managers
# to upgrade it. Use this only if you installed Topgrade by using a package
# manager such as Scoop or Cargo
# self_rename = true
[npm]
# Use sudo if the NPM directory isn't owned by the current user
use_sudo = true
[yarn]
# Run `yarn global upgrade` with `sudo`
# use_sudo = true
[vim]
# For `vim-plug`, execute `PlugUpdate!` instead of `PlugUpdate`
# force_plug_update = true
[firmware]
# Offer to update firmware; if false just check for and display available updates
upgrade = true
[vagrant]
# Vagrant directories
# directories = []
# power on vagrant boxes if needed
# power_on = true
# Always suspend vagrant boxes instead of powering off
# always_suspend = true
[flatpak]
# Use sudo for updating the system-wide installation
# use_sudo = true
[distrobox]
# use_root = false
# containers = ["archlinux-latest"]
[containers]
# ignored_containers = ["ghcr.io/rancher-sandbox/rancher-desktop/rdx-proxy:latest"]
Additional Details
- Operation System/Version: win 11 pro N (
Microsoft Windows [Version 10.0.22635.3066]) - Installation: scoop
- Topgrade version (
topgrade -V):15.0.0
Verbose Output (topgrade -v)
command: topgrade -v --only=node
DEBUG Configuration at C:\Users\u6\AppData\Roaming\topgrade.toml
DEBUG Loaded configuration: ConfigFile { include: Some(Include { paths: None }), misc: Some(Misc { pre_sudo: None, sudo_command: None, disable: Some([Pnpm, System]), ignore_failures: None, remote_topgrades: None, remote_topgrade_path: None, ssh_arguments: None, tmux_arguments: None, set_title: None, display_time: Some(true), assume_yes: Some(true), no_retry: None, run_in_tmux: Some(true), cleanup: Some(true), notify_each_step: Some(true), skip_notify: None, bashit_branch: None, only: None, no_self_update: None, log_filters: None }), pre_commands: Some({}), post_commands: Some({}), commands: Some({}), python: Some(Python { enable_pip_review: Some(true), enable_pip_review_local: Some(true), enable_pipupgrade: Some(true), pipupgrade_arguments: None }), composer: Some(Composer { self_update: None }), brew: Some(Brew { greedy_cask: None, greedy_latest: None, autoremove: None, fetch_head: None }), linux: Some(Linux { yay_arguments: None, aura_aur_arguments: None, aura_pacman_arguments: None, arch_package_manager: None, show_arch_news: None, garuda_update_arguments: None, trizen_arguments: None, pikaur_arguments: None, pamac_arguments: None, dnf_arguments: None, nix_arguments: None, nix_env_arguments: None, apt_arguments: None, enable_tlmgr: None, redhat_distro_sync: None, suse_dup: None, rpm_ostree: None, emerge_sync_flags: None, emerge_update_flags: None, home_manager_arguments: None }), git: Some(Git { max_concurrency: None, arguments: None, repos: None, pull_predefined: None }), containers: Some(Containers { ignored_containers: None }), windows: Some(Windows { accept_all_updates: Some(true), self_rename: None, open_remotes_in_new_terminal: Some(true), wsl_update_pre_release: Some(true), wsl_update_use_web_download: Some(true) }), npm: Some(NPM { use_sudo: Some(true) }), yarn: Some(Yarn { use_sudo: None }), vim: Some(Vim { force_plug_update: None }), firmware: Some(Firmware { upgrade: Some(true) }), vagrant: Some(Vagrant { directories: None, power_on: None, always_suspend: None }), flatpak: Some(Flatpak { use_sudo: None }), distrobox: Some(Distrobox { use_root: None, containers: None }), lensfun: None }
DEBUG Version: 15.0.0
DEBUG OS: x86_64-pc-windows-msvc
DEBUG Args { inner: ["topgrade", "-v", "--only=node"] }
DEBUG Binary path: Ok("C:\\Users\\u6\\AppData\\Local\\Microsoft\\WinGet\\Links\\topgrade.exe")
DEBUG self-update Feature Enabled: true
DEBUG Configuration: Config { opt: CommandLineArgs { edit_config: false, show_config_reference: false, run_in_tmux: false, cleanup: false, dry_run: false, no_retry: false, disable: [], only: [Node], custom_commands: [], env: [], verbose: true, keep_at_end: false, skip_notify: false, yes: None, disable_predefined_git_repos: false, config: None, remote_host_limit: None, show_skipped: false, log_filter: "warn", gen_completion: None, gen_manpage: false, no_self_update: false }, config_file: ConfigFile { include: Some(Include { paths: None }), misc: Some(Misc { pre_sudo: None, sudo_command: None, disable: Some([Pnpm, System]), ignore_failures: None, remote_topgrades: None, remote_topgrade_path: None, ssh_arguments: None, tmux_arguments: None, set_title: None, display_time: Some(true), assume_yes: Some(true), no_retry: None, run_in_tmux: Some(true), cleanup: Some(true), notify_each_step: Some(true), skip_notify: None, bashit_branch: None, only: None, no_self_update: None, log_filters: None }), pre_commands: Some({}), post_commands: Some({}), commands: Some({}), python: Some(Python { enable_pip_review: Some(true), enable_pip_review_local: Some(true), enable_pipupgrade: Some(true), pipupgrade_arguments: None }), composer: Some(Composer { self_update: None }), brew: Some(Brew { greedy_cask: None, greedy_latest: None, autoremove: None, fetch_head: None }), linux: Some(Linux { yay_arguments: None, aura_aur_arguments: None, aura_pacman_arguments: None, arch_package_manager: None, show_arch_news: None, garuda_update_arguments: None, trizen_arguments: None, pikaur_arguments: None, pamac_arguments: None, dnf_arguments: None, nix_arguments: None, nix_env_arguments: None, apt_arguments: None, enable_tlmgr: None, redhat_distro_sync: None, suse_dup: None, rpm_ostree: None, emerge_sync_flags: None, emerge_update_flags: None, home_manager_arguments: None }), git: Some(Git { max_concurrency: None, arguments: None, repos: None, pull_predefined: None }), containers: Some(Containers { ignored_containers: None }), windows: Some(Windows { accept_all_updates: Some(true), self_rename: None, open_remotes_in_new_terminal: Some(true), wsl_update_pre_release: Some(true), wsl_update_use_web_download: Some(true) }), npm: Some(NPM { use_sudo: Some(true) }), yarn: Some(Yarn { use_sudo: None }), vim: Some(Vim { force_plug_update: None }), firmware: Some(Firmware { upgrade: Some(true) }), vagrant: Some(Vagrant { directories: None, power_on: None, always_suspend: None }), flatpak: Some(Flatpak { use_sudo: None }), distrobox: Some(Distrobox { use_root: None, containers: None }), lensfun: None }, allowed_steps: [Node] }
DEBUG Detected "C:\\Program Files\\PowerShell\\7\\pwsh.exe" as "pwsh"
DEBUG Executing command `C:\Program Files\PowerShell\7\pwsh.exe -NoProfile -Command 'Split-Path $profile'`
DEBUG Path "C:\\Users\\u6\\OneDrive\\Documents\\PowerShell" exists
DEBUG Path "C:\\Users\\u6\\AppData\\Roaming\\.emacs.d" doesn't exist
DEBUG Cannot find "doas"
DEBUG Detected "C:\\tools\\gsudo\\Current\\sudo.exe" as "sudo"
DEBUG Step "npm"
DEBUG Detected "C:\\Program Files\\nodejs\\npm.cmd" as "npm"
DEBUG Desktop notification: Node Package Manager
── 23:38:44 - Node Package Manager ─────────────────────────────────────────────
DEBUG Executing command C:\Program Files\nodejs\npm.cmd --version
DEBUG Executing command C:\Program Files\nodejs\npm.cmd update '--location=global'
npm error code EPERM
npm error syscall unlink
npm error path C:\ProgramData\nvm\v22.3.0\node_modules\npm\LICENSE
npm error errno -4048
npm error [Error: EPERM: operation not permitted, unlink 'C:\ProgramData\nvm\v22.3.0\node_modules\npm\LICENSE'] {
npm error errno: -4048,
npm error code: 'EPERM',
npm error syscall: 'unlink',
npm error path: 'C:\ProgramData\nvm\v22.3.0\node_modules\npm\LICENSE'
npm error }
npm error
npm error The operation was rejected by your operating system.
npm error It's possible that the file was already in use (by a text editor or antivirus),
npm error or that you lack permissions to access it.
npm error
npm error If you believe this might be a permissions issue, please double-check the
npm error permissions of the file and its containing directories, or try running
npm error the command again as root/Administrator.
npm error A complete log of this run can be found in: C:\Users\u6\AppData\Local\npm-cache_logs\2024-07-12T21_38_45_121Z-debug-0.log
DEBUG Command failed: Err(
0: Command failed: C:\Program Files\nodejs\npm.cmd update '--location=global'
1: C:\Program Files\nodejs\npm.cmd failed: exit code: 0xfffff030
Location:
src\steps\node.rs:102)
DEBUG Step "npm" failed:
0: Command failed: C:\Program Files\nodejs\npm.cmd update '--location=global'
1: C:\Program Files\nodejs\npm.cmd failed: exit code: 0xfffff030
Location:
src\steps\node.rs:102
npm failed:
0: Command failed: C:\Program Files\nodejs\npm.cmd update '--location=global'
1: C:\Program Files\nodejs\npm.cmd failed: exit code: 0xfffff030
Location:
src\steps\node.rs:102DEBUG Desktop notification: npm failed
Retry? (y)es/(N)o/(s)hell/(q)uit
can confirm it works when running as admin:
# topgrade -v --only=node
── 23:44:02 - Node Package Manager ─────────────────────────────────────────────
DEBUG Executing command `C:\Program Files\nodejs\npm.cmd --version`
DEBUG Executing command `C:\Program Files\nodejs\npm.cmd update '--location=global'`
changed 105 packages in 14s
DEBUG Desktop notification: Summary
── 23:44:16 - Summary ──────────────────────────────────────────────────────────
npm: OK
DEBUG Desktop notification: Topgrade finished successfully
This configuration is currently Linux-only, on Windows, the parameter is simply set to false:
https://github.com/topgrade-rs/topgrade/blob/07118fa0d25aba573c36a874375caea8c768cd33/src/steps/node.rs#L214-L228
The reason is probably that we don't know how to do this on Windows. This configuration works in this way:
If you have sudo installed, it checks if the current Topgrade process is capable of modifying the NPM file, if not, sudo will be used. On an UNIX OS, Topgrade checks the privilege by checking if the effective user ID of the Topgrade process equals to the user ID of the owner of the NPM file, both EUID and file owner are concepts of UNIX, we need the counterparts on Windows to implement this configuration.
oh i see.. if it can be useful, fyi here's how on windows you can both check if a user is admin and elevate if necessary.
-
check if user is admin (uses: powershell)
https://serverfault.com/a/95464
(New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
-
elevate without opening additional window (uses: gsudo)
https://github.com/gerardog/gsudo#usage-from-powershell--powershell-core
gsudo some-command /with /parameters --and --whatnot
gsudo can also "cache" the administrative privilege if needed: https://github.com/gerardog/gsudo?tab=readme-ov-file#credentials-cache
Should be a simple fix now that Windows sudo is here, and integrated into Topgrade with #1208