๐ฌ๐ง English ยท ๐ฎ๐น Italiano ยท ๐ช๐ธ Espaรฑol
๐งฐ small scripts automating the repetitive setup work i do every time i spin up a new webapp โ dns, hosting, and whatever else earns its place here over time.
in animation, comics and video games, hammerspace is the imaginary extradimensional storage that lets a character pull out exactly the tool they need, out of nowhere, the moment they need it โ wikipedia has the full history, tracing it from looney tunes gags to the anime fandoms (urusei yatsura, ranma ยฝ) that coined the term. tv tropes has a deeper catalogue if you want to go down that rabbit hole.
that's the idea here: reach in, pull out the right script, get back to building.
one python script per job, driven either from the github actions tab โ nothing installed, credentials living in repository secrets โ or from a terminal when you want to watch it work.
%%{init: {'theme': 'dark'}}%%
flowchart LR
YOU[you] -->|actions tab| GHA[github actions]
YOU -->|terminal| CLI[the scripts]
GHA --> CLI
CLI -->|subdomain, .htaccess, autossl, ftp account| UAPI[cpanel uapi]
CLI -->|delete subdomain, delete files| API2[cpanel api2]
CLI -->|branches, secrets, deploy workflow| GH[github api]
CLI -.->|a record, optional| NC[namecheap api]
CLI -.->|authorized urls, optional| PH[posthog api]
UAPI --> SRV[(your server)]
API2 --> SRV
GH -->|push| SRV
the split between the two cpanel apis isn't a style choice: uapi simply has no delete function, for subdomains or for files โ though it does have one for ftp accounts. see cpanel api notes and github api notes.
| tool | what it does |
|---|---|
create_subdomain.py |
creates a subdomain on cpanel, points its document root at ~/<name>, forces an https redirect, drops a PostHog-instrumented starter page (or a plain one, with --skip-posthog), adds the subdomain to PostHog's authorized urls so web analytics shows it, and starts autossl. optionally creates a dedicated dns record. deletes it again too, with or without its files. |
setup_autodeploy.py |
wires a github repo to that subdomain: creates the ftp account, writes the three FTP_* secrets on the target repo, and commits a deploy workflow so every push publishes. tears the whole thing down too. |
setup_gitflow.py |
readies a github repo for gitflow: an initial commit if it's empty, and a develop branch next to master. setup_autodeploy.py runs it by itself on an empty repo. |
- ๐ one click from the actions tab โ no checkout, no local python, credentials never leave repository secrets
- ๐งฏ create and delete are separate workflows โ a destructive run is never one mis-clicked checkbox away from a routine one, and each one only receives the credentials it needs
- โ๏ธ destructive runs ask you to retype the name โ checked before anything is even installed
- ๐
--dry-runeverywhere โ prints every call it would make and genuinely makes none, not even read-only ones - ๐ซ checks before it writes โ the auto-deploy run verifies the repo, the branch, the workflow and the ftp name up front, so a failure halfway through doesn't leave an orphan account behind
- ๐ฑ empty repos just work โ auto-deploy on a repo with no commits yet gives it an initial commit and gitflow's
master+developfirst, instead of failing on a branch that doesn't exist - ๐ forced https, encrypted ftp โ a
mod_rewriteblock appended to the subdomain's.htaccess, and deploys over ftps rather than the plaintext ftp the original procedure used - ๐ secrets sealed, never printed โ repo secrets are libsodium sealed boxes, and a generated ftp password stays out of the logs unless you ask for it
- ๐ files to the trash by default โ recoverable from cpanel's file manager;
--purgewhen you really mean it - ๐ก paths read, never guessed โ the document root comes from cpanel itself, and anything outside the home directory (or
public_html) is refused - ๐ซ won't clobber what it didn't write โ an existing
.htaccessor deploy workflow stops the run instead of being replaced - ๐ dns usually unnecessary โ a one-time wildcard record means every subdomain resolves the moment it exists
- ๐ tracked from minute one โ a noindex starter page carrying a cookieless PostHog snippet lands in the document root (
--skip-posthogleaves the snippet out), andsetup_autodeploy.pywarns if the repo it deploys later doesn't carry the snippet too. with a PostHog personal api key set, the subdomain also joins the project's authorized urls โ web analytics filters to those โ and leaves them again on--delete - ๐ฅ fails loudly and early โ a missing credential names itself instead of turning into a confusing api error later
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # fill in your own credentials, never commit this file
chmod 600 .env # it holds live api tokensfull credential list, and the github secrets to configure, in setup.
from the actions tab, or:
python3 create_subdomain.py lab # create
python3 create_subdomain.py lab --dry-run # rehearse it
python3 create_subdomain.py lab --delete # remove, keep the files
python3 create_subdomain.py lab --delete --with-files # + folder to the trash
python3 create_subdomain.py lab --delete --with-files --purge # + folder gone for good
python3 setup_autodeploy.py lab --repo you/lab # push to deploy
python3 setup_autodeploy.py lab --repo you/lab --branch web # from another branch
python3 setup_autodeploy.py lab --repo you/lab --delete # unhook it again
python3 setup_gitflow.py --repo you/lab # master + developevery flag, and what each step actually does, in usage.
create_subdomain.py the subdomain tool
setup_autodeploy.py the push-to-deploy tool
setup_gitflow.py the gitflow tool
acme/ wildcard certificate: acme.sh dns hook + setup, run on the server
test_*.py tests for the pure functions
.github/workflows/ one workflow per tool, plus a separate one for each delete
docs/ setup, usage, api notes, troubleshooting
assets/ art, and the PostHog snippet baked into every starter page
- setup โ credentials, the two token levels, github secrets, who can run what
- usage โ every flag and workflow input, and what a run actually does
- cpanel api notes โ the gaps and surprises that cost real time to find
- github api notes โ sealed-box secrets, the scope everyone forgets, and why a 404 isn't a typo
- troubleshooting โ what to do when a run goes sideways
- wildcard certificate โ a real certificate for the
*catch-all, renewed from inside cpanel, no token stored
work happens on develop; master is what the workflows run from.
pip install -r requirements-dev.txt
pytest -qthe tests cover the pure functions โ name validation, the document-root safety
guard, response parsing, dns parameter construction, secret encryption, the
generated workflow โ so they run without network or credentials. anything
touching an api is exercised with --dry-run instead.
one convention worth knowing before you read the source: comments and
docstrings are in italian, everything a user ever sees is in english. log
lines, error messages, --help, the workflow inputs in the actions tab, and
the deploy workflow this writes into your repo are all english. the italian is
the author thinking out loud about his own infrastructure, and stays there.
