Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/test-auto-venv.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
on:
pull_request:
paths:
- "modules/virtual_environments/auto-venv/**"
push:
paths:
- "modules/virtual_environments/auto-venv/**"

jobs:
test-auto-venv:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-13]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- uses: hustcer/setup-nu@v3.9
with:
version: "*"
check-latest: true

- name: Run auto-venv tests
shell: bash
working-directory: modules/virtual_environments/auto-venv
run: nu test_auto_venv.nu
20 changes: 9 additions & 11 deletions modules/virtual_environments/auto-venv/auto-venv.nu
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ export-env {


def default-hooks [] {
(if ($env.config.hooks.env_change.PWD != null) {
[$env.config.hooks.env_change.PWD]
}
else {
let existing = ($env.config? | default {} | get -o hooks.env_change.PWD)
if ($existing != null) {
$existing
} else {
[]
})
}
}

def build-hooks [] {
Expand All @@ -33,24 +33,22 @@ def build-hooks [] {

let on_enter = '
let _env = $env
let pwd = $_env.PWD

let trigger = (path_extensions path find-sub . __trigger__ --type ["symlink", "file"])
if ($trigger | is-empty) { return }

cd ($trigger | path dirname)
overlay use __trigger__ as __auto_venv

cd ($pwd)

auto-venv-on-enter $_env

hide _env
hide pwd
hide trigger
'

let on_exit = '
overlay hide __auto_venv --keep-env [PWD]
if (venv_helpers venv-is-active) {
overlay hide __auto_venv --keep-env [PWD]
}
'

let on_enter = ($on_enter | str replace -a '__trigger__' $trigger)
Expand Down
4 changes: 2 additions & 2 deletions modules/virtual_environments/auto-venv/path_extensions.nu
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ export def "path find-sub" [
);

if ($paths != null) and ($paths | length) > 0 {
[ ($paths | first), $subfolder ] | path join
} else {[]}
[ ($paths | last), $subfolder ] | path join
} else { null }
}
104 changes: 104 additions & 0 deletions modules/virtual_environments/auto-venv/test_auto_venv.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env nu
# Test suite for auto-venv module on nushell 0.108+

use path_extensions.nu
use venv_helpers.nu

export def main [] {
let test_root = (mktemp -d | path expand)
mut results = []

print $"nushell version: (version | get version)"
print $"test root: ($test_root)"
print "---"

# Setup: create mock project structure
let trigger_name = ".__test-trigger.nu"
let trigger_src = ($env.FILE_PWD | path join "venvs" "python-venv.nu")

mkdir ($test_root | path join "project_a" ".venv" "bin")
mkdir ($test_root | path join "project_a" "sub")
mkdir ($test_root | path join "project_b")
mkdir ($test_root | path join "nested" "outer" ".venv" "bin")
mkdir ($test_root | path join "nested" "outer" "inner" ".venv" "bin")

ln -sf $trigger_src ($test_root | path join "project_a" $trigger_name)
ln -sf $trigger_src ($test_root | path join "project_b" $trigger_name)
ln -sf $trigger_src ($test_root | path join "nested" "outer" $trigger_name)
ln -sf $trigger_src ($test_root | path join "nested" "outer" "inner" $trigger_name)

# Test 1: path find-sub locates trigger file
let result = (path_extensions path find-sub ($test_root | path join "project_a") $trigger_name --type ["symlink", "file"])
let ok = ($result == ($test_root | path join "project_a" $trigger_name))
print $"(if $ok { 'PASS' } else { 'FAIL' }): path find-sub finds trigger in project_a"
$results = ($results | append $ok)

# Test 2: path find-sub returns null when not found
let result = (path_extensions path find-sub "/tmp" $trigger_name)
let ok = ($result | is-empty)
print $"(if $ok { 'PASS' } else { 'FAIL' }): path find-sub returns null for missing trigger"
$results = ($results | append $ok)

# Test 3: path find-sub returns deepest match (last not first)
let result = (path_extensions path find-sub ($test_root | path join "nested" "outer" "inner") $trigger_name --type ["symlink", "file"])
let ok = ($result == ($test_root | path join "nested" "outer" "inner" $trigger_name))
print $"(if $ok { 'PASS' } else { 'FAIL' }): path find-sub returns deepest match"
if not $ok { print $" got: ($result)" }
$results = ($results | append $ok)

# Test 4: path find-sub finds .venv
let result = (path_extensions path find-sub ($test_root | path join "project_a") ".venv")
let ok = ($result | is-empty | not $in)
print $"(if $ok { 'PASS' } else { 'FAIL' }): path find-sub locates .venv"
$results = ($results | append $ok)

# Test 5: path find-sub returns null for .venv in project_b (no .venv there)
let result = (path_extensions path find-sub ($test_root | path join "project_b") ".venv")
let ok = ($result | is-empty)
print $"(if $ok { 'PASS' } else { 'FAIL' }): path find-sub returns null for missing .venv"
$results = ($results | append $ok)

# Test 6: venv-is-active returns false when no overlay
let ok = (not (venv_helpers venv-is-active))
print $"(if $ok { 'PASS' } else { 'FAIL' }): venv-is-active false with no active overlay"
$results = ($results | append $ok)

# Test 7: has-entered-venv condition check
$env.AUTO_VENV_TRIGGER = $trigger_name
let ok = (venv_helpers has-entered-venv ($test_root | path join "project_a"))
print $"(if $ok { 'PASS' } else { 'FAIL' }): has-entered-venv detects trigger in project_a"
$results = ($results | append $ok)

# Test 8: has-entered-venv returns false for dir without trigger in ancestry
let ok = (not (venv_helpers has-entered-venv $test_root))
print $"(if $ok { 'PASS' } else { 'FAIL' }): has-entered-venv false for dir without trigger in ancestry"
$results = ($results | append $ok)

# Test 9: default-hooks does not wrap list in list
$env.AUTO_VENV_TRIGGER = $trigger_name
$env.config.hooks.env_change = { PWD: [{|| null}] }
source-env auto-venv.nu
let hook_types = ($env.config.hooks.env_change.PWD | each {|h| $h | describe })
let ok = (not ($hook_types | any {|t| $t starts-with "list<" }))
print $"(if $ok { 'PASS' } else { 'FAIL' }): no nested list types in PWD hooks"
if not $ok { print $" types: ($hook_types)" }
$results = ($results | append $ok)

# Test 10: overlay use resolves via NU_LIB_DIRS
$env.NU_LIB_DIRS = ($env.NU_LIB_DIRS | append ($env.FILE_PWD | path join "venvs"))
overlay use python-venv.nu as __auto_venv_test
let ok = ('__auto_venv_test' in (overlay list | where active | get name))
print $"(if $ok { 'PASS' } else { 'FAIL' }): overlay use resolves via NU_LIB_DIRS"
overlay hide __auto_venv_test --keep-env [PWD]
$results = ($results | append $ok)

# Cleanup
rm -rf $test_root

# Summary
let passed = ($results | where $it | length)
let failed = ($results | where (not $it) | length)
print "---"
print $"Results: ($passed) passed, ($failed) failed"
if $failed > 0 { exit 1 }
}
2 changes: 1 addition & 1 deletion modules/virtual_environments/auto-venv/venv_helpers.nu
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def get-env [
}

export def venv-is-active [] {
'__auto_venv' in (overlay list)
'__auto_venv' in (overlay list | where active | get name)
}

# Creates a virtual environment under the current directory
Expand Down
Loading