Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,44 @@
Versions are git tags (`vX.Y.Z`); projects install one with
`uv tool install git+ssh://git@github.com/hybridinteract/deployctl@vX.Y.Z`.

## 0.11.0

The control panel, rebuilt around continuous deployment.

**Panel**
- **Five tabs, in the order a project needs them.** *Operate* (every day: what runs, deploy
or roll back a tag, the release history, apply a config change, maintenance, a terminal),
*Setup* (once per server: the bootstrap script, the missing configuration, the first
deploy), *CI/CD* (once per repository: `ci doctor` as a checklist with a fix button per
row, recent runs), *Configure*, *Logs*. The Tutorial and Terminals tabs are gone: their
content is in Setup, Operate and the docs.
- **A live top bar and a stepper.** The running tag, who shipped it and when, health,
whether GitHub's config matches this machine's, and whether merges deploy — read through
`deploy status --json` and `ci doctor --json`, cached for 15 s, refreshed after every job.
The stepper (Server → First deploy → CI/CD → Live) names the next step. The page opens
on Operate once CI/CD is set up, on Setup before.
- **Deploys go through GitHub.** Deploy a tag, roll back, roll back to any earlier
release, and apply a config change all run the deploy workflow (`ci deploy`), so every
deploy lands in one history. Deploying straight from this machine is folded under
*Emergency*.
- **Buttons can take a value — only one the action declares.** Today that is an image tag,
checked against the CLI's own pattern and passed as one whole argument; an undeclared,
repeated or malformed value starts nothing and says why in the output pane.
- Dark mode (follows the system), keyboard-navigable tabs, a toast when a job ends, the
open tab kept in the address. Configure shows what validation says, re-read on Save; the
image-tag field is gone (the tag is not configuration since 0.10.0); a *Deploy branch*
field is new.

**CLI**
- `deploy history --json`, for the panel's release list.
- `ci doctor --json`: the automatic-deploys row carries `value: on|off`.

**Fixed**
- `validate` failed every project without `IMAGE_TAG` in its config (so every project
after `migrate-config`): it compared the rendered compose file with `repo:` and an empty
tag. It now checks against the tag the files were rendered with, and an image reference
is never derived without a tag.

## 0.10.0

Continuous deployment by default, and fleets that are never left split.
Expand Down
59 changes: 18 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,51 +94,28 @@ drives your SSH keys, so never expose the port.

## Then, in the browser

**0 · Tutorial** *(first deployment only)* — the whole path from a bare VPS, droplet or EC2
instance to a running deployment, in order: what to install on the server, the deploy user
and its permissions, the cloud pieces a cluster needs, then configure and deploy. Every
command is rendered with your own values, and the steps the panel can perform have a Run
button on them. Skip to **1** if your servers are already bootstrapped.
The panel follows the project's journey — **Server → First deploy → CI/CD → Live** — and
the stepper under the top bar always names the next step.

**1 · Configure** — fill in the sections and press **Save**. The form adapts to the
environment's shape: a single-server environment shows TLS and containerized database
settings, a cluster shows the host list with a "primary" selector. Required fields are
marked; help text sits under each input. Saving writes the same `config/*.env` files the
CLI reads — there is no hidden state.
**Setup** *(once per server)* — the root script that prepares a fresh server, rendered with
this environment's values; the configuration still missing; then the first deploy as a
numbered rail: pick the tag CI published, Regenerate → Validate → Doctor → Init → SSL →
Status. Everything before Init changes nothing on a server.

**2 · Set the image tag** — in *Application image*, press **fetch tags from the registry**
and click the newest, then **Save**. (Nothing published yet? Run `deployctl ci init` for
a GitHub Actions workflow, or `deployctl image push` to build from this machine.)
**CI/CD** *(once per repository)* — `deployctl ci doctor` as a checklist, each row with the
button that fixes it: connect GitHub, generate the workflows, create the CI key, pin the
host keys, sync the config, turn automatic deploys on.

**3 · Deploy** — the tab is laid out as the procedure itself: a numbered rail you work left
to right, where each step assumes the previous one passed.
**Operate** *(every day)* — shipping is a merge. What remains is here: what each host runs
and who shipped it, deploying or rolling back a tag through GitHub, the release history
with **Roll back to this**, putting a config change live, backups and a shell. Deploying
straight from this machine is folded under **Emergency**.

