add AUTOMATED_DEV.md: Gitea + Mattermost + CI + µPaaS pipeline
All checks were successful
check / check (push) Successful in 12s
All checks were successful
check / check (push) Successful in 12s
This commit is contained in:
parent
0db057ff4a
commit
3950e33fff
249
AUTOMATED_DEV.md
Normal file
249
AUTOMATED_DEV.md
Normal file
@ -0,0 +1,249 @@
|
||||
# Automated Development Systems
|
||||
|
||||
How we connect self-hosted Gitea, Mattermost, CI, and a lightweight PaaS into a
|
||||
continuous development pipeline where code goes from PR to production with
|
||||
minimal human intervention.
|
||||
|
||||
---
|
||||
|
||||
## The Hub: Self-Hosted Gitea
|
||||
|
||||
[Gitea](https://gitea.io) is the coordination center. Every repo, issue, PR, and
|
||||
code review lives here. We self-host it because:
|
||||
|
||||
- Full API access for automation (the agent has its own Gitea user with API
|
||||
token)
|
||||
- Gitea Actions for CI (compatible with GitHub Actions syntax)
|
||||
- Webhooks on every repo event
|
||||
- No vendor lock-in, no rate limits, no surprise pricing changes
|
||||
- Complete control over visibility (public/private per-repo)
|
||||
|
||||
The agent (OpenClaw) has its own Gitea account (`clawbot`) separate from the
|
||||
human user. This matters because:
|
||||
|
||||
- The agent's commits, comments, and PR reviews are clearly attributed
|
||||
- The human can @-mention the agent in issues to assign work
|
||||
- Assignment is unambiguous — issues assigned to `clawbot` are the agent's
|
||||
queue, issues assigned to the human are theirs
|
||||
- The agent can be given write access per-repo (some repos it can push to
|
||||
directly, others it must fork and PR)
|
||||
- API rate limits and permissions are independent
|
||||
|
||||
## Real-Time Activity Feed: Gitea → Mattermost
|
||||
|
||||
Every repo has a Gitea webhook that sends all activity (pushes, PRs, issues,
|
||||
comments, reviews, CI status) to a channel in our self-hosted
|
||||
[Mattermost](https://mattermost.com) instance. This creates a real-time feed
|
||||
where everyone — human and agent — can see what's happening across all projects
|
||||
without cross-communication overhead.
|
||||
|
||||
The agent also has its own Mattermost bot user (`@claw`), separate from the
|
||||
human. This means:
|
||||
|
||||
- The agent posts status updates to dedicated channels (`#git` for Gitea work,
|
||||
`#claw` for general work narration)
|
||||
- The human's DMs stay clean — only direct alerts and responses
|
||||
- In group channels, it's clear who said what
|
||||
- The agent can be @-mentioned in any channel
|
||||
|
||||
### Channel Architecture
|
||||
|
||||
A practical setup:
|
||||
|
||||
- **#git** — Real-time Gitea webhook feed (all repos) + agent's work status
|
||||
updates. Everyone sees commits, PRs, reviews, CI results as they happen.
|
||||
- **#claw** — Agent's internal work narration. Useful for debugging what the
|
||||
agent is doing, but notifications muted so it doesn't disturb anyone.
|
||||
- **DM with agent** — Private conversation, sitreps, sensitive commands
|
||||
- **Project-specific channels** — For coordination with external collaborators
|
||||
|
||||
The webhook feed in #git means nobody needs to check Gitea's notification inbox
|
||||
manually. PRs, reviews, and CI results flow into a channel that's always open.
|
||||
The agent monitors the same feed (via its notification poller) and can react to
|
||||
events in near-realtime.
|
||||
|
||||
## CI: Gitea Actions
|
||||
|
||||
Every repo has a CI workflow in `.gitea/workflows/` that runs on push. The
|
||||
standard workflow is one line:
|
||||
|
||||
```yaml
|
||||
- name: Build and check
|
||||
run: docker build .
|
||||
```
|
||||
|
||||
Because the Dockerfile runs `make check` (which runs tests, linting, and
|
||||
formatting checks), a successful Docker build means everything passes. A failed
|
||||
build means something is broken. Binary signal, no ambiguity.
|
||||
|
||||
### PR Preview Deployments
|
||||
|
||||
For web projects (like a blog or documentation site), CI can go further than
|
||||
pass/fail. When a PR is opened against a site repo, CI can:
|
||||
|
||||
1. Build the site from the PR branch
|
||||
2. Deploy it to a preview URL
|
||||
3. Post the preview URL as a comment on the PR or drop it into a Mattermost
|
||||
channel
|
||||
|
||||
This lets reviewers see the actual rendered result before merging, not just the
|
||||
code diff. For a Jekyll/Hugo blog, this means you can see how a new post looks
|
||||
on the real site layout, on mobile, with real CSS — before it goes live.
|
||||
|
||||
## Deployment: µPaaS
|
||||
|
||||
[µPaaS](https://git.eeqj.de/sneak/upaas) is a lightweight, self-hosted
|
||||
platform-as-a-service that auto-deploys Docker containers when code changes. It
|
||||
exists because:
|
||||
|
||||
- Full PaaS platforms (Kubernetes, Nomad, etc.) are massive overkill for a small
|
||||
fleet of services
|
||||
- Heroku/Render/Fly.io mean vendor dependency and recurring costs
|
||||
- We wanted: push to main → live in production, automatically, with zero human
|
||||
intervention
|
||||
|
||||
### How It Works
|
||||
|
||||
µPaaS is a single Go binary that:
|
||||
|
||||
1. **Receives Gitea webhooks** on push/merge events
|
||||
2. **Clones the repo** using deploy keys (read-only SSH keys per-repo)
|
||||
3. **Runs `docker build`** to build the new image
|
||||
4. **Swaps the running container** with the new image
|
||||
5. **Routes traffic** via Traefik reverse proxy with automatic TLS
|
||||
|
||||
The deploy flow:
|
||||
|
||||
```
|
||||
Developer merges PR to main/prod
|
||||
→ Gitea fires webhook to µPaaS
|
||||
→ µPaaS clones repo, builds Docker image
|
||||
→ µPaaS stops old container, starts new one
|
||||
→ Traefik routes traffic to new container
|
||||
→ Site is live on its production URL with TLS
|
||||
```
|
||||
|
||||
Time from merge to live: typically under 2 minutes (dominated by Docker build
|
||||
time).
|
||||
|
||||
### Deploy Keys
|
||||
|
||||
Each repo that deploys via µPaaS has a read-only SSH deploy key. This means:
|
||||
|
||||
- µPaaS can clone the repo to build it, but can't push to it
|
||||
- Each key is scoped to one repo — compromise of one key doesn't affect others
|
||||
- No shared credentials, no broad API tokens
|
||||
|
||||
### What's Deployed This Way
|
||||
|
||||
Any Docker-based service or site:
|
||||
|
||||
- Static sites (Jekyll, Hugo) — Dockerfile builds the site, nginx serves it
|
||||
- Go services — Dockerfile builds the binary, runs it
|
||||
- Web applications — same pattern
|
||||
|
||||
Everything gets a production URL with automatic TLS via Traefik.
|
||||
|
||||
## The Full Pipeline
|
||||
|
||||
Putting it all together, the development lifecycle looks like this:
|
||||
|
||||
```
|
||||
1. Issue filed in Gitea (by human or agent)
|
||||
↓
|
||||
2. Agent picks up the issue (via notification poller)
|
||||
↓
|
||||
3. Agent posts "starting work on #N" to Mattermost #git
|
||||
↓
|
||||
4. Agent (or sub-agent) creates branch, writes code, pushes
|
||||
↓
|
||||
5. Gitea webhook fires → #git shows the push
|
||||
↓
|
||||
6. CI runs docker build → passes or fails
|
||||
↓
|
||||
7. Agent creates PR "(closes #N)"
|
||||
↓
|
||||
8. Gitea webhook fires → #git shows the PR
|
||||
↓
|
||||
9. Agent reviews code, runs make check locally, verifies
|
||||
↓
|
||||
10. Agent assigns PR to human when all checks pass
|
||||
↓
|
||||
11. Human reviews, requests changes or approves
|
||||
↓
|
||||
12. If changes requested → agent reworks, back to step 6
|
||||
↓
|
||||
13. Human merges PR
|
||||
↓
|
||||
14. Gitea webhook fires → µPaaS deploys to production
|
||||
↓
|
||||
15. Gitea webhook fires → #git shows the merge
|
||||
↓
|
||||
16. Site/service is live on production URL
|
||||
```
|
||||
|
||||
Steps 2-10 can happen without any human involvement. The human's role is reduced
|
||||
to: review the PR, approve or request changes, merge. Everything else is
|
||||
automated.
|
||||
|
||||
### Observability
|
||||
|
||||
Because everything flows through Mattermost channels:
|
||||
|
||||
- The human can glance at #git to see the current state of all projects
|
||||
- CI failures are immediately visible (Gitea Actions posts status)
|
||||
- Deployments are immediately visible (µPaaS can log to the same channel)
|
||||
- The agent's work narration in #claw shows what it's currently doing
|
||||
- No need to check multiple dashboards — one chat client shows everything
|
||||
|
||||
## Identity Separation
|
||||
|
||||
A key architectural decision: the agent has its own identity everywhere.
|
||||
|
||||
| System | Human Account | Agent Account |
|
||||
| ---------- | ------------- | ------------- |
|
||||
| Gitea | @sneak | @clawbot |
|
||||
| Mattermost | @sneak | @claw |
|
||||
|
||||
This separation means:
|
||||
|
||||
- **Clear attribution.** Every commit, comment, and message shows who did it.
|
||||
When reviewing git history, you know which commits were human and which were
|
||||
agent.
|
||||
- **Independent permissions.** The agent can have write access to repos where
|
||||
it's trusted, fork-and-PR access where it's not. The human can have admin
|
||||
access without the agent inheriting it.
|
||||
- **Mentionability.** The human can @-mention the agent in an issue comment to
|
||||
assign work. The agent can @-mention the human when it needs review. This
|
||||
works exactly like human-to-human collaboration.
|
||||
- **Separate notification streams.** The agent's notification poller watches
|
||||
`@clawbot`'s inbox. The human's notifications are separate. No cross-talk.
|
||||
|
||||
## Why Self-Host Everything
|
||||
|
||||
The stack — Gitea, Mattermost, µPaaS, OpenClaw — is entirely self-hosted. This
|
||||
isn't ideological; it's practical:
|
||||
|
||||
- **No API rate limits.** The agent makes dozens of API calls per hour to Gitea.
|
||||
GitHub's API limits would throttle it.
|
||||
- **No surprise costs.** CI minutes, seat licenses, storage — all free when
|
||||
self-hosted.
|
||||
- **Full API access.** Every feature of every tool is available via API. No
|
||||
"enterprise only" gates.
|
||||
- **Custom webhooks.** We can wire up any event to any action. Gitea push →
|
||||
Mattermost notification → µPaaS deploy → agent notification, all custom.
|
||||
- **Data sovereignty.** Code, issues, conversations, and deployment
|
||||
infrastructure all live on machines we control.
|
||||
- **Offline resilience.** If GitHub/Slack/Vercel have an outage, our pipeline
|
||||
keeps running.
|
||||
|
||||
The trade-off is maintenance burden, but with an AI agent handling most of the
|
||||
operational work (monitoring, updates, issue triage), the maintenance cost is
|
||||
surprisingly low.
|
||||
|
||||
---
|
||||
|
||||
_This document describes a production system that's been running since
|
||||
early 2026. The specific tools (Gitea, Mattermost, µPaaS) are interchangeable —
|
||||
the patterns (webhook-driven deployment, real-time activity feeds, identity
|
||||
separation, automated CI gates) apply to any self-hosted stack._
|
||||
@ -7,6 +7,11 @@ agent) under the direction of [@sneak](https://sneak.berlin).
|
||||
|
||||
## What's Here
|
||||
|
||||
- **[AUTOMATED_DEV.md](AUTOMATED_DEV.md)** — How self-hosted Gitea, Mattermost,
|
||||
Gitea Actions CI, and µPaaS connect into a continuous pipeline where code goes
|
||||
from PR to production automatically. Covers webhook-driven deployments,
|
||||
real-time activity feeds, PR preview sites, identity separation (agent vs
|
||||
human accounts), and why self-hosting the entire stack matters.
|
||||
- **[OPENCLAW_TRICKS.md](OPENCLAW_TRICKS.md)** — Tested configuration recipes
|
||||
for OpenClaw agents: workspace bootstrapping, daily context state files,
|
||||
sitrep (situation report) prompts, sleep tracking, medication tracking,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user