Files
sjnetPKG/README.md
T
jhkaktus f1250881f4 Implement sjnetpkg 0.1.0, a Git-based package manager
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>
2026-09-24 16:51:29 +02:00

171 lines
5.2 KiB
Markdown

# 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`.
```bash
pip install .
# or, for development
pip install -e .
```
You can also run it straight from the checkout:
```bash
PYTHONPATH=src python3 -m sjnetpkg --help
```
## Package format
Every package must contain a `sjnetpkg.toml` in its root directory:
```toml
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:
```toml
[packages.cool-tool]
repo = "https://github.com/example/cool-tool.git"
```
The registry URL is configured in `~/.sjnetpkg/config.toml`:
```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 messages
* `config.py` - the `~/.sjnetpkg/` layout, `config.toml`, registry configuration
* `registry.py` - downloading, reading and searching the registry
* `package.py` - package paths, `sjnetpkg.toml`, metadata, install state
* `git.py` - `git clone`, `git pull`, git availability and errors
* `runner.py` - run command lookup, working directory, process, exit code
Run the tests (standard library `unittest`, no extra dependencies):
```bash
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.