From d17c4a017c84227a7b33d840b35025ef5779c48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 13:42:38 +0200 Subject: [PATCH 01/26] Start fetching package from registry --- nupm/install.nu | 83 ++++++++++++++++++++++- nupm/mod.nu | 21 +++++- nupm/utils/dirs.nu | 5 ++ nupm/utils/misc.nu | 38 +++++++++++ nupm/utils/version.nu | 8 +++ tests/packages/registry.nuon | 20 ++++++ tests/packages/spam_script/nupm.nuon | 2 +- tests/packages/spam_script/spam_script.nu | 2 +- 8 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 nupm/utils/misc.nu create mode 100644 nupm/utils/version.nu create mode 100644 tests/packages/registry.nuon diff --git a/nupm/install.nu b/nupm/install.nu index 0429500..2ff0b8b 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -2,6 +2,8 @@ use std log use utils/dirs.nu [ nupm-home-prompt script-dir module-dir tmp-dir ] use utils/log.nu throw-error +use utils/version.nu sort-pkgs +use utils/misc.nu check-cols def open-package-file [dir: path] { let package_file = $dir | path join "nupm.nuon" @@ -146,9 +148,86 @@ def install-path [ } } +def fetch-package [ + package: string # Name of the package + --registry: string # Which registry to use +] { + let regs = $env.NUPM_REGISTRIES + | items {|name, path| + if ($registry | is-empty) or ($name == $registry) or ($path == $registry) { + print $path + let registry = if ($path | path type) == file { + open $path + } else { + try { + let reg = http get $path + + if local in $reg { + throw-error ("Can't have local packages in online registry" + + $" '($path)'.") + } + + $reg + } catch { + throw-error $"Cannot open '($path)' as a file or URL." + } + } + + $registry | check-cols --missing-ok "registry" [ git local ] | ignore + + let pkgs_local = $registry.local? + | default [] + | check-cols "local packages" [ name version path ] + | where name == $package + + let pkgs_git = $registry.git? + | default [] + | check-cols "git packages" [ name version url revision path ] + | where name == $package + + # Detect duplicate versions + let all_versions = $pkgs_local.version? + | default [] + | append ($pkgs_git.version? | default []) + + if ($all_versions | uniq | length) != ($all_versions | length) { + throw-error ($'Duplicate versions of package ($package) detected' + + $' in registry ($name).') + } + + let pkgs = $pkgs_local + | insert type local + | insert url null + | insert revision null + | append ($pkgs_git | insert type git) + + { + name: $name + pkgs: $pkgs + } + } + } + | compact + + if ($regs | is-empty) { + throw-error 'No registries found' + } else if ($regs | length) > 1 { + # TODO: Here could be interactive prompt + throw-error 'Multiple registries contain the same package' + } + + # Now, only one registry contains the package + $regs | first | get pkgs | sort-pkgs | last +} + # Install a nupm package +# +# Installation consists of two parts: +# 1. Fetching the package (if the package is online) +# 2. Installing the package (build action, if any; copy files to install location) export def main [ package # Name, path, or link to the package + --registry: string # Which registry to use --path # Install package from a directory with nupm.nuon given by 'name' --force(-f) # Overwrite already installed package --no-confirm # Allows to bypass the interactive confirmation, useful for scripting @@ -158,8 +237,8 @@ export def main [ } if not $path { - throw-error "missing_required_option" "`nupm install` currently requires a `--path` flag" + fetch-package $package --registry $registry } - install-path $package --force=$force + # install-path $package --force=$force } diff --git a/nupm/mod.nu b/nupm/mod.nu index dc8a176..3b7b0de 100644 --- a/nupm/mod.nu +++ b/nupm/mod.nu @@ -1,4 +1,6 @@ -use utils/dirs.nu [ DEFAULT_NUPM_HOME DEFAULT_NUPM_TEMP nupm-home-prompt ] +use utils/dirs.nu [ + DEFAULT_NUPM_HOME DEFAULT_NUPM_TEMP DEFAULT_NUPM_CACHE nupm-home-prompt +] export module install.nu export module test.nu @@ -10,6 +12,23 @@ export-env { # Ensure temporary path is set. $env.NUPM_TEMP = ($env.NUPM_TEMP? | default $DEFAULT_NUPM_TEMP) + + # Ensure install cache is set + $env.NUPM_CACHE = ($env.NUPM_CACHE? | default $DEFAULT_NUPM_CACHE) + + # TODO: Maybe this is not the best way to store registries, but should be + # good enough for now. + # TODO: Remove local and kubouch which are just for testing + # TODO: Move setting this to config file + # TODO: Add `nupm registry` for showing info about registries + # TODO: Add `nupm registry add/remove` to add/remove registry from config + # file (requires nuon formatting). + $env.NUPM_REGISTRIES = { + # nupm: ($env.NUPM_HOME | path join registry.nuon) + kubouch:'https://git.sr.ht/~kubouch/nupkgs/blob/main/registry.nuon' + local_test: ($env.FILE_PWD | path join tests packages registry.nuon) + # remote_test: 'https://raw.githubusercontent.com/nushell/nupm/main/tests/packages/registry.nuon' + } } # Nushell Package Manager diff --git a/nupm/utils/dirs.nu b/nupm/utils/dirs.nu index e756a4a..14d4490 100644 --- a/nupm/utils/dirs.nu +++ b/nupm/utils/dirs.nu @@ -6,6 +6,11 @@ export const DEFAULT_NUPM_HOME = ($nu.default-config-dir | path join "nupm") # Default temporary path for various nupm purposes export const DEFAULT_NUPM_TEMP = ($nu.temp-path | path join "nupm") +# Default path for installation cache +export const DEFAULT_NUPM_CACHE = ($nu.default-config-dir + | path join nupm cache) + + # Prompt to create $env.NUPM_HOME if it does not exist and some sanity checks. # # returns true if the root directory exists or has been created, false otherwise diff --git a/nupm/utils/misc.nu b/nupm/utils/misc.nu new file mode 100644 index 0000000..8897887 --- /dev/null +++ b/nupm/utils/misc.nu @@ -0,0 +1,38 @@ +# Misc unsorted helpers + +# Make sure input has requested columns and no extra columns +export def check-cols [ + what: string, + required_cols: list + --extra-ok + --missing-ok +]: [ table -> table, record -> record ] { + let inp = $in + + if ($inp | is-empty) { + return $inp + } + + let cols = $inp | columns + if not $missing_ok { + let missing_cols = $required_cols | where {|req_col| $req_col not-in $cols } + + if not ($missing_cols | is-empty) { + throw-error ($"Missing the following required columns in ($what):" + + $" ($missing_cols | str join ', ')") + ) + } + } + + if not $extra_ok { + let extra_cols = $cols | where {|col| $col not-in $required_cols } + + if not ($extra_cols | is-empty) { + throw-error ($"Got the following extra columns in ($what):" + + $" ($extra_cols | str join ', ')") + ) + } + } + + $inp +} diff --git a/nupm/utils/version.nu b/nupm/utils/version.nu new file mode 100644 index 0000000..9ee8e20 --- /dev/null +++ b/nupm/utils/version.nu @@ -0,0 +1,8 @@ +# Commands related to handling versions +# +# We might move some of this to Nushell builtins + +# Sort packages by version +export def sort-pkgs []: table -> table { + sort-by version +} diff --git a/tests/packages/registry.nuon b/tests/packages/registry.nuon new file mode 100644 index 0000000..5884cad --- /dev/null +++ b/tests/packages/registry.nuon @@ -0,0 +1,20 @@ +# Testing packages +{ + git: [ + [name version url revision path]; + [ + spam_script + 0.1.0 + https://github.com/nushell/nupm.git + 38f80e78997129c42b8ee8a2cb542e0b059a9e44 + tests/packages/spam_script + ] + ] + + local: [ + [name version path]; + [spam_script 0.2.0 spam_script] + [spam_custom 0.1.0 spam_custom] + [spam_module 0.1.0 spam_module] + ] +} diff --git a/tests/packages/spam_script/nupm.nuon b/tests/packages/spam_script/nupm.nuon index 7988e6d..293578f 100644 --- a/tests/packages/spam_script/nupm.nuon +++ b/tests/packages/spam_script/nupm.nuon @@ -1,6 +1,6 @@ { name: spam_script, type: script, - version: "0.1.0", + version: "0.2.0", scripts: ['spam_bar.nu'] } diff --git a/tests/packages/spam_script/spam_script.nu b/tests/packages/spam_script/spam_script.nu index d9371ac..ca363f2 100755 --- a/tests/packages/spam_script/spam_script.nu +++ b/tests/packages/spam_script/spam_script.nu @@ -1,4 +1,4 @@ #!/usr/bin/env nu def main [] { - "Hello world!" + "Hello world v0.2.0!" } From 5a135514c1d6410d70e6047a628f93f2c1492796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 13:53:48 +0200 Subject: [PATCH 02/26] Add comments --- nupm/install.nu | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nupm/install.nu b/nupm/install.nu index 2ff0b8b..13c35f8 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -152,10 +152,13 @@ def fetch-package [ package: string # Name of the package --registry: string # Which registry to use ] { + # Collect all registries matching the package and all matching packages let regs = $env.NUPM_REGISTRIES | items {|name, path| if ($registry | is-empty) or ($name == $registry) or ($path == $registry) { print $path + + # Open registry (online or offline) let registry = if ($path | path type) == file { open $path } else { @@ -175,6 +178,7 @@ def fetch-package [ $registry | check-cols --missing-ok "registry" [ git local ] | ignore + # Find all packages matching $package in the registry let pkgs_local = $registry.local? | default [] | check-cols "local packages" [ name version path ] From c4826698628a76eb08942d21896dc4b96826a519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 14:23:13 +0200 Subject: [PATCH 03/26] Add registries completions --- nupm/install.nu | 3 ++- nupm/utils/completions.nu | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 nupm/utils/completions.nu diff --git a/nupm/install.nu b/nupm/install.nu index 13c35f8..3b3b097 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -1,5 +1,6 @@ use std log +use utils/completions.nu complete-registries use utils/dirs.nu [ nupm-home-prompt script-dir module-dir tmp-dir ] use utils/log.nu throw-error use utils/version.nu sort-pkgs @@ -231,7 +232,7 @@ def fetch-package [ # 2. Installing the package (build action, if any; copy files to install location) export def main [ package # Name, path, or link to the package - --registry: string # Which registry to use + --registry: string@complete-registries # Which registry to use --path # Install package from a directory with nupm.nuon given by 'name' --force(-f) # Overwrite already installed package --no-confirm # Allows to bypass the interactive confirmation, useful for scripting diff --git a/nupm/utils/completions.nu b/nupm/utils/completions.nu new file mode 100644 index 0000000..d1d37ab --- /dev/null +++ b/nupm/utils/completions.nu @@ -0,0 +1,3 @@ +export def complete-registries [] { + $env.NUPM_REGISTRIES? | default {} | columns +} From 7645765e6b95c87e9961be72098e2834edc4f39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 15:27:44 +0200 Subject: [PATCH 04/26] Download online package and install it --- nupm/install.nu | 90 ++++++++++++++++++++++++++++++++++++++++------ nupm/utils/dirs.nu | 15 ++++++-- 2 files changed, 91 insertions(+), 14 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index 3b3b097..b9e6556 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -1,10 +1,10 @@ use std log use utils/completions.nu complete-registries -use utils/dirs.nu [ nupm-home-prompt script-dir module-dir tmp-dir ] +use utils/dirs.nu [ nupm-home-prompt cache-dir module-dir script-dir tmp-dir ] use utils/log.nu throw-error -use utils/version.nu sort-pkgs use utils/misc.nu check-cols +use utils/version.nu sort-pkgs def open-package-file [dir: path] { let package_file = $dir | path join "nupm.nuon" @@ -154,7 +154,7 @@ def fetch-package [ --registry: string # Which registry to use ] { # Collect all registries matching the package and all matching packages - let regs = $env.NUPM_REGISTRIES + let regs = $env.NUPM_REGISTRIES | items {|name, path| if ($registry | is-empty) or ($name == $registry) or ($path == $registry) { print $path @@ -180,18 +180,18 @@ def fetch-package [ $registry | check-cols --missing-ok "registry" [ git local ] | ignore # Find all packages matching $package in the registry - let pkgs_local = $registry.local? - | default [] + let pkgs_local = $registry.local? + | default [] | check-cols "local packages" [ name version path ] | where name == $package - let pkgs_git = $registry.git? - | default [] + let pkgs_git = $registry.git? + | default [] | check-cols "git packages" [ name version url revision path ] | where name == $package # Detect duplicate versions - let all_versions = $pkgs_local.version? + let all_versions = $pkgs_local.version? | default [] | append ($pkgs_git.version? | default []) @@ -208,6 +208,7 @@ def fetch-package [ { name: $name + path: $path pkgs: $pkgs } } @@ -222,7 +223,72 @@ def fetch-package [ } # Now, only one registry contains the package - $regs | first | get pkgs | sort-pkgs | last + let reg = $regs | first + let pkg = $reg | get pkgs | sort-pkgs | last + print ($reg | table -e) + + if $pkg.type == 'git' { + download-pkg $pkg + } else { + # local package path is either absolute or relative to the registry file + $reg.path | path join $pkg.path + } +} + +# Downloads a package and returns its downloaded path +def download-pkg [ + pkg: record< + name: string, + version: string, + url: string, + revision: string, + path: string, + type: string, + > +]: nothing -> path { + # TODO: Add some kind of hashing to check that files really match + + if ($pkg.type != 'git') { + throw-error 'Downloading non-git packages is not supported yet' + } + + let cache_dir = cache-dir --ensure + cd $cache_dir + + let git_dir = $cache_dir | path join git + mkdir $git_dir + cd $git_dir + + let repo_name = $pkg.url | url parse | get path | path parse | get stem + let url_hash = $pkg.url | hash md5 # in case of git repo name collision + let clone_dir = $'($repo_name)-($url_hash)-($pkg.revision)' + + let pkg_dir = $env.PWD | path join $clone_dir $pkg.path + + if ($pkg_dir | path exists) { + print 'Found package in cache' + return $pkg_dir + } + + try { + git clone $pkg.url $clone_dir + } catch { + throw-error $'Error cloning repository ($pkg.url)' + } + + cd $clone_dir + + try { + git checkout $pkg.revision + } catch { + throw-error $'Error checking out revision ($pkg.revision)' + } + + if not ($pkg_dir | path exists) { + throw-error $'Path ($pkg.path) does not exist' + } + + $pkg_dir } # Install a nupm package @@ -241,9 +307,11 @@ export def main [ return } - if not $path { + let pkg = if not $path { fetch-package $package --registry $registry + } else { + $package } - # install-path $package --force=$force + install-path $pkg --force=$force } diff --git a/nupm/utils/dirs.nu b/nupm/utils/dirs.nu index 14d4490..83ae89c 100644 --- a/nupm/utils/dirs.nu +++ b/nupm/utils/dirs.nu @@ -3,13 +3,12 @@ # Default installation path for nupm packages export const DEFAULT_NUPM_HOME = ($nu.default-config-dir | path join "nupm") -# Default temporary path for various nupm purposes -export const DEFAULT_NUPM_TEMP = ($nu.temp-path | path join "nupm") - # Default path for installation cache export const DEFAULT_NUPM_CACHE = ($nu.default-config-dir | path join nupm cache) +# Default temporary path for various nupm purposes +export const DEFAULT_NUPM_TEMP = ($nu.temp-path | path join "nupm") # Prompt to create $env.NUPM_HOME if it does not exist and some sanity checks. # @@ -75,6 +74,16 @@ export def module-dir [--ensure]: nothing -> path { $d } +export def cache-dir [--ensure]: nothing -> path { + let d = $env.NUPM_CACHE + + if $ensure { + mkdir $d + } + + $d +} + export def tmp-dir [subdir: string, --ensure]: nothing -> path { let d = $env.NUPM_TEMP | path join $subdir From 2dd709bc0170b38b95601580726b9a59698148ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 15:33:40 +0200 Subject: [PATCH 05/26] Fix wrong local relativee path --- nupm/install.nu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index b9e6556..5fd1212 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -225,13 +225,13 @@ def fetch-package [ # Now, only one registry contains the package let reg = $regs | first let pkg = $reg | get pkgs | sort-pkgs | last - print ($reg | table -e) + print $pkg if $pkg.type == 'git' { download-pkg $pkg } else { # local package path is either absolute or relative to the registry file - $reg.path | path join $pkg.path + $reg.path | path dirname | path join $pkg.path } } From 06c37b7b7247f71f2280d9519761d0e9261205ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 15:34:33 +0200 Subject: [PATCH 06/26] Adjust comment --- nupm/install.nu | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nupm/install.nu b/nupm/install.nu index 5fd1212..67507f7 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -230,7 +230,8 @@ def fetch-package [ if $pkg.type == 'git' { download-pkg $pkg } else { - # local package path is either absolute or relative to the registry file + # local package path is relative to the registry file (absolute paths + # are discouraged but work) $reg.path | path dirname | path join $pkg.path } } From 87198ea8193782e97a2b7b7723e4ef0ccd0c85f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 16:50:51 +0200 Subject: [PATCH 07/26] Add --version to allow setting exact version --- nupm/install.nu | 23 +++++++++++++++++------ nupm/utils/version.nu | 14 +++++++++++++- tests/packages/registry.nuon | 2 +- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index 67507f7..d9755e0 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -4,7 +4,7 @@ use utils/completions.nu complete-registries use utils/dirs.nu [ nupm-home-prompt cache-dir module-dir script-dir tmp-dir ] use utils/log.nu throw-error use utils/misc.nu check-cols -use utils/version.nu sort-pkgs +use utils/version.nu filter-by-version def open-package-file [dir: path] { let package_file = $dir | path join "nupm.nuon" @@ -152,6 +152,7 @@ def install-path [ def fetch-package [ package: string # Name of the package --registry: string # Which registry to use + --version: string # Package version to install ] { # Collect all registries matching the package and all matching packages let regs = $env.NUPM_REGISTRIES @@ -224,7 +225,12 @@ def fetch-package [ # Now, only one registry contains the package let reg = $regs | first - let pkg = $reg | get pkgs | sort-pkgs | last + let pkgs = $reg.pkgs | filter-by-version $version + let pkg = try { + $pkgs | last + } catch { + throw-error $'No package matching version `($version)`' + } print $pkg if $pkg.type == 'git' { @@ -298,19 +304,24 @@ def download-pkg [ # 1. Fetching the package (if the package is online) # 2. Installing the package (build action, if any; copy files to install location) export def main [ - package # Name, path, or link to the package - --registry: string@complete-registries # Which registry to use + package # Name, path, or link to the package + --registry: string@complete-registries # Which registry to use + --pkg-version(-v): string # Package version to install --path # Install package from a directory with nupm.nuon given by 'name' --force(-f) # Overwrite already installed package - --no-confirm # Allows to bypass the interactive confirmation, useful for scripting + --no-confirm # Allows to bypass the interactive confirmation, useful for scripting ]: nothing -> nothing { if not (nupm-home-prompt --no-confirm=$no_confirm) { return } let pkg = if not $path { - fetch-package $package --registry $registry + fetch-package $package --registry $registry --version $pkg_version } else { + if $pkg_version != null { + throw-error "Use only --path or --pkg-version, not both" + } + $package } diff --git a/nupm/utils/version.nu b/nupm/utils/version.nu index 9ee8e20..fb0d0e8 100644 --- a/nupm/utils/version.nu +++ b/nupm/utils/version.nu @@ -3,6 +3,18 @@ # We might move some of this to Nushell builtins # Sort packages by version -export def sort-pkgs []: table -> table { +def sort-by-version []: table -> table { sort-by version } + +# Check if the target version is equal or higher than the target version +def matches-version [version: string]: string -> bool { + # TODO: Add proper version sorting + $in == $version +} + +# Filter packages by version and sort them by version +export def filter-by-version [version: string]: table -> table { + let pkgs = $in | filter {|row| $row.version | matches-version $version} + $pkgs | sort-by-version +} diff --git a/tests/packages/registry.nuon b/tests/packages/registry.nuon index 5884cad..f6cfe12 100644 --- a/tests/packages/registry.nuon +++ b/tests/packages/registry.nuon @@ -6,7 +6,7 @@ spam_script 0.1.0 https://github.com/nushell/nupm.git - 38f80e78997129c42b8ee8a2cb542e0b059a9e44 + e46e2a93adacf1b8daec83882dc0aa9437a0a7b4 tests/packages/spam_script ] ] From 584e6f264a07f461ededb0f42d9733a3670a3c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Dec 2023 16:55:52 +0200 Subject: [PATCH 08/26] Adjust comment --- nupm/utils/version.nu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nupm/utils/version.nu b/nupm/utils/version.nu index fb0d0e8..b0f3c74 100644 --- a/nupm/utils/version.nu +++ b/nupm/utils/version.nu @@ -9,7 +9,7 @@ def sort-by-version []: table -> table { # Check if the target version is equal or higher than the target version def matches-version [version: string]: string -> bool { - # TODO: Add proper version sorting + # TODO: Add proper version matching $in == $version } From 2d33b3a210cef80bbcaa59c3d0b434ac38f4b124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Fri, 29 Dec 2023 12:55:47 +0200 Subject: [PATCH 09/26] Fix error when missing --pkg-version --- nupm/install.nu | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index d9755e0..ac95f63 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -152,7 +152,7 @@ def install-path [ def fetch-package [ package: string # Name of the package --registry: string # Which registry to use - --version: string # Package version to install + --version: any # Package version to install (string or null) ] { # Collect all registries matching the package and all matching packages let regs = $env.NUPM_REGISTRIES @@ -225,12 +225,19 @@ def fetch-package [ # Now, only one registry contains the package let reg = $regs | first - let pkgs = $reg.pkgs | filter-by-version $version + + let pkgs = if $version == null { + $reg.pkgs + } else { + $reg.pkgs | filter-by-version $version + } + let pkg = try { $pkgs | last } catch { throw-error $'No package matching version `($version)`' } + print $pkg if $pkg.type == 'git' { From 000b89e83731facd06e5ca5436767c1ac1daac97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Fri, 29 Dec 2023 14:07:17 +0200 Subject: [PATCH 10/26] Fix missing version sorting --- nupm/install.nu | 7 +------ nupm/utils/version.nu | 10 +++++++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index ac95f63..21eb59d 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -225,12 +225,7 @@ def fetch-package [ # Now, only one registry contains the package let reg = $regs | first - - let pkgs = if $version == null { - $reg.pkgs - } else { - $reg.pkgs | filter-by-version $version - } + let pkgs = $reg.pkgs | filter-by-version $version let pkg = try { $pkgs | last diff --git a/nupm/utils/version.nu b/nupm/utils/version.nu index b0f3c74..58eb36a 100644 --- a/nupm/utils/version.nu +++ b/nupm/utils/version.nu @@ -14,7 +14,11 @@ def matches-version [version: string]: string -> bool { } # Filter packages by version and sort them by version -export def filter-by-version [version: string]: table -> table { - let pkgs = $in | filter {|row| $row.version | matches-version $version} - $pkgs | sort-by-version +export def filter-by-version [version: any]: table -> table { + if $version == null { + $in + } else { + $in | filter {|row| $row.version | matches-version $version} + } + | sort-by-version } From 00d8fe1d7d1718f8e1c93d04f9e30c7f6251c447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Tue, 23 Jan 2024 20:31:43 +0200 Subject: [PATCH 11/26] Do not require registry env with flag; Add tests --- nupm/install.nu | 5 +++++ tests/mod.nu | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/nupm/install.nu b/nupm/install.nu index 21eb59d..3947651 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -154,6 +154,11 @@ def fetch-package [ --registry: string # Which registry to use --version: any # Package version to install (string or null) ] { + if not ($registry | is-empty) { + let reg_name = $registry | path parse | get stem + $env.NUPM_REGISTRIES = { $reg_name: $registry } + } + # Collect all registries matching the package and all matching packages let regs = $env.NUPM_REGISTRIES | items {|name, path| diff --git a/tests/mod.nu b/tests/mod.nu index dc6980f..b5a1c4a 100644 --- a/tests/mod.nu +++ b/tests/mod.nu @@ -3,6 +3,8 @@ use std assert use ../nupm/utils/dirs.nu tmp-dir use ../nupm +const TEST_REGISTRY_PATH = ([tests packages registry.nuon] | path join) + def with-nupm-home [closure: closure]: nothing -> nothing { let dir = tmp-dir test --ensure @@ -43,3 +45,22 @@ export def install-custom [] { assert installed [plugins nu_plugin_test] } } + +export def install-from-registry-with-flag [] { + with-nupm-home { + nupm install --registry $TEST_REGISTRY_PATH spam_script + + let contents = open ($env.NUPM_HOME | path join scripts spam_script.nu) + assert ($contents | str contains '0.2.0') + } +} + +export def install-from-registry-without-flag [] { + with-nupm-home { + $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } + nupm install spam_script + + let contents = open ($env.NUPM_HOME | path join scripts spam_script.nu) + assert ($contents | str contains '0.2.0') + } +} From 9b0ed0cbe8c842521a873ce2a374714fcdd8ad38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Tue, 23 Jan 2024 20:35:07 +0200 Subject: [PATCH 12/26] Fix whitespace --- nupm/mod.nu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nupm/mod.nu b/nupm/mod.nu index 3b7b0de..941ba50 100644 --- a/nupm/mod.nu +++ b/nupm/mod.nu @@ -1,4 +1,4 @@ -use utils/dirs.nu [ +use utils/dirs.nu [ DEFAULT_NUPM_HOME DEFAULT_NUPM_TEMP DEFAULT_NUPM_CACHE nupm-home-prompt ] @@ -13,10 +13,10 @@ export-env { # Ensure temporary path is set. $env.NUPM_TEMP = ($env.NUPM_TEMP? | default $DEFAULT_NUPM_TEMP) - # Ensure install cache is set + # Ensure install cache is set $env.NUPM_CACHE = ($env.NUPM_CACHE? | default $DEFAULT_NUPM_CACHE) - # TODO: Maybe this is not the best way to store registries, but should be + # TODO: Maybe this is not the best way to store registries, but should be # good enough for now. # TODO: Remove local and kubouch which are just for testing # TODO: Move setting this to config file From 4bcc71f43f9b193c3a163e6e88c50948604794e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Tue, 23 Jan 2024 21:07:42 +0200 Subject: [PATCH 13/26] Add nupm's own package registry --- nupm/mod.nu | 11 +++------ registry.nuon | 46 ++++++++++++++++++++++++++++++++++++ tests/packages/registry.nuon | 2 +- 3 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 registry.nuon diff --git a/nupm/mod.nu b/nupm/mod.nu index 941ba50..9ad2aad 100644 --- a/nupm/mod.nu +++ b/nupm/mod.nu @@ -18,16 +18,11 @@ export-env { # TODO: Maybe this is not the best way to store registries, but should be # good enough for now. - # TODO: Remove local and kubouch which are just for testing - # TODO: Move setting this to config file # TODO: Add `nupm registry` for showing info about registries - # TODO: Add `nupm registry add/remove` to add/remove registry from config - # file (requires nuon formatting). + # TODO: Add `nupm registry add/remove` to add/remove registry from the env $env.NUPM_REGISTRIES = { - # nupm: ($env.NUPM_HOME | path join registry.nuon) - kubouch:'https://git.sr.ht/~kubouch/nupkgs/blob/main/registry.nuon' - local_test: ($env.FILE_PWD | path join tests packages registry.nuon) - # remote_test: 'https://raw.githubusercontent.com/nushell/nupm/main/tests/packages/registry.nuon' + # TODO: Change the registry to a URL + "nupm-test": ($env.FILE_PWD | path join registry.nuon) } } diff --git a/registry.nuon b/registry.nuon new file mode 100644 index 0000000..d2c09d5 --- /dev/null +++ b/registry.nuon @@ -0,0 +1,46 @@ +# Registry is a NUON file that tells nupm where to look for packages +# +# Currently, these package types are supported: +# * git ..... package is an online git repository +# * local ... package is on a local filesystem +# +# The package type defines the columns of the registry record. Each column holds +# holds a table of packages. +# +# Git packages accept the following columns: +# * name ....... name of the package +# * version .... version of the package +# * url ........ Git URL of the package +# * revision ... Git revision to check out when installing the package (commit +# hash, version, branch name, ...) +# * path ....... Path relative to the repository root where to look for nupm.nuon +# +# Local packages accept the following columns: +# * name ....... name of the package +# * version .... version of the package +# * path ....... Path where to look for nupm.nuon. It can be absolute, or +# relative. If relative, then relative to the registry file. +# +# One registry can contain packages with the same name, but they must be of +# different version. + +{ + git: [ + [name version url revision path]; + [ + nu-git-manager + 0.5.0 + https://github.com/amtoine/nu-git-manager.git + 0.5.0 + pkgs/nu-git-manager + ] + + [ + nu-git-manager-sugar + 0.5.0 + https://github.com/amtoine/nu-git-manager.git + 0.5.0 + pkgs/nu-git-manager-sugar + ] + ] +} diff --git a/tests/packages/registry.nuon b/tests/packages/registry.nuon index f6cfe12..1e08a89 100644 --- a/tests/packages/registry.nuon +++ b/tests/packages/registry.nuon @@ -1,4 +1,4 @@ -# Testing packages +# Testing registry for testing packages { git: [ [name version url revision path]; From 32b0ba0b2b9eb7e353911b6101006f0b9444687d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Tue, 23 Jan 2024 21:41:34 +0200 Subject: [PATCH 14/26] Extend --registry to be more flexible --- nupm/install.nu | 106 +++++++++++++++++++++++++----------------------- tests/mod.nu | 22 ++++++---- 2 files changed, 70 insertions(+), 58 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index 3947651..e1774f4 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -154,69 +154,74 @@ def fetch-package [ --registry: string # Which registry to use --version: any # Package version to install (string or null) ] { - if not ($registry | is-empty) { + let registries = if (not ($registry | is-empty)) and ($registry in $env.NUPM_REGISTRIES) { + # If $registry is a valid column in $env.NUPM_REGISTRIES, use that + { $registry : ($env.NUPM_REGISTRIES | get $registry) } + } else if (not ($registry | is-empty)) and ($registry | path exists) { + # If $registry is a path, use that let reg_name = $registry | path parse | get stem - $env.NUPM_REGISTRIES = { $reg_name: $registry } + { $reg_name: $registry } + } else { + # Otherwise use $env.NUPM_REGISTRIES + $env.NUPM_REGISTRIES } # Collect all registries matching the package and all matching packages - let regs = $env.NUPM_REGISTRIES + let regs = $registries | items {|name, path| - if ($registry | is-empty) or ($name == $registry) or ($path == $registry) { - print $path - - # Open registry (online or offline) - let registry = if ($path | path type) == file { - open $path - } else { - try { - let reg = http get $path - - if local in $reg { - throw-error ("Can't have local packages in online registry" - + $" '($path)'.") - } - - $reg - } catch { - throw-error $"Cannot open '($path)' as a file or URL." + print $path + + # Open registry (online or offline) + let registry = if ($path | path type) == file { + open $path + } else { + try { + let reg = http get $path + + if local in $reg { + throw-error ("Can't have local packages in online registry" + + $" '($path)'.") } + + $reg + } catch { + throw-error $"Cannot open '($path)' as a file or URL." } + } - $registry | check-cols --missing-ok "registry" [ git local ] | ignore + $registry | check-cols --missing-ok "registry" [ git local ] | ignore - # Find all packages matching $package in the registry - let pkgs_local = $registry.local? - | default [] - | check-cols "local packages" [ name version path ] - | where name == $package + # Find all packages matching $package in the registry + let pkgs_local = $registry.local? + | default [] + | check-cols "local packages" [ name version path ] + | where name == $package - let pkgs_git = $registry.git? - | default [] - | check-cols "git packages" [ name version url revision path ] - | where name == $package + let pkgs_git = $registry.git? + | default [] + | check-cols "git packages" [ name version url revision path ] + | where name == $package - # Detect duplicate versions - let all_versions = $pkgs_local.version? - | default [] - | append ($pkgs_git.version? | default []) + # Detect duplicate versions + let all_versions = $pkgs_local.version? + | default [] + | append ($pkgs_git.version? | default []) - if ($all_versions | uniq | length) != ($all_versions | length) { - throw-error ($'Duplicate versions of package ($package) detected' - + $' in registry ($name).') - } + if ($all_versions | uniq | length) != ($all_versions | length) { + throw-error ($'Duplicate versions of package ($package) detected' + + $' in registry ($name).') + } - let pkgs = $pkgs_local - | insert type local - | insert url null - | insert revision null - | append ($pkgs_git | insert type git) + let pkgs = $pkgs_local + | insert type local + | insert url null + | insert revision null + | append ($pkgs_git | insert type git) - { - name: $name - path: $path - pkgs: $pkgs - } + { + name: $name + path: $path + pkgs: $pkgs } } | compact @@ -312,7 +317,8 @@ def download-pkg [ # 2. Installing the package (build action, if any; copy files to install location) export def main [ package # Name, path, or link to the package - --registry: string@complete-registries # Which registry to use + --registry: string@complete-registries # Which registry to use (either a name + # in $env.NUPM_REGISTRIES or a path) --pkg-version(-v): string # Package version to install --path # Install package from a directory with nupm.nuon given by 'name' --force(-f) # Overwrite already installed package diff --git a/tests/mod.nu b/tests/mod.nu index b5a1c4a..d11ea75 100644 --- a/tests/mod.nu +++ b/tests/mod.nu @@ -46,21 +46,27 @@ export def install-custom [] { } } -export def install-from-registry-with-flag [] { +export def install-from-local-registry [] { + def check-file [] { + let contents = open ($env.NUPM_HOME | path join scripts spam_script.nu) + assert ($contents | str contains '0.2.0') + } + with-nupm-home { + $env.NUPM_REGISTRIES = {} nupm install --registry $TEST_REGISTRY_PATH spam_script + check-file + } - let contents = open ($env.NUPM_HOME | path join scripts spam_script.nu) - assert ($contents | str contains '0.2.0') + with-nupm-home { + $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } + nupm install --registry test spam_script + check-file } -} -export def install-from-registry-without-flag [] { with-nupm-home { $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } nupm install spam_script - - let contents = open ($env.NUPM_HOME | path join scripts spam_script.nu) - assert ($contents | str contains '0.2.0') + check-file } } From 14fcf91f70fa38258a3e80927dca7ed47f5e4dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 13:31:55 +0200 Subject: [PATCH 15/26] Make test env more robust I.e., don't use user cache and temp dirs --- tests/mod.nu | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/mod.nu b/tests/mod.nu index d11ea75..7f30c83 100644 --- a/tests/mod.nu +++ b/tests/mod.nu @@ -6,10 +6,20 @@ use ../nupm const TEST_REGISTRY_PATH = ([tests packages registry.nuon] | path join) -def with-nupm-home [closure: closure]: nothing -> nothing { - let dir = tmp-dir test --ensure - with-env { NUPM_HOME: $dir } $closure - rm --recursive $dir +def with-test-env [closure: closure]: nothing -> nothing { + let home = tmp-dir nupm_test --ensure + let cache = tmp-dir 'nupm_test/cache' --ensure + let temp = tmp-dir 'nupm_test/temp' --ensure + + with-env { + NUPM_HOME: $home + NUPM_CACHE: $cache + NUPM_TEMP: $temp + } $closure + + rm --recursive $temp + rm --recursive $cache + rm --recursive $home } # Examples: @@ -20,7 +30,7 @@ def "assert installed" [path_tokens: list] { } export def install-script [] { - with-nupm-home { + with-test-env { nupm install --path tests/packages/spam_script assert installed [scripts spam_script.nu] @@ -29,7 +39,7 @@ export def install-script [] { } export def install-module [] { - with-nupm-home { + with-test-env { nupm install --path tests/packages/spam_module assert installed [scripts script.nu] @@ -39,7 +49,7 @@ export def install-module [] { } export def install-custom [] { - with-nupm-home { + with-test-env { nupm install --path tests/packages/spam_custom assert installed [plugins nu_plugin_test] @@ -52,19 +62,19 @@ export def install-from-local-registry [] { assert ($contents | str contains '0.2.0') } - with-nupm-home { + with-test-env { $env.NUPM_REGISTRIES = {} nupm install --registry $TEST_REGISTRY_PATH spam_script check-file } - with-nupm-home { + with-test-env { $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } nupm install --registry test spam_script check-file } - with-nupm-home { + with-test-env { $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } nupm install spam_script check-file From b546082ff0dc634e8f049330739c7fba3d575302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 13:33:54 +0200 Subject: [PATCH 16/26] Fix word --- nupm/mod.nu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nupm/mod.nu b/nupm/mod.nu index 9ad2aad..accc715 100644 --- a/nupm/mod.nu +++ b/nupm/mod.nu @@ -16,7 +16,7 @@ export-env { # Ensure install cache is set $env.NUPM_CACHE = ($env.NUPM_CACHE? | default $DEFAULT_NUPM_CACHE) - # TODO: Maybe this is not the best way to store registries, but should be + # TODO: Maybe this is not the best way to set registries, but should be # good enough for now. # TODO: Add `nupm registry` for showing info about registries # TODO: Add `nupm registry add/remove` to add/remove registry from the env From 5bcef1ada8e5fdf3e6f8931b015b0ef04f1e933a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 14:36:13 +0200 Subject: [PATCH 17/26] Add `nupm search` for searching packages --- nupm/install.nu | 146 ++++++++++++----------------------------- nupm/mod.nu | 1 + nupm/search.nu | 12 ++++ nupm/utils/registry.nu | 79 ++++++++++++++++++++++ 4 files changed, 133 insertions(+), 105 deletions(-) create mode 100644 nupm/search.nu create mode 100644 nupm/utils/registry.nu diff --git a/nupm/install.nu b/nupm/install.nu index e1774f4..e6a3925 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -4,6 +4,7 @@ use utils/completions.nu complete-registries use utils/dirs.nu [ nupm-home-prompt cache-dir module-dir script-dir tmp-dir ] use utils/log.nu throw-error use utils/misc.nu check-cols +use utils/registry.nu search-package use utils/version.nu filter-by-version def open-package-file [dir: path] { @@ -149,110 +150,6 @@ def install-path [ } } -def fetch-package [ - package: string # Name of the package - --registry: string # Which registry to use - --version: any # Package version to install (string or null) -] { - let registries = if (not ($registry | is-empty)) and ($registry in $env.NUPM_REGISTRIES) { - # If $registry is a valid column in $env.NUPM_REGISTRIES, use that - { $registry : ($env.NUPM_REGISTRIES | get $registry) } - } else if (not ($registry | is-empty)) and ($registry | path exists) { - # If $registry is a path, use that - let reg_name = $registry | path parse | get stem - { $reg_name: $registry } - } else { - # Otherwise use $env.NUPM_REGISTRIES - $env.NUPM_REGISTRIES - } - - # Collect all registries matching the package and all matching packages - let regs = $registries - | items {|name, path| - print $path - - # Open registry (online or offline) - let registry = if ($path | path type) == file { - open $path - } else { - try { - let reg = http get $path - - if local in $reg { - throw-error ("Can't have local packages in online registry" - + $" '($path)'.") - } - - $reg - } catch { - throw-error $"Cannot open '($path)' as a file or URL." - } - } - - $registry | check-cols --missing-ok "registry" [ git local ] | ignore - - # Find all packages matching $package in the registry - let pkgs_local = $registry.local? - | default [] - | check-cols "local packages" [ name version path ] - | where name == $package - - let pkgs_git = $registry.git? - | default [] - | check-cols "git packages" [ name version url revision path ] - | where name == $package - - # Detect duplicate versions - let all_versions = $pkgs_local.version? - | default [] - | append ($pkgs_git.version? | default []) - - if ($all_versions | uniq | length) != ($all_versions | length) { - throw-error ($'Duplicate versions of package ($package) detected' - + $' in registry ($name).') - } - - let pkgs = $pkgs_local - | insert type local - | insert url null - | insert revision null - | append ($pkgs_git | insert type git) - - { - name: $name - path: $path - pkgs: $pkgs - } - } - | compact - - if ($regs | is-empty) { - throw-error 'No registries found' - } else if ($regs | length) > 1 { - # TODO: Here could be interactive prompt - throw-error 'Multiple registries contain the same package' - } - - # Now, only one registry contains the package - let reg = $regs | first - let pkgs = $reg.pkgs | filter-by-version $version - - let pkg = try { - $pkgs | last - } catch { - throw-error $'No package matching version `($version)`' - } - - print $pkg - - if $pkg.type == 'git' { - download-pkg $pkg - } else { - # local package path is relative to the registry file (absolute paths - # are discouraged but work) - $reg.path | path dirname | path join $pkg.path - } -} # Downloads a package and returns its downloaded path def download-pkg [ @@ -310,6 +207,45 @@ def download-pkg [ $pkg_dir } +# Fetch a package from a registry +def fetch-package [ + package: string # Name of the package + --registry: string # Which registry to use + --version: string # Package version to install (string or null) +]: nothing -> path { + let regs = (search-package $package + --registry $registry + --version $version + --exact-match) + + if ($regs | is-empty) { + throw-error 'No registries found' + } else if ($regs | length) > 1 { + # TODO: Here could be interactive prompt + throw-error 'Multiple registries contain the same package' + } + + # Now, only one registry contains the package + let reg = $regs | first + let pkgs = $reg.pkgs | filter-by-version $version + + let pkg = try { + $pkgs | last + } catch { + throw-error $'No package matching version `($version)`' + } + + print $pkg + + if $pkg.type == 'git' { + download-pkg $pkg + } else { + # local package path is relative to the registry file (absolute paths + # are discouraged but work) + $reg.path | path dirname | path join $pkg.path + } +} + # Install a nupm package # # Installation consists of two parts: @@ -328,7 +264,7 @@ export def main [ return } - let pkg = if not $path { + let pkg: path = if not $path { fetch-package $package --registry $registry --version $pkg_version } else { if $pkg_version != null { diff --git a/nupm/mod.nu b/nupm/mod.nu index accc715..26cbf48 100644 --- a/nupm/mod.nu +++ b/nupm/mod.nu @@ -4,6 +4,7 @@ use utils/dirs.nu [ export module install.nu export module test.nu +export module search.nu export-env { # Ensure that $env.NUPM_HOME is always set when running nupm. Any missing diff --git a/nupm/search.nu b/nupm/search.nu new file mode 100644 index 0000000..4695826 --- /dev/null +++ b/nupm/search.nu @@ -0,0 +1,12 @@ +use utils/completions.nu complete-registries +use utils/registry.nu search-package + +# Search for a package +export def main [ + package # Name, path, or link to the package + --registry: string@complete-registries # Which registry to use (either a name + # in $env.NUPM_REGISTRIES or a path) + --pkg-version(-v): string # Package version to install +]: nothing -> table { + search-package $package --registry $registry --version $pkg_version +} diff --git a/nupm/utils/registry.nu b/nupm/utils/registry.nu new file mode 100644 index 0000000..15a95ee --- /dev/null +++ b/nupm/utils/registry.nu @@ -0,0 +1,79 @@ +# Utilities related to nupm registries + +# Search for a package in a registry +export def search-package [ + package: string # Name of the package + --registry: string # Which registry to use + --version: any # Package version to install (string or null) + --exact-match # Searched package name must match exactly +] -> table { + let registries = if (not ($registry | is-empty)) and ($registry in $env.NUPM_REGISTRIES) { + # If $registry is a valid column in $env.NUPM_REGISTRIES, use that + { $registry : ($env.NUPM_REGISTRIES | get $registry) } + } else if (not ($registry | is-empty)) and ($registry | path exists) { + # If $registry is a path, use that + let reg_name = $registry | path parse | get stem + { $reg_name: $registry } + } else { + # Otherwise use $env.NUPM_REGISTRIES + $env.NUPM_REGISTRIES + } + + let name_matcher: closure = if $exact_match { + {|row| $row.name == $package } + } else { + {|row| $package in $row.name } + } + + # Collect all registries matching the package and all matching packages + let regs = $registries + | items {|name, path| + print $path + + # Open registry (online or offline) + let registry = if ($path | path type) == file { + open $path + } else { + try { + let reg = http get $path + + if local in $reg { + throw-error ("Can't have local packages in online registry" + + $" '($path)'.") + } + + $reg + } catch { + throw-error $"Cannot open '($path)' as a file or URL." + } + } + + $registry | check-cols --missing-ok "registry" [ git local ] | ignore + + # Find all packages matching $package in the registry + let pkgs_local = $registry.local? + | default [] + | check-cols "local packages" [ name version path ] + | filter $name_matcher + + let pkgs_git = $registry.git? + | default [] + | check-cols "git packages" [ name version url revision path ] + | filter $name_matcher + + let pkgs = $pkgs_local + | insert type local + | insert url null + | insert revision null + | append ($pkgs_git | insert type git) + + { + name: $name + path: $path + pkgs: $pkgs + } + } + | compact + + $regs | where not ($it.pkgs | is-empty) +} From b4385de140d874e35676bb4eaf8c0d1e360a4020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 14:40:11 +0200 Subject: [PATCH 18/26] Remove registry print --- nupm/utils/registry.nu | 2 -- 1 file changed, 2 deletions(-) diff --git a/nupm/utils/registry.nu b/nupm/utils/registry.nu index 15a95ee..5ad32cb 100644 --- a/nupm/utils/registry.nu +++ b/nupm/utils/registry.nu @@ -28,8 +28,6 @@ export def search-package [ # Collect all registries matching the package and all matching packages let regs = $registries | items {|name, path| - print $path - # Open registry (online or offline) let registry = if ($path | path type) == file { open $path From 40a533d564ae1def94ab3bfff73ffce2f7ba8ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 14:48:35 +0200 Subject: [PATCH 19/26] Improve error message --- nupm/install.nu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index e6a3925..42912eb 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -98,8 +98,8 @@ def install-path [ if ($destination | path type) == dir { throw-error "package_already_installed" ( - $"Package ($package.name) is already installed." - + "Use `--force` to override the package" + $"Package ($package.name) is already installed in" + + $" ($destination). Use `--force` to override the package" ) } From 0ab10c57a1bef6a881e111ca3374f96387fd3faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 17:49:27 +0200 Subject: [PATCH 20/26] Test nupm search --- tests/mod.nu | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/mod.nu b/tests/mod.nu index 7f30c83..4bd1fa2 100644 --- a/tests/mod.nu +++ b/tests/mod.nu @@ -80,3 +80,10 @@ export def install-from-local-registry [] { check-file } } + +export def search-registry [] { + with-test-env { + $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } + assert ((nupm search spam | get pkgs.0 | length) == 4) + } +} From 7d28528a79fa8ac968a1584b58d4b118dbe6bb46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 17:50:46 +0200 Subject: [PATCH 21/26] Add registry setting to test env --- tests/mod.nu | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/mod.nu b/tests/mod.nu index 4bd1fa2..a342bae 100644 --- a/tests/mod.nu +++ b/tests/mod.nu @@ -10,11 +10,13 @@ def with-test-env [closure: closure]: nothing -> nothing { let home = tmp-dir nupm_test --ensure let cache = tmp-dir 'nupm_test/cache' --ensure let temp = tmp-dir 'nupm_test/temp' --ensure + let reg = { test: $TEST_REGISTRY_PATH } with-env { NUPM_HOME: $home NUPM_CACHE: $cache NUPM_TEMP: $temp + NUPM_REGISTRIES: $reg } $closure rm --recursive $temp @@ -69,13 +71,11 @@ export def install-from-local-registry [] { } with-test-env { - $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } nupm install --registry test spam_script check-file } with-test-env { - $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } nupm install spam_script check-file } @@ -83,7 +83,6 @@ export def install-from-local-registry [] { export def search-registry [] { with-test-env { - $env.NUPM_REGISTRIES = { test: $TEST_REGISTRY_PATH } assert ((nupm search spam | get pkgs.0 | length) == 4) } } From 368294d4b814aed72f794f52645d1d3616c0e1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 18:34:07 +0200 Subject: [PATCH 22/26] Add more installation tests; Better errors --- nupm/install.nu | 8 ++- tests/mod.nu | 52 ++++++++++++++++--- tests/packages/registry.nuon | 12 +---- tests/packages/spam_script_old/nupm.nuon | 5 ++ tests/packages/spam_script_old/spam_script.nu | 4 ++ 5 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 tests/packages/spam_script_old/nupm.nuon create mode 100755 tests/packages/spam_script_old/spam_script.nu diff --git a/nupm/install.nu b/nupm/install.nu index 42912eb..e20d7a4 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -8,6 +8,12 @@ use utils/registry.nu search-package use utils/version.nu filter-by-version def open-package-file [dir: path] { + if not ($dir | path exists) { + throw-error "package_dir_does_not_exist" ( + $"Package directory ($dir) does not exist" + ) + } + let package_file = $dir | path join "nupm.nuon" if not ($package_file | path exists) { @@ -219,7 +225,7 @@ def fetch-package [ --exact-match) if ($regs | is-empty) { - throw-error 'No registries found' + throw-error $'Package ($package) not found in any registry' } else if ($regs | length) > 1 { # TODO: Here could be interactive prompt throw-error 'Multiple registries contain the same package' diff --git a/tests/mod.nu b/tests/mod.nu index a342bae..a5c8046 100644 --- a/tests/mod.nu +++ b/tests/mod.nu @@ -31,6 +31,12 @@ def "assert installed" [path_tokens: list] { assert ($path_tokens | prepend $env.NUPM_HOME | path join | path exists) } +def check-file-content [content: string] { + let file_str = open ($env.NUPM_HOME | path join scripts spam_script.nu) + assert ($file_str | str contains $content) +} + + export def install-script [] { with-test-env { nupm install --path tests/packages/spam_script @@ -59,25 +65,55 @@ export def install-custom [] { } export def install-from-local-registry [] { - def check-file [] { - let contents = open ($env.NUPM_HOME | path join scripts spam_script.nu) - assert ($contents | str contains '0.2.0') - } - with-test-env { $env.NUPM_REGISTRIES = {} nupm install --registry $TEST_REGISTRY_PATH spam_script - check-file + check-file-content 0.2.0 } with-test-env { nupm install --registry test spam_script - check-file + check-file-content 0.2.0 } with-test-env { nupm install spam_script - check-file + check-file-content 0.2.0 + } +} + +export def install-with-version [] { + with-test-env { + nupm install spam_script -v 0.1.0 + check-file-content 0.1.0 + } +} + +export def install-multiple-registries-fail [] { + with-test-env { + $env.NUPM_REGISTRIES.test2 = $TEST_REGISTRY_PATH + + let out = try { + nupm install spam_script + "wrong value that shouldn't match the assert below" + } catch {|err| + $err.msg + } + + assert ("Multiple registries contain the same package" in $out) + } +} + +export def install-package-not-found [] { + with-test-env { + let out = try { + nupm install invalid-package + "wrong value that shouldn't match the assert below" + } catch {|err| + $err.msg + } + + assert ("Package invalid-package not found in any registry" in $out) } } diff --git a/tests/packages/registry.nuon b/tests/packages/registry.nuon index 1e08a89..5eed899 100644 --- a/tests/packages/registry.nuon +++ b/tests/packages/registry.nuon @@ -1,19 +1,9 @@ # Testing registry for testing packages { - git: [ - [name version url revision path]; - [ - spam_script - 0.1.0 - https://github.com/nushell/nupm.git - e46e2a93adacf1b8daec83882dc0aa9437a0a7b4 - tests/packages/spam_script - ] - ] - local: [ [name version path]; [spam_script 0.2.0 spam_script] + [spam_script 0.1.0 spam_script_old] [spam_custom 0.1.0 spam_custom] [spam_module 0.1.0 spam_module] ] diff --git a/tests/packages/spam_script_old/nupm.nuon b/tests/packages/spam_script_old/nupm.nuon new file mode 100644 index 0000000..1612782 --- /dev/null +++ b/tests/packages/spam_script_old/nupm.nuon @@ -0,0 +1,5 @@ +{ + name: spam_script, + type: script, + version: "0.1.0", +} diff --git a/tests/packages/spam_script_old/spam_script.nu b/tests/packages/spam_script_old/spam_script.nu new file mode 100755 index 0000000..3f37512 --- /dev/null +++ b/tests/packages/spam_script_old/spam_script.nu @@ -0,0 +1,4 @@ +#!/usr/bin/env nu +def main [] { + "Hello world v0.1.0!" +} From 3aa27a42f6d82f1fe9c8ca3a87bef726a65ab297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 18:36:37 +0200 Subject: [PATCH 23/26] Improve error message --- nupm/install.nu | 2 +- tests/mod.nu | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nupm/install.nu b/nupm/install.nu index e20d7a4..5fd5e7a 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -228,7 +228,7 @@ def fetch-package [ throw-error $'Package ($package) not found in any registry' } else if ($regs | length) > 1 { # TODO: Here could be interactive prompt - throw-error 'Multiple registries contain the same package' + throw-error $'Multiple registries contain package ($package)' } # Now, only one registry contains the package diff --git a/tests/mod.nu b/tests/mod.nu index a5c8046..9d40733 100644 --- a/tests/mod.nu +++ b/tests/mod.nu @@ -100,7 +100,7 @@ export def install-multiple-registries-fail [] { $err.msg } - assert ("Multiple registries contain the same package" in $out) + assert ("Multiple registries contain package spam_script" in $out) } } From ef14a6a6253407208038fe534cffa3b48d3c1e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sun, 28 Jan 2024 18:39:53 +0200 Subject: [PATCH 24/26] Improve message --- nupm/install.nu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nupm/install.nu b/nupm/install.nu index 5fd5e7a..a6c5cc4 100644 --- a/nupm/install.nu +++ b/nupm/install.nu @@ -188,7 +188,7 @@ def download-pkg [ let pkg_dir = $env.PWD | path join $clone_dir $pkg.path if ($pkg_dir | path exists) { - print 'Found package in cache' + print $'Package ($pkg.name) found in cache' return $pkg_dir } From fadfac2dcac7811f08037367ce8b5d2dab2e4f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Wed, 31 Jan 2024 20:11:59 +0200 Subject: [PATCH 25/26] Use online registry by default --- nupm/mod.nu | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nupm/mod.nu b/nupm/mod.nu index 26cbf48..db5ad4a 100644 --- a/nupm/mod.nu +++ b/nupm/mod.nu @@ -20,10 +20,9 @@ export-env { # TODO: Maybe this is not the best way to set registries, but should be # good enough for now. # TODO: Add `nupm registry` for showing info about registries - # TODO: Add `nupm registry add/remove` to add/remove registry from the env + # TODO: Add `nupm registry add/remove` to add/remove registry from the env? $env.NUPM_REGISTRIES = { - # TODO: Change the registry to a URL - "nupm-test": ($env.FILE_PWD | path join registry.nuon) + nupm_test: 'https://raw.githubusercontent.com/nushell/nupm/main/registry.nuon' } } From b410ae9dc826716ed54bef2d8574c5bdda54540c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Wed, 31 Jan 2024 20:16:49 +0200 Subject: [PATCH 26/26] Remove registry file (comitted separately) --- registry.nuon | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 registry.nuon diff --git a/registry.nuon b/registry.nuon deleted file mode 100644 index d2c09d5..0000000 --- a/registry.nuon +++ /dev/null @@ -1,46 +0,0 @@ -# Registry is a NUON file that tells nupm where to look for packages -# -# Currently, these package types are supported: -# * git ..... package is an online git repository -# * local ... package is on a local filesystem -# -# The package type defines the columns of the registry record. Each column holds -# holds a table of packages. -# -# Git packages accept the following columns: -# * name ....... name of the package -# * version .... version of the package -# * url ........ Git URL of the package -# * revision ... Git revision to check out when installing the package (commit -# hash, version, branch name, ...) -# * path ....... Path relative to the repository root where to look for nupm.nuon -# -# Local packages accept the following columns: -# * name ....... name of the package -# * version .... version of the package -# * path ....... Path where to look for nupm.nuon. It can be absolute, or -# relative. If relative, then relative to the registry file. -# -# One registry can contain packages with the same name, but they must be of -# different version. - -{ - git: [ - [name version url revision path]; - [ - nu-git-manager - 0.5.0 - https://github.com/amtoine/nu-git-manager.git - 0.5.0 - pkgs/nu-git-manager - ] - - [ - nu-git-manager-sugar - 0.5.0 - https://github.com/amtoine/nu-git-manager.git - 0.5.0 - pkgs/nu-git-manager-sugar - ] - ] -}