```
First deployment
1 Fill in config → 2 Regenerate → 3 Validate → 4 Doctor → 5 Init → 6 SSL: obtain → 7 Status
(you edit) (local) (local) (reads) (deploys) (single only)

Roll out a new version ← the everyday one
1 Set image tag → 2 Regenerate → 3 Validate → 4 Doctor → 5 Update → 6 Status
(you edit) (local) (local) (reads) (rolling)
```

Steps 2–4 change nothing on any server; the deploying step is the first that does. Below
the rails sit the groups that genuinely have no order — **Check & observe**, **Recover**,
and **Take it down** — drawn as plain grids so the difference between "do these in turn"
and "run whichever you need" is visible at a glance.

Output streams live into the pane on the right (drag its edge to resize), and every card
shows the exact `deployctl` command it runs, whether it touches a host, and what it leaves
changed — so anything you see, you can reproduce in a terminal.

**4 · TLS** *(single-server only)* — after the first `Init`, run **SSL: obtain**. Until
then the site is serving a self-signed placeholder certificate. Cluster deployments
terminate TLS at the load balancer instead, so the action is hidden there.
**Configure** — every setting as a form, validated as you save. **Logs** — follow a host.

That is a complete first deployment. **Day to day it is two clicks:** pick a new tag in
*Application image*, then **Update**. **Rollback** returns to the previously deployed tag,
and **Backup now** dumps the database.
Every card shows the exact `deployctl` command it runs, where it acts and what it leaves
changed, and output streams live into the pane on the right — anything you see, you can
reproduce in a terminal.

Full details — the action whitelist, how secrets are handled, what the panel deliberately
cannot do: **[docs/50-WEBUI.md](docs/50-WEBUI.md)**.
Expand Down Expand Up @@ -287,7 +264,7 @@ troubleshooting table. The rest is depth to come back for:
4. **[30-OPERATIONS.md](docs/30-OPERATIONS.md)** — day two: rolling updates (what to edit, in what order), rollback, backups, `/docs` auth, troubleshooting.
**[35-CONTINUOUS-DEPLOYMENT.md](docs/35-CONTINUOUS-DEPLOYMENT.md)** — deploy on merge from GitHub Actions: keys, the config secret, rollback.
5. **[40-ADOPTING-A-NEW-PROJECT.md](docs/40-ADOPTING-A-NEW-PROJECT.md)** — setting up a new repository, or moving one off a copied-in deployctl.
6. **[50-WEBUI.md](docs/50-WEBUI.md)** — the control panel, its Deploy tab, and its safety model.
6. **[50-WEBUI.md](docs/50-WEBUI.md)** — the control panel, its tabs, and its safety model.
7. **[60-COMMAND-REFERENCE.md](docs/60-COMMAND-REFERENCE.md)** — every command and flag, checked against the CLI by a test.

