Rename picnit to nitpm and intro 4 main new features.
### Install package versions
Install specific versions of a package using the following command:
~~~
nitpm install gamnit=0.5
~~~
The version string (the `0.5` in the above example) must be a Git branch or tag, it will be used when cloning the package locally. The package will be downloaded to `~/.local/lib/nit/gamnit=0.5/`, allowing multiple versions of the same package to be installed concurrently.
### Dependencies in package.ini
Packages should now declare dependencies to other nitpm packages in the `package.ini` at the `import` key:
~~~
[package]
name=my_package
import=hello_nitpm, gamnit=0.5
~~~
The dependencies can then be installed automatically with `nitpm install` from the root of the package.
Nit tools read the local `package.ini` to redirect imports of `gamnit` inside this package to this specific version. So for `my_package` described above, all references to `gamnit` will use the implementation `gamnit=0.5`.
### Recursive installation
nitpm installs dependencies recursively, so if `gamnit` requires `glesv2`, after an explicit command to install `gamnit` nitpm will also install `glesv2`. This implementation is minimal, it could be improved by precalculating all dependencies and asking for confirmation.
### Customizable install directory
You can now use the env var `NITPM_PATH` to set the path where libraries are installed. This will override the default path at `~/.local/lib/nit/`.
### Others
* `nitpm uninstall` can uninstall many packages at once, it is safer and it accepts the -f option to skip the confirmation.
* `nitpm list` lists packages in alphabetical order.
Pull-Request: #2622
Reviewed-by: Romain Chanoir <romain.chanoir@viacesi.fr>
Reviewed-by: Jean Privat <jean@pryen.org>
--- /dev/null
+# NAME
+
+nitpm - Nit package manager
+
+# SYNOPSIS
+
+nitpm [--help] [--verbose] <command> [<args>]
+
+# OPTIONS
+
+### `-h`, `--help`
+Show help message.
+
+### `-v`, `--verbose`
+Print more information.
+
+# COMMANDS
+
+### `install`
+Install packages by name, Git repository address or from the local package.ini.
+
+ # Search and install package by name, e.g. hello_nitpm:
+ nitpm install hello_nitpm
+
+ # Install package from a Git repository:
+ nitpm install https://gitlab.com/xymus/hello_nitpm.git
+
+ # Search and install a specific branch or tag of a package, e.g. 1.0:
+ nitpm install nitpm_test_versions=1.0
+
+ # Install all packages declared in the local package.ini file:
+ nitpm install
+
+To support the last command, the package.ini file should list the required packages after `[package]` on an `import=` line:
+
+ [package]
+ name=my_package
+ import=nitpm_test_versions=1.0,gamnit
+
+### `list`
+List installed packages.
+
+ nitpm list
+
+### `upgrade`
+Upgrade a package.
+
+ nitpm upgrade hello_nitpm
+
+### `uninstall`
+Uninstall packages.
+
+ nitpm uninstall hello_nitpm
+
+### `help`
+Show general help message or the help for a command.
+
+ nitpm help
+ nitpm help install
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
+++ /dev/null
-# NAME
-
-picnit - Nit package manager
-
-# SYNOPSIS
-
-picnit [--help] [--verbose] <command> [<args>]
-
-# OPTIONS
-
-### `-h`, `--help`
-
-Show the help message.
-
-### `-v`, `--verbose`
-
-Print more information, may be useful for debugging.
-
-# COMMANDS
-
-### install
-
-Install a package by searching for its name or directly from a Git repository URL.
-
- picnit install hello_picnit
- picnit install https://gitlab.com/xymus/hello_picnit.git
-
-### list
-
-List installed packages.
-
- picnit list
-
-### upgrade
-
-Upgrade a package.
-
- picnit upgrade hello_picnit
-
-### uninstall
-
-Uninstall a package.
-
- picnit uninstall hello_picnit
-
-### help
-
-Show general help message or the help for a command.
-
- picnit help
- picnit help install
-
-# SEE ALSO
-
-The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
NITCOPT=--semi-global
OLDNITCOPT=--semi-global
-OBJS=nitc nitpick nit nitls nitunit picnit nitx nitlight nitserial nitrestful
+OBJS=nitc nitpick nit nitls nitunit nitpm nitx nitlight nitserial nitrestful
SRCS=$(patsubst %,%.nit,$(OBJS))
BINS=$(patsubst %,../bin/%,$(OBJS))
MOREOBJS=nitdoc nitweb nitcatalog nitmetrics nitpretty nitweb
import modelbuilder_base
import ini
-import picnit_shared
+import nitpm_shared
redef class ToolContext
# Option --path
# Setup the paths value
paths.append(toolcontext.opt_path.value)
- # Packages managed by picnit, only use when not testing with tests.sh
+ # Packages managed by nitpm, only use when not testing with tests.sh
if "NIT_TESTING_TESTS_SH".environ != "true" then
- paths.add picnit_lib_dir
+ paths.add nitpm_lib_dir
end
var path_env = "NIT_PATH".environ
end
end
+ if mgroup != null then
+ var alias = mgroup.mpackage.import_alias(name)
+ if alias != null then name = alias
+ end
+
var loc = null
if anode != null then loc = anode.hot_location
var candidate = search_module_in_paths(loc, name, lookpaths)
# If found, the module is returned.
private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable MModule
do
+ var name_no_version
+ if name.has('=') then
+ name_no_version = name.split('=').first
+ else name_no_version = name
+
var res = new ArraySet[MModule]
for dirname in lookpaths do
# Try a single module file
var g = identify_group((dirname/name).simplify_path)
if g != null then
scan_group(g)
- res.add_all g.mmodules_by_name(name)
+ res.add_all g.mmodules_by_name(name_no_version)
end
end
if res.is_empty then return null
# Resolve the module identification for a given `AModuleName`.
#
# This method handles qualified names as used in `AModuleName`.
- fun seach_module_by_amodule_name(n_name: AModuleName, mgroup: nullable MGroup): nullable MModule
+ fun search_module_by_amodule_name(n_name: AModuleName, mgroup: nullable MGroup): nullable MModule
do
var mod_name = n_name.n_id.text
# If no module yet, then assume that the first element of the path
# Is to be searched in the path.
var root_name = n_name.n_path.first.text
+
+ # Search for an alias in required external packages
+ if mgroup != null then
+ var alias = mgroup.mpackage.import_alias(root_name)
+ if alias != null then root_name = alias
+ end
+
var roots = search_group_in_paths(root_name, paths)
if roots.is_empty then
error(n_name, "Error: cannot find `{root_name}`. Tried: {paths.join(", ")}.")
# Basically it check that `bar::foo` matches `bar/foo.nit` and `bar/baz/foo.nit`
# but not `baz/foo.nit` nor `foo/bar.nit`
#
- # Is used by `seach_module_by_amodule_name` to validate qualified names.
+ # Is used by `search_module_by_amodule_name` to validate qualified names.
private fun match_amodulename(n_name: AModuleName, m: MModule): Bool
do
var g: nullable MGroup = m.mgroup
end
# Load the imported module
- var sup = seach_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
+ var sup = search_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
if sup == null then
mmodule.is_broken = true
nmodule.mmodule = null # invalidate the module
var atconditionals = aimport.get_annotations("conditional")
if atconditionals.is_empty then continue
- var suppath = seach_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
+ var suppath = search_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
if suppath == null then continue # skip error
for atconditional in atconditionals do
end
return true
end
+
+ # Get the name to search for, for a `root_name` declared as `import` in `ini`
+ fun import_alias(root_name: String): nullable String
+ do
+ var map = import_aliases_parsed
+ if map == null then return null
+
+ var val = map.get_or_null(root_name)
+ if val == null then return null
+
+ return val.dir_name
+ end
+
+ private var import_aliases_parsed: nullable Map[String, ExternalPackage] is lazy do
+ var ini = ini
+ if ini == null then return null
+
+ var import_line = ini["package.import"]
+ if import_line == null then return null
+
+ var map = import_line.parse_import
+ if map.is_empty then return null
+
+ return map
+ end
end
redef class MGroup
# limitations under the License.
# Nit package manager command line interface
-module picnit
+module nitpm
import opts
import prompt
import ini
import curl
-import picnit_shared
-
-redef class Text
-
- # Does `self` look like a package name?
- #
- # ~~~
- # assert "gamnit".is_package_name
- # assert "n1t".is_package_name
- # assert not ".".is_package_name
- # assert not "./gamnit".is_package_name
- # assert not "https://github.com/nitlang/nit.git".is_package_name
- # assert not "git://github.com/nitlang/nit".is_package_name
- # assert not "git@gitlab.com:xymus/gamnit.git".is_package_name
- # assert not "4it".is_package_name
- # ~~~
- private fun is_package_name: Bool
- do
- if is_empty then return false
- if not chars.first.is_alpha then return false
-
- for c in chars do
- if not (c.is_alphanumeric or c == '_') then return false
- end
-
- return true
- end
+import nitpm_shared
- # Get package name from the Git address `self`
- #
- # Return `null` on failure.
- #
- # ~~~
- # assert "https://github.com/nitlang/nit.git".git_name == "nit"
- # assert "git://github.com/nitlang/nit".git_name == "nit"
- # assert "gamnit".git_name == "gamnit"
- # assert "///".git_name == null
- # assert "file:///".git_name == "file:"
- # ~~~
- private fun git_name: nullable String
- do
- var parts = split("/")
- for part in parts.reverse_iterator do
- if not part.is_empty then
- return part.strip_extension(".git")
- end
- end
-
- return null
- end
-end
-
-# Command line action, passed after `picnit`
+# Command line action, passed after `nitpm`
abstract class Command
# Short name of the command, specified in the command line
super Command
redef fun name do return "install"
- redef fun usage do return "picnit install [package or git-repository]"
- redef fun description do return "Install a package by its name or from a git-repository"
+ redef fun usage do return "nitpm install [package0[=version] [package1 ...]]"
+ redef fun description do return "Install packages by name, Git repository address or from the local package.ini"
+
+ # Packages installed in this run (identified by the full path)
+ private var installed = new Array[String]
redef fun apply(args)
do
- if args.length != 1 then
- print_local_help
- exit 1
+ if args.not_empty then
+ # Install each package
+ for arg in args do
+ # Parse each arg as an import string, with versions and commas
+ install_packages arg
+ end
+ else
+ # Install packages from local package.ini
+ var ini_path = "package.ini"
+ if not ini_path.file_exists then
+ print_error "Local `package.ini` not found."
+ print_local_help
+ exit 1
+ end
+
+ var ini = new ConfigTree(ini_path)
+ var import_line = ini["package.import"]
+ if import_line == null then
+ print_error "The local `package.ini` declares no external dependencies."
+ exit 0
+ abort
+ end
+
+ install_packages import_line
+ end
+ end
+
+ # Install packages defined by the `import_line`
+ private fun install_packages(import_line: String)
+ do
+ var imports = import_line.parse_import
+ for name, ext_package in imports do
+ install_package(ext_package.id, ext_package.version)
end
+ end
- var package_id = args.first
+ # Install the `package_id` at `version`
+ private fun install_package(package_id: String, version: nullable String)
+ do
if package_id.is_package_name then
# Ask a centralized server
# TODO choose a future safe URL
# TODO customizable server list
# TODO parse ini file in memory
- var url = "https://xymus.net/picnit/{package_id}.ini"
+ var url = "https://xymus.net/nitpm/{package_id}.ini"
var ini_path = "/tmp/{package_id}.ini"
if verbose then print "Looking for a package description at '{url}'"
assert response isa CurlFileResponseSuccess
if response.status_code == 404 then
- print_error "Package not found by the server"
+ print_error "Package '{package_id}' not found on the server"
exit 1
else if response.status_code != 200 then
print_error "Server side error: {response.status_code}"
abort
end
- install_from_git(git_repo, package_id)
+ install_from_git(git_repo, package_id, version)
else
var name = package_id.git_name
if name != null and name != "." and not name.is_empty then
name = name.to_lower
- install_from_git(package_id, name)
+ install_from_git(package_id, name, version)
else
print_error "Failed to infer the package name"
exit 1
end
end
- private fun install_from_git(git_repo, name: String)
+ private fun install_from_git(git_repo, name: String, version: nullable String)
do
check_git
- var target_dir = picnit_lib_dir / name
- if target_dir.file_exists then
- print_error "Already installed"
- exit 1
+ var target_dir = nitpm_lib_dir / name
+ if version != null then target_dir += "=" + version
+ if installed.has(target_dir) then
+ # Ignore packages installed in this run
+ return
end
+ installed.add target_dir
- var cmd = "git clone {git_repo.escape_to_sh} {target_dir.escape_to_sh}"
- if verbose then print "+ {cmd}"
+ if target_dir.file_exists then
+ # Warn about packages previously installed,
+ # install dependencies anyway in case of a previous error.
+ print_error "Package '{name}' is already installed"
+ else
+ # Actually install it
+ var cmd_branch = ""
+ if version != null then cmd_branch = "--branch '{version}'"
- if "NIT_TESTING".environ == "true" then
- # Silence git output when testing
- cmd += " 2> /dev/null"
- end
+ var cmd = "git clone --depth 1 {cmd_branch} {git_repo.escape_to_sh} {target_dir.escape_to_sh}"
+ if verbose then print "+ {cmd}"
- var proc = new Process("sh", "-c", cmd)
- proc.wait
+ if "NIT_TESTING".environ == "true" then
+ # Silence git output when testing
+ cmd += " 2> /dev/null"
+ end
- if proc.status != 0 then
- print_error "Install failed"
- exit 1
+ var proc = new Process("sh", "-c", cmd)
+ proc.wait
+
+ if proc.status != 0 then
+ print_error "Install of '{name}' failed"
+ exit 1
+ end
+ end
+
+ # Recursive install
+ var ini = new ConfigTree(target_dir/"package.ini")
+ var import_line = ini["package.import"]
+ if import_line != null then
+ install_packages import_line
end
end
end
super Command
redef fun name do return "upgrade"
- redef fun usage do return "picnit upgrade <package>"
+ redef fun usage do return "nitpm upgrade <package>"
redef fun description do return "Upgrade a package"
redef fun apply(args)
end
var name = args.first
- var target_dir = picnit_lib_dir / name
+ var target_dir = nitpm_lib_dir / name
if not target_dir.file_exists or not target_dir.to_path.is_dir then
print_error "Package not found"
super Command
redef fun name do return "uninstall"
- redef fun usage do return "picnit uninstall <package>"
- redef fun description do return "Uninstall a package"
+ redef fun usage do return "nitpm uninstall [-f] <package0>[=version] [package1 ...]"
+ redef fun description do return "Uninstall packages"
redef fun apply(args)
do
- if args.length != 1 then
+ var opt_force = "-f"
+ var force = args.has(opt_force)
+ if force then args.remove(opt_force)
+
+ if args.is_empty then
print_local_help
exit 1
end
- var name = args.first
- var target_dir = picnit_lib_dir / name
+ for name in args do
- if not target_dir.file_exists or not target_dir.to_path.is_dir then
- print_error "Package not found"
- exit 1
- end
+ var clean_nitpm_lib_dir = nitpm_lib_dir.simplify_path
+ var target_dir = clean_nitpm_lib_dir / name
+
+ # Check validity of the package to delete
+ target_dir = target_dir.simplify_path
+ var within_dir = target_dir.has_prefix(clean_nitpm_lib_dir + "/") and
+ target_dir.length > clean_nitpm_lib_dir.length + 1
+ var valid_name = name.length > 0 and name.chars.first.is_lower
+ if not valid_name or not within_dir then
+ print_error "Package name '{name}' is invalid"
+ continue
+ end
- # Ask confirmation
- var response = prompt("Delete {target_dir.escape_to_sh}? [Y/n] ")
- var accept = response != null and
- (response.to_lower == "y" or response.to_lower == "yes" or response == "")
- if not accept then return
+ if not target_dir.file_exists or not target_dir.to_path.is_dir then
+ print_error "Package not found"
+ exit 1
+ end
- var cmd = "rm -rf {target_dir.escape_to_sh}"
- if verbose then print "+ {cmd}"
+ # Ask confirmation
+ if not force then
+ var response = prompt("Delete {target_dir.escape_to_sh}? [Y/n] ")
+ var accept = response != null and
+ (response.to_lower == "y" or response.to_lower == "yes" or response == "")
+ if not accept then return
+ end
- var proc = new Process("sh", "-c", cmd)
- proc.wait
+ var cmd = "rm -rf {target_dir.escape_to_sh}"
+ if verbose then print "+ {cmd}"
- if proc.status != 0 then
- print_error "Uninstall failed"
- exit 1
+ var proc = new Process("sh", "-c", cmd)
+ proc.wait
+
+ if proc.status != 0 then
+ print_error "Uninstall failed"
+ exit 1
+ end
end
end
end
super Command
redef fun name do return "list"
- redef fun usage do return "picnit list"
+ redef fun usage do return "nitpm list"
redef fun description do return "List installed packages"
redef fun apply(args)
do
- var files = picnit_lib_dir.files
+ var files = nitpm_lib_dir.files
+ var name_to_desc = new Map[String, nullable String]
+ var max_name_len = 0
+
+ # Collect package info
for file in files do
- var ini_path = picnit_lib_dir / file / "package.ini"
+ var ini_path = nitpm_lib_dir / file / "package.ini"
if verbose then print "- Reading ini file at {ini_path}"
var ini = new ConfigTree(ini_path)
var tags = ini["package.tags"]
- if tags != null then
- print "{file.justify(15, 0.0)} {tags}"
- else
- print file
- end
+ name_to_desc[file] = tags
+ max_name_len = max_name_len.max(file.length)
+ end
+
+ # Sort in alphabetical order
+ var sorted_names = name_to_desc.keys.to_a
+ alpha_comparator.sort sorted_names
+
+ # Print with clear columns
+ for name in sorted_names do
+ var col0 = name.justify(max_name_len+1, 0.0)
+ var col1 = name_to_desc[name] or else ""
+ var line = col0 + col1
+ print line.trim
end
end
end
super Command
redef fun name do return "help"
- redef fun usage do return "picnit help [command]"
+ redef fun usage do return "nitpm help [command]"
redef fun description do return "Show general help message or the help for a command"
redef fun apply(args)
var opts = new OptionContext
# Help option
- var opt_help = new OptionBool("Show this help message", "--help", "-h")
+ var opt_help = new OptionBool("Show help message", "-h", "--help")
# Verbose mode option
- var opt_verbose = new OptionBool("Print more information", "--verbose", "-v")
+ var opt_verbose = new OptionBool("Print more information", "-v", "--verbose")
private fun verbose: Bool do return opt_verbose.value
# All command line actions, mapped to their short `name`
private var command_help = new CommandHelp(commands)
end
-redef fun picnit_lib_dir
+redef fun nitpm_lib_dir
do
if "NIT_TESTING".environ == "true" then
- return "/tmp/picnit-test-" + "NIT_TESTING_ID".environ
+ return "/tmp/nitpm-test-" + "NIT_TESTING_ID".environ
else return super
end
# Print the general help message
private fun print_help
do
- print "usage: picnit <command> [options]"
+ print "usage: nitpm <command> [options]"
print ""
print "commands:"
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services related to the Nit package manager
+module nitpm_shared
+
+# Folder where are downloaded nitpm packages
+fun nitpm_lib_dir: String
+do
+ var dir = "NITPM_PATH".environ
+ if not dir.is_empty then return dir
+
+ return "HOME".environ / ".local/lib/nit/"
+end
+
+redef class Text
+
+ # Does `self` look like a package name?
+ #
+ # ~~~
+ # assert "gamnit".is_package_name
+ # assert "n1t".is_package_name
+ # assert not ".".is_package_name
+ # assert not "./gamnit".is_package_name
+ # assert not "https://github.com/nitlang/nit.git".is_package_name
+ # assert not "git://github.com/nitlang/nit".is_package_name
+ # assert not "git@gitlab.com:xymus/gamnit.git".is_package_name
+ # assert not "4it".is_package_name
+ # ~~~
+ fun is_package_name: Bool
+ do
+ if is_empty then return false
+ if not chars.first.is_alpha then return false
+
+ for c in chars do
+ if not (c.is_alphanumeric or c == '_') then return false
+ end
+
+ return true
+ end
+
+ # Get package name from the Git address `self`
+ #
+ # Return `null` on failure.
+ #
+ # ~~~
+ # assert "https://github.com/nitlang/nit.git".git_name == "nit"
+ # assert "git://github.com/nitlang/nit".git_name == "nit"
+ # assert "gamnit".git_name == "gamnit"
+ # assert "///".git_name == null
+ # assert "file:///".git_name == "file:"
+ # ~~~
+ fun git_name: nullable String
+ do
+ var parts = split("/")
+ for part in parts.reverse_iterator do
+ if not part.is_empty then
+ return part.strip_extension(".git")
+ end
+ end
+
+ return null
+ end
+
+ # Parse the external package declaration, as declared in package.ini
+ #
+ # Return a map of `ExternalPackage` organized by the short package name,
+ # as used in imports from Nit code.
+ fun parse_import: Map[String, ExternalPackage]
+ do
+ var res = new Map[String, ExternalPackage]
+ var ids = self.split(",")
+ for id in ids do
+ id = id.chomp
+ if id.is_empty then continue
+
+ # Check version suffix (e.g. gamnit=1.0)
+ var match = id.search_last("=")
+ var package_name
+ var version = null
+ if match != null then
+ # There's a version suffix
+ package_name = id.substring(0, match.from)
+ version = id.substring_from(match.after)
+ id = package_name
+ else
+ package_name = id
+ end
+
+ # Extract a package name from a Git address
+ if not package_name.is_package_name then
+ # Assume it's a Git repository
+ var git_name = package_name.git_name
+ if git_name == null then
+ # Invalid name
+ # TODO report error only when used by the parser
+ continue
+ end
+ package_name = git_name
+ end
+
+ res[package_name] = new ExternalPackage(id, package_name, version)
+ end
+ return res
+ end
+end
+
+# Reference to a nitpm package
+class ExternalPackage
+
+ # Package identifier (name or Git address), without the version
+ var id: String
+
+ # Standard Nit package name, as used in importations from Nit
+ var name: String
+
+ # Version string of the package
+ var version: nullable String
+
+ # Expected folder name for this package
+ var dir_name: String is lazy do
+ var version = version
+ if version == null then return name
+ return name + "=" + version
+ end
+end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Services related to the Nit package manager
-module picnit_shared
-
-# Folder where are downloaded picnit packages
-fun picnit_lib_dir: String do do return "HOME".environ / ".local/lib/nit/"
test_rubix_visual
test_csv
repeating_key_xor_solve
+nitpm
--- /dev/null
+install https://gitlab.com/xymus/hello_nitpm.git
+install hello_nitpm
+upgrade hello_nitpm
+install nitpm_test_versions=1.0 nitpm_test_versions=stable nitpm_test_imports
+list
+uninstall -f nitpm_test_versions=1.0 nitpm_test_versions=2.0 nitpm_test_versions=stable nitpm_test_imports nitpm_test_cycle hello_nitpm
+list
+uninstall . .. 0invalid _invalid a/../
test_explain_assert
base_notnull_lit_alt2
assertions
+nitpm
+++ /dev/null
-install https://gitlab.com/xymus/hello_picnit.git
-install hello_picnit
-list
-upgrade hello_picnit
--- /dev/null
+usage: nitpm <command> [options]
+
+commands:
+ install Install packages by name, Git repository address or from the local package.ini
+ list List installed packages
+ upgrade Upgrade a package
+ uninstall Uninstall packages
+ help Show general help message or the help for a command
+
+options:
+ -h, --help Show help message
+ -v, --verbose Print more information
--- /dev/null
+Package 'hello_nitpm' is already installed
--- /dev/null
+hello_nitpm example
+nitpm_test_cycle
+nitpm_test_imports
+nitpm_test_versions=1.0
+nitpm_test_versions=2.0
+nitpm_test_versions=stable
--- /dev/null
+Package name '.' is invalid
+Package name '..' is invalid
+Package name '0invalid' is invalid
+Package name '_invalid' is invalid
+Package name 'a/../' is invalid
+++ /dev/null
-usage: picnit <command> [options]
-
-commands:
- install Install a package by its name or from a git-repository
- list List installed packages
- upgrade Upgrade a package
- uninstall Uninstall a package
- help Show general help message or the help for a command
-
-options:
- --help, -h Show this help message
- --verbose, -v Print more information
+++ /dev/null
-Already installed
+++ /dev/null
-hello_picnit example
+++ /dev/null
-Already up-to-date.