Skip to main content

abstract_pypit

One-command PyPI publisher + GitHub pusher, a server-wide staging release loop (init / stage / release / request / runner), a Postgres mirror of package trees (src) and fleet distribution to hosts (fleet).

Finds the next free version above whatever is on PyPI, bumps setup.py and pyproject.toml, builds sdist + wheel, uploads to PyPI via twine, commits and pushes to GitHub, then syncs the local install — all in one call.

Dependencies: requests, plus build and twine (so a fresh install can publish).

Install

pip install abstract_pypit

Usage

# from any package directory that has setup.py + pyproject.toml:
from pypit import runit
runit()
# or from the command line:
abstract-pypit

Staging release loop

The source of a package is a directory on the server (default /srv/pyit/dev/<project>, symlinks resolved). Nobody edits it directly; changes reach it only through a disposable staging dir and a host-side runner:

# once, as the user who owns the staging root (the "runner"):
abstract-pypit runner install --root /srv/vm_mgr/src --requesters vm_mgr,hugpy
abstract-pypit init abstract_utilities --root /srv/vm_mgr/src      # staging dir + push.sh, then stage

# any requester:
/srv/vm_mgr/src/abstract_utilities/push.sh stage     # source -> ./source -> ./live
#   ... edit ./live, test there ...
/srv/vm_mgr/src/abstract_utilities/push.sh release   # upload, then the source is swapped
/srv/vm_mgr/src/abstract_utilities/push.sh           # status

push.sh only drops <root>/_pypit/requests/<project>.<action>.<id>.req and follows the log (abstract-pypit request ...); the owner's systemd --user path unit runs abstract-pypit runner run, which executes requests one at a time. No ssh, sudo or credentials are needed to release.

release, per project:

  1. If the source moved on since staging (version, content or git HEAD), it PAUSES: writes merge-*.diff files, 3-way merges the source into live/ (base = source/), marks conflicts <<<<<<< live / ======= / >>>>>>> source (or a <file>.CONFLICT note; nothing wins), rebases source/ and stops. The merged live is what the next push releases; it refuses while markers or notes remain.
  2. Otherwise, by backend (auto-detected):
    • pypi: runPypit inside live/ (next free version, build, upload, commit+push without pulling, sync the local install); success = PyPI lists the version.
    • pkg_src: the source is <repo>/py/<group>/<pkg> of a pkg-src-watched tree (<repo>/py/tooling/pkg_src.py); the swap is the release; success = pkg-src-watch recorded exactly the swapped tree (read-only query), else the swap is reversed.
  3. live/ moves to prod/, prod/ is copied to a temp dir and content-verified, then source -> _old, temp -> source, _old removed; then it re-stages.

Copies are rsync -aAX --checksum, keep .git, and exclude build output. Commands:

command
abstract-pypit init <p> [--root R] [--source S] [--editors u1,u2] [--handoff R2] [--force] create/rebuild the staging dir, stage
abstract-pypit stage <p> [--force] / release <p> / status <p> the engine (run by the runner)
abstract-pypit request <stage|release|status> <p> what push.sh calls
abstract-pypit runner install [--root R ...] [--requesters u1,u2] / run --root R / status the runner
abstract-pypit src ... / abstract-pypit fleet ... the mirror and the fleet (below)

Env: PYPIT_ROOT (default root ~/staging), PYPIT_DEV_ROOT (default /srv/pyit/dev), PYPIT_PYPI_WAIT, PYPIT_PKG_SRC_WAIT, PYPIT_PKG_SRC_PYTHON. Tests: python -m unittest discover -s tests (nothing is uploaded).

Package mirror + fleet (src, fleet)

hugpy's pkg-src-watch system, housed here and generalized to any tree, DB and schema. pip install 'abstract-pypit[src]' adds psycopg (Postgres); the fleet hub and agent need only the base package.