## Working on deployctl
Expand Down
64 changes: 27 additions & 37 deletions docs/05-QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ optional and nothing here is ambiguous — run the blocks in order, top to botto
>
> 1. **A server** you can `ssh` into, with **Docker** installed and a **deploy user** that
> can run `docker` without `sudo`. → Don't have one? Do
> [Part A and Part B of the Tutorial tab](#step-5--open-the-control-panel) first; it
> takes about ten minutes and covers provisioning and bootstrapping the host.
> the panel's **Setup → Prepare the server** (or `deployctl server bootstrap-script`) first;
> it takes about ten minutes on a fresh Ubuntu server.
> 2. **A container image in a registry.** → Don't have one? Step 4 creates the CI workflow
> that builds it.

Expand Down Expand Up @@ -83,50 +83,42 @@ Ctrl+C stops it. If the port is taken: `--stop` ends the old one, `--restart` re

# In the browser

## Configure
The panel opens on **Setup** and the stepper under the top bar — Server → First deploy →
CI/CD → Live — always names the next step.

Fill in the sections and press **Save**. The form adapts to the environment's shape:
single-server shows TLS and containerized-database settings, a cluster shows the host list
with a "primary" selector. Required fields are marked `*`; help text sits under each input.
## Setup

Saving writes the same `config/*.env` files the CLI reads — there is no hidden state.

**Set the image tag:** in *Application image*, press **fetch tags from the registry**,
click the newest, then **Save**.

## Deploy

Open the **Deploy** tab and follow the **First deployment** rail left to right. Each card
shows the exact command it runs and what it changes:
1. **Prepare the server** — the root script for this environment, and the one line that
runs it on each new server. Skip it for a server you already bootstrapped.
2. **Finish the configuration** — lists what still blocks a deploy; **Open Configure**,
fill in the sections and press **Save**. The form adapts to the environment's shape;
Save writes the same `config/*.env` files the CLI reads.
3. **First deploy** — type the tag CI published (or pick it from **Recent tags from the
registry**), then work the rail left to right:

```
1 Fill in config → 2 Regenerate → 3 Validate → 4 Doctor → 5 Init → 6 SSL: obtain → 7 Status
(you edit) (local) (local) (reads) (deploys) (single only)
1 Regenerate → 2 Validate → 3 Doctor → 4 Init → 5 SSL: obtain → 6 Status
(local) (local) (reads) (deploys) (single only)
```

Steps 2–4 change nothing on a server; step 5 is the first one that does. **Do not skip
Steps 1–3 change nothing on a server; Init is the first one that does. **Do not skip
Doctor** — it is what catches an unreachable host or an unpullable image *before* anything
is torn down.
starts. **SSL: obtain** appears only for a single-server Let's Encrypt environment.

Step 6 (**SSL: obtain**) appears only for a single-server Let's Encrypt environment. Until
you run it the site serves a self-signed placeholder certificate. Cluster deployments
terminate TLS at the load balancer, so the step is not shown there.
## CI/CD

That is a complete first deployment.
Work down the checklist — each row has the button that fixes it: Connect GitHub, Generate
workflows (then commit and push them), Create the CI key, Pin host keys, Sync config. Then
**Redeploy what's running** once, to prove it, and turn automatic deploys on. Details:
[35-CONTINUOUS-DEPLOYMENT.md](35-CONTINUOUS-DEPLOYMENT.md).

## Afterwards, day to day

Two clicks. Follow the **Roll out a new version** rail — it is the first thing on the
Deploy tab and marked *everyday*:

```
1 Set image tag → 2 Regenerate → 3 Validate → 4 Doctor → 5 Update → 6 Status
(you edit) (local) (local) (reads) (rolling)
```

On a routine release, **`IMAGE_TAG` is the only value that changes.** Full detail on what
to edit and why the order is what it is:
[30-OPERATIONS.md § Rolling out an update](30-OPERATIONS.md#rolling-out-an-update).
**Merge to the deploy branch** — that is the release. The panel opens on **Operate**: what
runs and who shipped it, **Deploy a version** / **Roll back** through GitHub, the release
history with **Roll back to this**, and **Apply a config change** (Save → Sync config →
Deploy the change). Deploying from this machine is still there, folded under
**Emergency**, for when GitHub is the thing that is broken.

---

Expand Down Expand Up @@ -174,6 +166,4 @@ run, without touching a host.
| Doctor fails on permissions | `deployctl deploy doctor --fix` repairs what does not need root; anything else is printed as an exact command to run on the host. |
| Health gate times out after a deploy | `deployctl deploy logs --host <host>`. The gate prints the last HTTP status and what it usually means. |

Full troubleshooting: [30-OPERATIONS.md § When it breaks](30-OPERATIONS.md), or the
**Tutorial** tab's "When it breaks" section, which is the same material with your values
substituted in.
Full troubleshooting: [30-OPERATIONS.md § Troubleshooting](30-OPERATIONS.md#troubleshooting).
5 changes: 3 additions & 2 deletions docs/30-OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ when something is broken.

## Rolling out an update

This is the everyday path. In the panel it is the **Roll out a new version** rail at the
top of the Deploy tab, drawn in this order; from the CLI it is the same six steps.
Once CI/CD is set up, a release is a merge — see
[35-CONTINUOUS-DEPLOYMENT.md](35-CONTINUOUS-DEPLOYMENT.md). This section is the same
release by hand, from a laptop: the panel's **Operate → Emergency**, or these commands.

### What you edit

Expand Down
Loading
Loading