# pyeasydeploy
Deploy Python apps to Linux servers over SSH, with plain Python functions on top of Fabric: no agents on the server, no YAML, no DSL to learn. Your deploy is a deploy.py in your project that reads top to bottom.
It is made for a few servers and a team that writes Python, not to compete with Ansible or Docker.
from pyeasydeploy import (
Host, Key, SupervisorService, connect, create_venv,
deploy_supervisor_service, get_target_python_instance,
install_local_package, supervisor_restart,
)
APP = "myapp"
USER = "deploy"
conn = connect(Host(
address="203.0.113.10",
user=USER,
auth=Key(path="~/.ssh/id_ed25519"),
sudo_password="...", # better: os.environ["SUDO_PASSWORD"]
))
py = get_target_python_instance(conn, "3.11")
venv = create_venv(conn, py, f"/home/{USER}/venvs/{APP}")
install_local_package(conn, venv, f"./{APP}")
deploy_supervisor_service(conn, SupervisorService(
name=APP,
command=f"{venv.path}/bin/python -m {APP}",
directory=f"/home/{USER}",
user=USER,
))
supervisor_restart(conn, APP)
# What a deploy does
The introductory example is a complete deploy. Each call is one step, run over SSH in order:
connectopens a connection to theHost: its address, user, and either aPasswordor aKey. Operations that need sudo usesudo_password, or the SSH password whenauthis aPassword. The connection is lazy: a wrong password shows up on the first command.get_target_python_instancepicks an interpreter already installed on the server: the newest one whose version matches"3.11".create_venvbuilds a virtual environment with that interpreter, removing any previous one at the same path.install_local_packageuploads your local package and installs it in the venv with its dependencies.deploy_supervisor_servicewrites a supervisord entry for the app and reloads supervisord, so the app runs as a service that restarts on failure and survives reboots.supervisor_restartthen restarts it, so it runs the new code even when its configuration did not change.
The venv object returned by create_venv carries its own path, so the service command is built from it and no path is repeated by hand. Run the script again and the server ends up in the same state: see Reproducibility.
The models (Host, Key, SupervisorService and the rest) validate themselves when built, so a relative path or a malformed service name fails on your machine before anything reaches the server. Keep secrets out of the script: read sudo_password from the environment, for example os.environ["SUDO_PASSWORD"].
# Requirements
- Your machine: Windows, macOS or Linux, with SSH access to the server.
- The server: Linux with SSH and a
python3in/usr/bin. Tested on Arch, Debian and Ubuntu. - Services: supervisord is installed and managed on Arch and on Debian-based distributions (Debian, Ubuntu), detected from
/etc/os-release. Managing services needs sudo on the server.
# Where to go next
- Guide: each part of a deploy in detail, with the options that change it.
- Reference: every public name.
- Reproducibility: how far "same script, same server" reaches.
- Design: the principles, and what the library deliberately does not do.