Packages are ordinary Git repositories carrying a sjnetpkg.toml, so sjnetpkg only needs to resolve, clone, track and run them instead of inventing an archive format or build system. This adds the v0.1 CLI (install from registry or Git URL, remove, list/inspect, registry update, run) on top of the Python standard library, together with a test suite. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
5.2 KiB
sjnetPKG
A simple, Git-based package manager.
A package is not a pre-compiled file or a special archive format - it is an
ordinary Git repository. sjnetpkg finds packages, clones them, keeps track
of what is installed, updates them and runs them.
Git repository -> ~/.sjnetpkg/pkg/<name> -> sjnetpkg.toml -> run command
The repository (including .git) is kept completely intact, and sjnetpkg
never modifies the package code itself.
Installation
Requires Python 3.11 or newer and the git executable on PATH.
pip install .
# or, for development
pip install -e .
You can also run it straight from the checkout:
PYTHONPATH=src python3 -m sjnetpkg --help
Package format
Every package must contain a sjnetpkg.toml in its root directory:
name = "cool-tool"
version = "1.0.0"
[run]
command = "cargo run"
name and version are required. version is treated as an opaque string;
v0.1 performs no semantic-version resolution.
The optional [run] section defines how the package is started. The command
is language-agnostic, for example:
| Language | command |
|---|---|
| Rust | cargo run |
| Python | python main.py |
| Go | go run . |
| Node.js | npm start |
| Bash | bash main.sh |
The command string is passed to the platform shell (/bin/sh on POSIX,
cmd.exe on Windows), so multi-argument commands such as
cargo run --release work as written. The process runs with the package
directory as its working directory, and STDIN, STDOUT and STDERR are passed
through directly.
Registry
The registry is a plain TOML file mapping package names to Git repositories:
[packages.cool-tool]
repo = "https://github.com/example/cool-tool.git"
The registry URL is configured in ~/.sjnetpkg/config.toml:
registry = "https://pkg.serverjonas.de/registry.toml"
A downloaded copy is kept at ~/.sjnetpkg/registry.toml so packages can be
resolved even when the remote registry is unreachable. A failed or invalid
download never destroys the existing local copy.
The default registry is https://pkg.serverjonas.de/registry.toml, defined by
DEFAULT_REGISTRY_URL in src/sjnetpkg/config.py. It is written into
config.toml on first start; afterwards the file in the home directory wins.
CLI
Usage:
sjnetpkg -S <package> install a package from the registry
sjnetpkg -U <url> install a package directly from a Git URL
sjnetpkg -R <package> remove an installed package
sjnetpkg -Q list installed packages
sjnetpkg -Q <package> show information about an installed package
sjnetpkg -Sy update the local copy of the registry
sjnetpkg run <package> run an installed package
sjnetpkg --help show the help
sjnetpkg --version show the version
With -U, the directory name is derived from the repository name in the URL,
while the name in sjnetpkg.toml is the final package identity. The two are
allowed to differ.
Installation never starts a package automatically - use sjnetpkg run <name>.
Packages are installed into ~/.sjnetpkg/pkg/ and are never copied to
/usr/bin, /usr/local/bin or ~/.local/bin.
Exit codes: 0 on success, 1 on errors, 2 on command line usage errors.
Errors are reported as short messages, never as Python tracebacks.
Directory layout
~/.sjnetpkg/
|-- config.toml
|-- registry.toml
`-- pkg/
`-- cool-tool/
|-- .git/
|-- sjnetpkg.toml
`-- ...
The home directory can be overridden with the SJNETPKG_HOME environment
variable (useful for testing and custom setups). It defaults to
pathlib.Path.home() / ".sjnetpkg".
Updating packages
Updates run git pull inside a package directory. sjnetpkg -Su, which
updates all installed packages, is planned; the update concept is defined but
v0.1 does not implement it yet. An update will never start a package.
Development
Modules and responsibilities:
cli.py- arguments, options, command dispatch, exit codes, error messagesconfig.py- the~/.sjnetpkg/layout,config.toml, registry configurationregistry.py- downloading, reading and searching the registrypackage.py- package paths,sjnetpkg.toml, metadata, install stategit.py-git clone,git pull, git availability and errorsrunner.py- run command lookup, working directory, process, exit code
Run the tests (standard library unittest, no extra dependencies):
PYTHONPATH=src python3 -m unittest discover -s tests -t tests
They also run under pytest, which picks up tests/conftest.py.
Scope of v0.1
Deliberately not included: dependency resolver, version constraints, automatic dependency installation, sandbox, signatures, checksums, binary packages, build system, automatic compiler installation, global executables, daemon, package hooks, an own Git implementation, remote package cache.
Security
A package is potentially executable foreign code: its [run] command may
start arbitrary commands. v0.1 provides no sandbox - only install and run
packages you trust.
License
To be determined.