Repository navigation
deployctl 0.14.0 — one console for all your projects - #5
Merged
Merged
Conversation
…oint of use - cli/home.py: deployctl's own files under the state home — 0700 folders, atomic 0600 writes, a cross-process lock. - cli/access.py: the registry login a pull uses, in order: the environment (CI's GITHUB_TOKEN), config/local.env, ~/.deployctl/credentials.env, then the gh login. The last two apply only to ghcr.io. Resolved only by commands that pull, never by the status the panel polls. - deployctl access [show|set-token|forget-token]: what you have and what is missing, each with its fix. set-token checks a classic read:packages token with GitHub before saving it, and never reads it from argv. - Engine: one EXIT/INT/TERM/HUP cleanup logs every host out of the registry and releases the deploy lock, so a failed or cancelled run never leaves a login on a host. - Panel: Your access reads `deployctl access --json`. A missing registry login is optional and never raises the Operate alert. The card can save a read:packages token, and shows the one line an owner runs to add your ssh key. - Config snapshots are keyed by repository name plus a path id, so two repositories with the same name never prune each other's; backup.sh honours DEPLOYCTL_HOME. - Tests: an autouse fixture keeps every test off the developer's own credentials.
… its own
- cli/projects.py: ~/.deployctl/projects.json holds name, repository and
port per project. Ports are given once from 8766 and kept. Writes are
locked and atomic; a damaged file is refused, never overwritten. A
copied-in deployctl or a repository without a project says what to do
instead.
- paths.find_deploy_dir(repo): finds another project's deploy directory
without walking up or reading the calling project's DEPLOYCTL_PROJECT.
- deployctl projects list|add|remove. init, adopt --apply and webui
register their project themselves.
- deployctl webui:
- uses the project's own port;
- reuses a panel already serving this project, and refuses --port while
one runs;
- names whatever else holds the port;
- --detach (log in ~/.deployctl/logs, returns once it answers), --json,
and --no-browser (the browser opens by itself only on a desktop,
never over ssh);
- outside a project, the home panel on 8765.
- --stop only ever stops a deployctl panel.
- /healthz (no token) says which panel a port serves. It replaces the
panels/<port>.json records, which named the wrapper's pid and raced a
failed bind.
- Switcher, top right: every project on this machine with its running tag (read-only deploy status, loaded lazily and cached briefly). Choosing one opens its own panel, started if need be. Only a name comes from the page, and the address is built by the server. The panel you leave keeps running with its jobs. - Add project, from any panel or the home panel: - a project opens; - a teammate's clone opens on the 0.13 Import; - a repository without a project gets the New form; - a copied-in deployctl gets the adopt commands, to run in a terminal. - New project: `deployctl init --set KEY=VALUE`. INIT_KEYS maps each key to its file; values are checked before anything is written, are never secret, and are refused for a file that already exists. The image is suggested from origin, lowercased. - Every dangerous confirmation names the project, and the page carries it. - The top bar and the switcher say "hosts unreachable — tag unknown" instead of "nothing deployed yet" when a host did not answer. - webui --stop waits for the process itself, not just the port; panels finish in-flight requests within 5s. - Tests: route tests over HTTP, init --set, and a wiring check across every panel module. A Playwright suite (tests/e2e, -m e2e, its own CI job) covers switching with a job running, a teammate's clone through Import, a new project to Setup, and confirmations, against real panel processes with TEST-NET hosts and ssh/gh stand-ins.
- README: one screen on what deployctl is, one install and access step,
and the three ways in — a new project, joining one, upgrading a
copied-in one. The layout now shows local.env, exports/, imports/ and
~/.deployctl.
- 05-QUICKSTART is now Getting started: install, gh login and access, the
panel, Add project (new or join), the first deployment, CI/CD, and day
to day.
- New 15-FIRST-DEPLOYMENT:
- the checklist (server, DNS, ports, a published image, access);
- bootstrap, and every configuration field;
- each first-deploy step: what it does, what success looks like, what
to do when it fails;
- SSL, verifying the site, the hand-off to CI/CD, troubleshooting.
- 50-WEBUI: a panel per project, the switcher, the home panel, Add
project, the safety model's new points, and Your access. The command
reference, the docs map, and the 10/20/30/35/40 guides follow the new
access model.
- CHANGELOG 0.14.0 with upgrade notes; ROADMAP follow-ups (a narrower
default for host pulls, panels left running across an upgrade).
- The 0.14 design brief, with its status corrected and the planning
decisions recorded.
- Version 0.14.0.
…ter a restart - /healthz probes never go through HTTP_PROXY. On a network that sets one, 127.0.0.1 went to the proxy, and a running panel looked like none at all, so a second was started on a busy port. - A port counts as free the way uvicorn binds it, with SO_REUSEADDR. A port a panel has just left sits in TIME_WAIT, and `webui --restart` was refused as "in use". - /healthz reports the panel's pid, so `webui --stop` works where lsof is not installed. When the holder cannot be named, --stop leaves it alone and says so. - Registering a project again writes nothing when nothing changed, since `webui` registers on every start.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One setup per person (the
ghlogin). Any number of projects on one laptop, each with its own panel, and a switcher between them. Add project handles a new first deployment, a teammate's clone (Import) or a copied-in deployctl. The docs now take a newcomer from nothing to deploying on every merge.Design brief, with the planning decisions recorded:
docs/design/0.14-brief.md.What changes
1. One setup per person —
cli/access.py,deployctl access [show|set-token|forget-token]Which registry login a pull uses, in order:
GITHUB_TOKEN);config/local.env(a per-project override);~/.deployctl/credentials.env, a read:packages-only token saved once for every project;ghlogin.The last two apply only to ghcr.io. The login is resolved only by commands that pull, never by the status the panel polls.
access set-tokenchecks a classic read:packages token with GitHub before saving it (0600). It never takes the token from argv, and refuses fine-grained tokens.The engine: one EXIT/INT/TERM/HUP cleanup logs every host out of the registry and releases the deploy lock. An interrupted or failed run never leaves a login on a host.
The panel: Your access reads
deployctl access --json. The registry row is optional and never raises the Operate alert. A token can be saved from the card, and a refused ssh key comes with the exact line the owner runs.2. Many projects, one panel each —
cli/projects.py,deployctl projects list|add|remove~/.deployctl/projects.jsonholds name, repository and port. Ports are given once from 8766 and kept. Writes are locked and atomic, and a damaged file is refused, never overwritten.deployctl webui:--portwhile that panel runs;--detach,--jsonand--no-browser;/healthz(token-exempt) says which panel a port serves, and replacespanels/<port>.json.--stoponly ever stops a deployctl panel, and waits for the process to exit.paths.find_deploy_dir(repo)finds another project's deploy directory without walking up or reading the caller'sDEPLOYCTL_PROJECT.3. The panel —
webui/panel/projects.py;routes.pydoes not growdeploy status. Opening a project sends only its name; the server looks it up and builds the address.deployctl init --set KEY=VALUE, allowlisted, never secret) / adopt commands.4. Docs
05is now Getting started.15-FIRST-DEPLOYMENT: every step, what success looks like, what to do on failure.50-WEBUIcovers projects and the switcher; the command reference covers the new commands; the 10/20/30/35/40 guides follow the access model.Hardening found in review:
HTTP_PROXYset.--stopworks withoutlsof.--restartis not refused by a port in TIME_WAIT.Where this differs from the brief
projects.json, not.toml: Python has no TOML writer.webuialways opens the home panel, never the last project used.adoptcommands to run in a terminal, not plan/apply in the panel.Testing
uv run pytest -q: 999 passed, up from 833. New:test_access,test_home,test_projects,test_init_values,test_panel_projects;test_webui_launch: real panel processes, including a dead proxy, no lsof, restart;uv run pytest -m e2e: 4 passed. A new Playwright suite, run as its own CI job, drives real panel processes on scratch repositories with TEST-NET hosts and ssh/gh stand-ins:selftestrenders all 6 shapes.origin.Upgrading
Stop any running panel first. Then install
v0.14.0,gh auth refresh -h github.com -s read:packages,deployctl access,deployctl ci init --env production --force. Panels move to per-project ports; 8765 is now the home panel.Risk to be aware of
With no read:packages token saved, a laptop-driven pull uses the person's
ghtoken. It can also write to their repositories, and a host holds it for the length of the pull (logged out on any exit). This is the default the brief chose.deployctl accessand the panel say so, the docs recommendaccess set-tokenfor production, and ROADMAP records ways to make a narrow login the default.