Protoprimer
Escape the fragile mess of pre-venv shell scripts with a pure Python bootstrap wrapper: :rocket: :fast_forward: single-touch, zero-args, self-contained, no-deps, cross-platform, testable init logic using standard tools to run apps in a custom environment isolated per repository
Install / Use
/learn @uvsmtid/ProtoprimerREADME
protoprimer
Want your users to run software straight from a git repo with a single, zero-argument, healing command?
./prime
- No conflicts: everything is isolated to that repo clone.
- No bloat: no automatic changes to global, local, user configs (e.g. no
~/.*rcupdates, etc.). - Reversible: simply remove the repo clone.
- not a binary but a text - a transparent, standalone
pythonscript that lives inside your repo - starts with "wild"
python- hands over with requiredpythonin an isolatedvenvwith pinned dependencies - handles multiple target environments with basic configs to take off in different directions
- an extensible bootstrap DAG to handle it all in one shot
- a universal main-function starter without explicit
venvactivation
<a id="protoprimer-motivation"></a>
First: why avoid shell?
Main reason:
Your org does not test shell scripts.
The shell paradox:
we start with shell because it is "simple", but that is also shell:
- :x: (unit) test code for
shellscripts is close to none - :x: no default error detection (forget
set -eand an undetected disaster bubbles through the call stack) - :x: cryptic "write-only" syntax (e.g.
echo "${file_path##*/}"vsos.path.basename(file_path)) - :x: subtle, error-prone pitfalls (e.g.
shoptnuances) - :x: unpredictable local/user overrides (e.g.
PATHpoints to unexpected binaries) - :x: less cross-platform than it seems even on *nixes (e.g. divergent command behaviors: macOS vs Linux)
- :x: no stack traces on failure (which encourages noisy, excessive logging instead)
- :x: limited native data structures (no nested ones)
- :x: no modularity (code larger than one-page-one-file is cumbersome)
- :x: no external libraries/packages (no enforce-able dependencies)
- :x: when
shellscripts multiply, they inter-depend for reuse (bysource-ing) into an entangled mess - :x: being so unpredictable makes
shellscripts high security risks - :x: slow
- ...
In short, shell is a very poor language choice for evolving software.
- Ubiquity: shares the same "pre-installed-anywhere" scripting niche (as
shell). - Mindshare: leverages a massive community and vast ecosystem everyone is exposed to (as
shell). - Sanity: testable, structured code with a clear syntax.
Problem 1: one does not simply avoid shell
Every time some repo.git is cloned,
it has to be prepared/bootstrapped/primed to make many things ready.
Because python is not ready yet,
people resort to shell scripts (again!) to make it ready.
Ultimately, why not use python to take care of itself?
Solution 1: immediately runnable python
Eliminate dependency on shell:
➖ instead of relying on the presence of a shell executable to bootstrap python
➕ rely on a python executable (of any version) to bootstrap the required python version
An app started via protoprimer switches to venv - no (explicit) activate-tion needed:
./hello_world
See the difference:
---
config:
look: handDrawn
theme: neutral
---
flowchart LR;
heavy_minus["➖"];
shell_exec["any<br>`shell`<br>executable"];
subgraph "sh"
sh_entry_script["entry script<br>like<br>`prime`"];
embedded_code["re-invented<br>ad-hoc<br>non-modular<br>`shell` script"];
end
heavy_plus["➕"];
python_exec["any<br>`python`<br>executable"];
subgraph "py"
py_entry_script["entry script<br>like<br>`prime`"];
py_bootstrap_code["tested<br>bootstrap code<br>from<br>`protoprimer`"];
end
external_exec["other<br>(external)<br>executables"];
invis_block[ ];
heavy_minus ~~~ shell_exec;
shell_exec --runs--> sh_entry_script;
sh_entry_script --with--> embedded_code;
heavy_plus ~~~ python_exec;
python_exec --runs--> py_entry_script;
py_entry_script --with--> py_bootstrap_code;
embedded_code --invokes--> external_exec;
py_bootstrap_code --invokes--> external_exec;
external_exec ~~~ invis_block;
style heavy_minus fill:none,stroke:none;
style heavy_plus fill:none,stroke:none;
style invis_block fill:none,stroke:none;
Next: why not uv?
In fact, protoprimer relies on uv (optionally).
But, it starts as a python script (then bootstraps uv and runs uv).
Problem 2: audience
Distinguish these:
- dev-authors: deeply understand the tools in use (like
uvor anything else), do not like writing manuals - dev-users: strangers to the repo, new to the
pythonecosystem, but can contribute and improve some stuff - end-users: ...
Relying on python first:
- is more robust for the single-touch bootstrap (
pythonis more ubiquitous thanuv) - uses easily modifiable local interpreted
pythoncode to wrap calls to any compiled binary (likeuv)
In short, uv is one of many other executables (external to python) employable for bootstrapping.
Also, uv is hardly arg-less and single-touch without a shell wrapper:
- Its binary has to be prepared. A
shellwrapper? - Its args have to be provided. A
shellwrapper? - Full bootstrap requires a few
uvinvocations. Ashellwrapper? - Project-specific steps require more than
uv. Ashellwrapper? - Users want these details hidden. A
shellwrapper?
It is not ideal to re-invent such wrappers for every project.
</details>Solution 2: wrap details
Feel the difference:
| protoprimer | uv |
|---------------|---------------------------------------|
| ./bootstrap | uv run python -m module_a.bootstrap |
| ./app_1 | uv run python -m module_b.app_1 |
| ./app_2 | uv run python -m module_c.app_2 |
<a id="protoprimer-extensions"></a>
Last: adaptive bootstrapping
Think:
- generating code
- downloading data
- ensuring security keys
- configuring pre-commit hooks & linters
- ...
protoprimer acts as a "seed" to grow via an extensible bootstrap (DAG or handover).
Different env? Different direction:
- local or cloud
- Alice or Bob
- dev or prod
- v1 or v2
- ...
Bonus: the springboard for any toolchain
python is omnipresent.
The bootstrap code can only rely on very basic pre-requisites satisfiable by default.
The availability of python is more reliable than "universal" shell compatibility:
- old
bash3.x andzshdifferences on macOS - non-POSIX "shells" on Windows
- ...
Why is python the ultimate seed?
- if not available, trivially installed
- arguably, new
pythondevs write less error-prone "Day 0" code than oldshelldevs - no compiler stage - it executes "committable text" directly without invoking build tools (just like
shell) - solves most of the "Why avoid
shell?" problems ([see above][shell_issues])
In turn, once bootstrapped, python code may springboard other toolchains:
javajsgo- ...
<a id="protoprimer-getting-started"></a>
Quick start
For the trivial case, see [instant_python_bootstrap][instant_python_bootstrap].
The single script [proto_kernel.py][local_proto_kernel.py] to be hosted by the client repo is called "proto code":
---
config:
look: handDrawn
theme: neutral
---
graph LR;
install_link["**1 x install:**"]
github_web["proto code<br>from github.com<
