# 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/ -> 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 install a package from the registry sjnetpkg -U install a package directly from a Git URL sjnetpkg -R remove an installed package sjnetpkg -Q list installed packages sjnetpkg -Q show information about an installed package sjnetpkg -Sy update the local copy of the registry sjnetpkg run 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 `. 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.