Automate production deployment on main pushes
Deploy production / deploy (push) Successful in 3s

This commit is contained in:
Labyricorn Deployment
2026-08-11 20:44:14 -07:00
parent 089589f8c4
commit 5b17b52e3f
2 changed files with 85 additions and 12 deletions
+27
View File
@@ -0,0 +1,27 @@
name: Deploy production
on:
push:
branches:
- main
concurrency:
group: labyricorn-production
cancel-in-progress: false
jobs:
deploy:
runs-on: labyricorn-deploy
timeout-minutes: 10
steps:
- name: Build and activate current main
run: sudo -n /usr/local/bin/deploy-labyricorn
- name: Verify the deployed revision and origin
run: |
deployed_revision="$(cat /srv/labyricorn/current/.labyricorn-commit)"
test "$deployed_revision" = "$GITHUB_SHA"
curl --fail --silent --show-error \
-H 'Host: www.labyricorn.com' \
-H 'X-Forwarded-Proto: https' \
http://127.0.0.1/ >/dev/null
+58 -12
View File
@@ -71,8 +71,9 @@ ignored and must not be committed.
## Routine content workflow
The preferred workflow is to edit in a normal Git checkout, review the changes,
push them to `main`, and then deploy on the production host.
Edit in a normal Git checkout, review the changes, and push them to `main`.
A successful push triggers the repository's Gitea Action, which builds and
atomically activates that exact revision on the production host.
```bash
git switch main
@@ -87,7 +88,8 @@ git commit -m "Describe the site change"
git push origin main
```
Then deploy on `ubuntu2-47`:
Watch the run under **Actions** in Gitea. Manual deployment remains available
for recovery or controlled operations:
```bash
sudo deploy-labyricorn
@@ -512,17 +514,61 @@ The first origin request should redirect; the simulated Cloudflare HTTPS request
should return the site. A different result indicates a proxy/header or nginx
configuration problem.
## Future automatic deployment
## Automatic deployment
Manual deployment is complete and remains the supported path. Automatic
deployment is not configured.
Pushes to `main` automatically deploy through Gitea Actions. The workflow is
versioned with the site at `.gitea/workflows/deploy.yml` and has one job:
The preferred future design is a Gitea Action triggered only by pushes to
`main`, invoking `deploy-labyricorn` through a dedicated, narrowly restricted
SSH identity. A webhook receiver is acceptable only if it authenticates a
secret, verifies the repository and branch, never executes payload text, and
runs with minimal privileges. Never expose a general shell endpoint or an
unauthenticated HTTP deployment URL.
1. Run on the repository-scoped runner labelled `labyricorn-deploy`.
2. Execute the existing root-owned `deploy-labyricorn` command.
3. Confirm `/srv/labyricorn/current/.labyricorn-commit` equals the pushed commit.
4. Request the site through nginx on the local origin and fail if it is unhealthy.
The action intentionally does not check out the repository into its own
workspace. The deployment script fetches `origin/main` using the dedicated
`labyricorn-deploy` account, validates a clean checkout, builds a new release,
and atomically changes the `current` symlink. Concurrent action runs share the
`labyricorn-production` concurrency group, and the deployment script also uses
a filesystem lock.
### Runner installation
The production host runs official Gitea Runner 3.0.0 directly on Linux; Docker
is not installed or required for this workflow.
| Item | Value |
|---|---|
| systemd unit | `gitea-runner.service` |
| service account | `gitea-runner` |
| runner name | `labyricorn-production` |
| registration scope | `Labyricorn/labyricorn-site` repository |
| execution label | `labyricorn-deploy:host` |
| runner binary | `/usr/local/bin/gitea-runner` |
| runner configuration | `/etc/gitea-runner/config.yaml` |
| registration state | `/var/lib/gitea-runner/.runner` |
| job workspace | `/var/lib/gitea-runner/work` |
| sudo policy | `/etc/sudoers.d/gitea-runner-labyricorn` |
The runner account has no general administrative access. Its sudo policy allows
only this exact command without a password:
```text
/usr/local/bin/deploy-labyricorn
```
Useful checks:
```bash
systemctl status gitea-runner
journalctl -u gitea-runner -n 100 --no-pager
sudo -l -U gitea-runner
```
If the runner must be replaced, delete or disable it in **Repository Settings →
Actions → Runners**, generate a fresh repository registration token, and
register the replacement. Registration tokens and `.runner` contents are
credentials: never commit or print them. A push can still be deployed manually
with `sudo deploy-labyricorn` while the runner is unavailable.
## Change-control checklist