Browse Source

fix shell inference in powershell when installed through scoop (#1164)

* be more verbose on shell inference issues in Windows

* add changeset

* fix shell inference with scoop

* change changeset text

* test scoop shims

* don't concat paths

* skip instead of not emitting tests

* setup scoop in windows

* use test.skip
remotes/origin/renovate/reqwest-0.x-lockfile
Gal Schlezinger 12 months ago committed by GitHub
parent
commit
318f86d729
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/warm-garlics-move.md
  2. 1
      .github/workflows/rust.yml
  3. 11
      e2e/shellcode/shells/cmdEnv.ts
  4. 33
      e2e/windows-scoop.test.ts
  5. 33
      src/shell/infer/windows.rs

5
.changeset/warm-garlics-move.md

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
---
"fnm": patch
---
windows: fix shell inference in powershell when using scoop's shims

1
.github/workflows/rust.yml

@ -138,6 +138,7 @@ jobs: @@ -138,6 +138,7 @@ jobs:
with:
name: fnm-windows
path: target/release
- uses: MinoruSekine/setup-scoop@v4
- uses: pnpm/action-setup@v2.4.0
with:
run_install: false

11
e2e/shellcode/shells/cmdEnv.ts

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
import { ScriptLine, define } from "./types.js"
type EnvConfig = {
executableName: string
useOnCd: boolean
logLevel: string
corepackEnabled: boolean
@ -9,9 +10,15 @@ type EnvConfig = { @@ -9,9 +10,15 @@ type EnvConfig = {
export type HasEnv = { env(cfg: Partial<EnvConfig>): ScriptLine }
function stringify(envConfig: Partial<EnvConfig> = {}) {
const { useOnCd, logLevel, corepackEnabled, resolveEngines } = envConfig
const {
useOnCd,
logLevel,
corepackEnabled,
resolveEngines,
executableName = "fnm",
} = envConfig
return [
`fnm env`,
`${executableName} env`,
useOnCd && "--use-on-cd",
logLevel && `--log-level=${logLevel}`,
corepackEnabled && "--corepack-enabled",

33
e2e/windows-scoop.test.ts

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells.js"
import testNodeVersion from "./shellcode/test-node-version.js"
import describe from "./describe.js"
import os from "node:os"
import { execa } from "execa"
if (os.platform() !== "win32") {
test.skip("scoop shims only work on Windows", () => {})
} else {
beforeAll(async () => {
// Create a scoop shim for tests
await execa(`scoop`, [
"shim",
"add",
"fnm_release",
"target/release/fnm.exe",
])
})
for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) {
describe(shell, () => {
test(`scoop shims infer the shell`, async () => {
await script(shell)
.then(shell.env({ executableName: "fnm_release" }))
.then(shell.call("fnm_release", ["install", "v20.14.0"]))
.then(shell.call("fnm_release", ["use", "v20.14.0"]))
.then(testNodeVersion(shell, "v20.14.0"))
.execute(shell)
})
})
}
}

33
src/shell/infer/windows.rs

@ -1,20 +1,32 @@ @@ -1,20 +1,32 @@
#![cfg(not(unix))]
use crate::shell::Shell;
use sysinfo::System;
use log::{debug, warn};
use sysinfo::{ProcessRefreshKind, System, UpdateKind};
pub fn infer_shell() -> Option<Box<dyn Shell>> {
let mut system = System::new();
let mut current_pid = sysinfo::get_current_pid().ok();
system
.refresh_processes_specifics(ProcessRefreshKind::new().with_exe(UpdateKind::OnlyIfNotSet));
while let Some(pid) = current_pid {
system.refresh_process(pid);
if let Some(process) = system.process(pid) {
current_pid = process.parent();
debug!("pid {pid} parent process is {current_pid:?}");
let process_name = process
.exe()
.and_then(|x| x.file_stem())
.and_then(|x| x.to_str())
.and_then(|x| {
tap_none(x.file_stem(), || {
warn!("failed to get file stem from {:?}", x);
})
})
.and_then(|x| {
tap_none(x.to_str(), || {
warn!("failed to convert file stem to string: {:?}", x);
})
})
.map(str::to_lowercase);
if let Some(shell) = process_name
.as_ref()
@ -24,9 +36,22 @@ pub fn infer_shell() -> Option<Box<dyn Shell>> { @@ -24,9 +36,22 @@ pub fn infer_shell() -> Option<Box<dyn Shell>> {
return Some(shell);
}
} else {
warn!("process not found for {pid}");
current_pid = None;
}
}
None
}
fn tap_none<T, F>(opt: Option<T>, f: F) -> Option<T>
where
F: FnOnce(),
{
match &opt {
Some(_) => (),
None => f(),
};
opt
}

Loading…
Cancel
Save