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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ nupm install --path .

### Manual

Copy `dagger.nu` anywhere on your `NU_LIB_DIRS` path, then add to `config.nu`:
Clone the repo, then add to `config.nu`:

```nushell
use dagger.nu *
use /path/to/dagger.nu/mod.nu *
```

Or source it directly:
Or, if you prefer to load only the completions file directly:

```nushell
use /path/to/dagger.nu *
use /path/to/dagger.nu/completions/dagger.nu *
```
Comment on lines +38 to 48
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README manual install now points users at use /path/to/dagger.nu/completions/dagger.nu *, but this PR introduces mod.nu specifically as the stable root entry point. Consider updating the README to recommend use /path/to/dagger.nu/mod.nu * (and optionally mention the completions file as an alternative) to keep documentation consistent with the new layout and intended public entrypoint.

Copilot uses AI. Check for mistakes.

## Usage
Expand Down
16 changes: 8 additions & 8 deletions dagger.nu → completions/dagger.nu
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# Completion Helpers
# ============================================================================

def "nu-complete dagger subcommands" [] {
export def "nu-complete dagger subcommands" [] {
[
[value description];
[login "Log in to Dagger Cloud"]
Expand All @@ -33,7 +33,7 @@ def "nu-complete dagger subcommands" [] {
]
}

def "nu-complete dagger sdks" [] {
export def "nu-complete dagger sdks" [] {
[
[value description];
[go "Go SDK"]
Expand All @@ -42,7 +42,7 @@ def "nu-complete dagger sdks" [] {
]
}

def "nu-complete dagger progress" [] {
export def "nu-complete dagger progress" [] {
[
[value description];
[auto "Automatically detect output format (default)"]
Expand All @@ -52,7 +52,7 @@ def "nu-complete dagger progress" [] {
]
}

def "nu-complete dagger models" [] {
export def "nu-complete dagger models" [] {
[
[value description];
[claude-sonnet-4-5 "Anthropic Claude Sonnet 4.5"]
Expand All @@ -65,7 +65,7 @@ def "nu-complete dagger models" [] {
]
}

def "nu-complete dagger licenses" [] {
export def "nu-complete dagger licenses" [] {
[
[value description];
[Apache-2.0 "Apache License 2.0 (default)"]
Expand All @@ -78,19 +78,19 @@ def "nu-complete dagger licenses" [] {
]
}

def "nu-complete dagger shells" [] {
export def "nu-complete dagger shells" [] {
[bash zsh fish powershell]
}

def "nu-complete dagger compat" [] {
export def "nu-complete dagger compat" [] {
[
[value description];
[latest "Use the latest engine API version (default)"]
[skip "Skip compatibility check"]
]
}

def "nu-complete dagger toolchain subcommands" [] {
export def "nu-complete dagger toolchain subcommands" [] {
[
[value description];
[install "Install a toolchain to the current module"]
Expand Down
9 changes: 9 additions & 0 deletions mod.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# dagger.nu - Nushell completions and utilities for the Dagger CLI
#
# Usage:
# use /path/to/dagger.nu/mod.nu *
#
# Or with nupm (once published):
# nupm install dagger.nu

export use completions/dagger.nu *
232 changes: 232 additions & 0 deletions tests/completions.test.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
#!/usr/bin/env nu
# tests/completions.test.nu — Test suite for dagger.nu completions
#
# Run:
# nu tests/completions.test.nu
# nu tests/completions.test.nu --verbose
# nu tests/completions.test.nu --test "sdk"

use std/assert

# Top-level use — brings externs and helpers into parser scope so
# scope commands reflects them and helper calls resolve without a subshell.
use ../completions/dagger.nu *
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ../completions/dagger.nu * is a brittle relative path. When running the test as documented (nu tests/completions.test.nu from the repo root), this resolves to ../completions/... and can fail depending on how use resolves relative paths. Build the path from $env.FILE_PWD (like the mod.nu test does) so the suite works regardless of the current working directory.

Suggested change
use ../completions/dagger.nu *
use ($env.FILE_PWD | path join ".." "completions" "dagger.nu") *

Copilot uses AI. Check for mistakes.

# ============================================================================
# Helpers
# ============================================================================

def assert-has-value [list: list, expected: string] {
let found = $list | where value == $expected
assert ($found | is-not-empty) $"Expected value '($expected)' not found"
}

def assert-unique-values [list: list, label: string] {
let dupes = $list | get value | uniq -d
assert ($dupes | is-empty) $"($label) has duplicate values: ($dupes | to nuon)"
}

# ============================================================================
# Helper function tests — completion values
# ============================================================================

def "test sdks" [] {
let result = nu-complete dagger sdks
assert (($result | length) == 3)
for v in ["go" "python" "typescript"] { assert-has-value $result $v }
assert-unique-values $result "sdks"
}

def "test progress formats" [] {
let result = nu-complete dagger progress
assert (($result | length) == 4)
for v in ["auto" "plain" "tty" "dots"] { assert-has-value $result $v }
assert-unique-values $result "progress"
}

def "test compat values" [] {
let result = nu-complete dagger compat
assert (($result | length) == 2)
for v in ["latest" "skip"] { assert-has-value $result $v }
}

def "test shell names" [] {
let result = nu-complete dagger shells
assert (($result | length) == 4)
for v in ["bash" "zsh" "fish" "powershell"] {
assert ($v in $result) $"Shell '($v)' missing"
}
}

def "test license spdx identifiers" [] {
let result = nu-complete dagger licenses
for v in ["Apache-2.0" "MIT" "GPL-3.0" "BSD-3-Clause" "MPL-2.0"] {
assert-has-value $result $v
}
assert-unique-values $result "licenses"
}

def "test model ids" [] {
let result = nu-complete dagger models
assert (($result | length) >= 4)
for v in ["claude-sonnet-4-5" "gpt-4.1" "gemini-2.0-flash"] {
assert-has-value $result $v
}
assert-unique-values $result "models"
}

def "test subcommands" [] {
let result = nu-complete dagger subcommands
for v in ["init" "develop" "call" "config" "install" "uninstall" "update"
"query" "run" "login" "logout" "version" "toolchain" "completion"] {
assert-has-value $result $v
}
assert-unique-values $result "subcommands"
}

def "test toolchain subcommands" [] {
let result = nu-complete dagger toolchain subcommands
assert (($result | length) == 4)
for v in ["install" "list" "uninstall" "update"] { assert-has-value $result $v }
assert-unique-values $result "toolchain subcommands"
}

def "test helpers have descriptions" [] {
# Every table-form helper should have non-empty descriptions
let helpers = [
(nu-complete dagger subcommands)
(nu-complete dagger sdks)
(nu-complete dagger progress)
(nu-complete dagger models)
(nu-complete dagger licenses)
(nu-complete dagger compat)
(nu-complete dagger toolchain subcommands)
]
for list in $helpers {
for entry in $list {
assert (($entry.description | str length) > 0) $"Empty description for '($entry.value)'"
}
}
}

# ============================================================================
# Extern registration tests — observable module surface
# ============================================================================

def "test top-level commands registered" [] {
let cmds = scope commands | where name =~ "^dagger" | get name
for expected in [
"dagger" "dagger call" "dagger config" "dagger core"
"dagger develop" "dagger functions" "dagger init"
"dagger install" "dagger uninstall" "dagger update"
"dagger login" "dagger logout"
"dagger query" "dagger q" "dagger run"
"dagger completion" "dagger version"
] {
assert ($expected in $cmds) $"'($expected)' not registered after use"
}
}

def "test toolchain commands registered" [] {
let cmds = scope commands | where name =~ "^dagger toolchain" | get name
for expected in [
"dagger toolchain"
"dagger toolchain install"
"dagger toolchain list"
"dagger toolchain uninstall"
"dagger toolchain update"
] {
assert ($expected in $cmds) $"'($expected)' not registered after use"
}
}

def "test no duplicate externs" [] {
let cmds = scope commands | where name =~ "^dagger" | get name
let dupes = $cmds | uniq -d
assert ($dupes | is-empty) $"Duplicate externs: ($dupes | to nuon)"
}

# ============================================================================
# mod.nu integration test
# ============================================================================

def "test mod.nu entry point" [] {
# Verify the root mod.nu re-exports everything from completions/
# We load it in a subshell to get an isolated scope reading
let mod_path = ([$env.FILE_PWD ".." "mod.nu"] | path join | path expand)
let out = ^nu --no-config-file -c $"
use '($mod_path)' *
scope commands | where name =~ '^dagger' | get name | to json
" | from json
assert ($out | is-not-empty) "mod.nu must re-export dagger commands"
assert ("dagger" in $out) "'dagger' extern must be reachable via mod.nu"
assert ("dagger init" in $out) "'dagger init' must be reachable via mod.nu"
}

# ============================================================================
# Runner
# ============================================================================

def main [
--verbose (-v) # Print each test name as it runs
--test (-t): string # Run only tests whose name contains this substring
] {
let all_tests = [
"sdks"
"progress formats"
"compat values"
"shell names"
"license spdx identifiers"
"model ids"
"subcommands"
"toolchain subcommands"
"helpers have descriptions"
"top-level commands registered"
"toolchain commands registered"
"no duplicate externs"
"mod.nu entry point"
]

let tests = if $test != null {
$all_tests | where { $in =~ $test }
} else {
$all_tests
}

print $"Running ($tests | length) tests...\n"

let results = $tests | each { |name|
if $verbose { print -n $" test ($name)... " }
let outcome = try {
match $name {
"sdks" => { test sdks }
"progress formats" => { test progress formats }
"compat values" => { test compat values }
"shell names" => { test shell names }
"license spdx identifiers" => { test license spdx identifiers }
"model ids" => { test model ids }
"subcommands" => { test subcommands }
"toolchain subcommands" => { test toolchain subcommands }
"helpers have descriptions" => { test helpers have descriptions }
"top-level commands registered" => { test top-level commands registered }
"toolchain commands registered" => { test toolchain commands registered }
"no duplicate externs" => { test no duplicate externs }
"mod.nu entry point" => { test mod.nu entry point }
}
if $verbose { print "✓" }
{name: $name, passed: true, error: ""}
} catch { |err|
if not $verbose { print -n $" test ($name)... " }
print $"✗ ($err.msg)"
{name: $name, passed: false, error: $err.msg}
}
$outcome
}

let passed = $results | where passed == true | length
let failed = $results | where passed == false | length

print $"\n($passed) passed, ($failed) failed"

if $failed > 0 { exit 1 }
}
Loading