src — mirror a tree of packages into Postgres. Every package (a directory with a pyproject.toml, found by globs; hugpy's */*/pyproject.toml layout is detected first) gets a table <schema>.pkg_<package>, one row per file, so a build recreates it from the table. Each distinct tree is an immutable version (contents deduped by sha256; identity = path + content + exec bit), labelled <release>.<micro>: a tag's tree is 0.2.1, later trees 0.2.1.1, 0.2.1.2 … resetting when the release advances. Configurations pin one version per package. good / config good refuse unless a verify job passed on exactly that version / member set, in a hashed test env that matched its fingerprint (optionally through a sandbox launcher). The schema is abstract-pypit's own (default pypit_src); a schema it did not create is refused.

abstract-pypit src init hugpy --tree /path/to/tree --dsn 'dbname=mydb' [--schema pypit_src]
abstract-pypit src sync && abstract-pypit src verify     # mirror; rebuild + compare byte-for-byte
abstract-pypit src install                               # the watch: systemd --user unit
abstract-pypit src status | versions | config save NAME | config diff A B
abstract-pypit src job verify PKG@REF [--now]  ->  abstract-pypit src good PKG REF
abstract-pypit src restore PKG REF [--apply]   |   config restore NAME [--apply]

The watch polls every interval s, syncs a package once it has been quiet for settle s, runs queued verify/restore jobs one at a time, optionally pins each settled edit as config dev-<utc> (--auto-config) and steps the fleets bound to the mirror.

fleet — ship configurations to hosts and keep them converged. A configuration becomes a release: one wheel per package built from the DB trees at one lockstep <base>.postN (or, --scheme label, each package at its own label), checked (versions, sibling pins), published immutably into an index dir. A pin (<index>/fleet.json + constraints.txt) points the fleet at a release; the hub serves the dir as a PEP 503 index and takes host reports; each host's agent pip-installs the pinned set under those constraints, runs its restart commands, checks health and reports. Each pin opens a rollout judged from those reports (healthy / unhealthy / unjudged; unhealthy releases are never auto-re-pushed). Rollback = pin an older release.

abstract-pypit fleet init prod --mirror hugpy --index-dir /srv/wheels [--gate known-good|none] [--auto]
abstract-pypit fleet publish CONFIG --pin       # or let the watch do it (--auto)
abstract-pypit fleet hub install                # serve the index + pin (default 127.0.0.1:9300)
abstract-pypit fleet status | releases | tick | pin RELEASE
# on every host (needs only `pip install abstract-pypit`):
abstract-pypit fleet agent init app --hub http://hub:9300 --python /srv/app/venv/bin/python \
    --track my-dist --restart 'systemctl --user restart app.service' --health-url http://127.0.0.1:8000/health
abstract-pypit fleet agent install app

Settings live in $PYPIT_CONFIG_DIR (default ~/.config/abstract-pypit): src/<mirror>.json, fleet/<fleet>.json, agent/<agent>.json. Tests: PYPIT_TEST_DSN='dbname=…' python -m unittest discover -s tests (DB tests use a throwaway schema and drop it; without the variable they are skipped).

Credentials

PyPI: twine reads ~/.pypirc or TWINE_USERNAME / TWINE_PASSWORD env vars.

GitHub: create pypit/src/envs/.env on the machine running pypit:

GITHUB_OWNER_1=your-username
GITPASS_1=<your-github-pat>
GITHUB_OWNER_2=your-org
GITPASS_2=<org-github-pat>

SSH key at ~/.ssh/github/githubssh_nopass must be registered with GitHub.

Per-package config

Add to the package's own pyproject.toml:

[tool.pypit]
github_owner = "your-org-or-username"   # which org/user owns the repo
github_push  = true                      # set false to skip GitHub entirely

License

MIT

Metadata

Release files for abstract-pypit 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for abstract-pypit 0.1.6
File Size Uploaded
abstract_pypit-0.1.6.tar.gz 127.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for abstract-pypit 0.1.6
File Interpreter ABI Platform
abstract_pypit-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 255.8 kB

Release files / abstract_pypit-0.1.6.tar.gz

Download URL abstract_pypit-0.1.6.tar.gz
Size 127.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f9e8dda2bd08e0ba8237bbca901bc73dac287fa504fe8fb8a56a4ab7dad51189
BLAKE2b-256 checksum
How to use checksums
7b55d42350946dc88f43273ed4cb3ea010cd2082056a15f464b16cdbb4d45305
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.3

Release files / abstract_pypit-0.1.6-py3-none-any.whl

Download URL abstract_pypit-0.1.6-py3-none-any.whl
Size 128.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
29193c0fd1bbc53eac4a31ee3e616f528a32d928ee278d5d81d9cee94d108838
BLAKE2b-256 checksum
How to use checksums
3582ab44b26c33c5a7b591cb79e6197c5f5df96eefe46c1926dbbf33dc4a695c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

This release

0.1.6 This release

2 release files

0.1.5

1 release file

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.7

2 release files

0.0.6

1 release file

0.0.5

1 release file

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page