topgrade-rs / topgrade

Upgrade all the things
GNU General Public License v3.0
1.82k stars 121 forks source link

`npm.use_sudo = true` not working - Windows #863

Open aetonsi opened 1 month ago

aetonsi commented 1 month ago

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

Did you run topgrade through Remote Execution

If yes, does the issue still occur when you run topgrade directlly in your remote host

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

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
aetonsi commented 1 month ago

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
SteveLauC commented 1 month ago

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.

aetonsi commented 1 month ago

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.