Change all references from "upaas" to "µPaaS" in page titles, headers, and README. Add attribution link to sneak.berlin in the navigation bar.
182 lines
5.4 KiB
Markdown
182 lines
5.4 KiB
Markdown
# µPaaS by [@sneak](https://sneak.berlin)
|
|
|
|
A simple self-hosted PaaS that auto-deploys Docker containers from Git repositories via Gitea webhooks.
|
|
|
|
## Features
|
|
|
|
- Single admin user with argon2id password hashing
|
|
- Per-app SSH keypairs for read-only deploy keys
|
|
- Per-app UUID-based webhook URLs for Gitea integration
|
|
- Branch filtering - only deploy on configured branch changes
|
|
- Environment variables, labels, and volume mounts per app
|
|
- Docker builds via socket access
|
|
- Notifications via ntfy and Slack-compatible webhooks
|
|
- Simple server-rendered UI with Tailwind CSS
|
|
|
|
## Non-Goals
|
|
|
|
- Multi-user support
|
|
- Complex CI pipelines
|
|
- Multiple container orchestration
|
|
- SPA/API-first design
|
|
- Support for non-Gitea webhooks
|
|
|
|
## Architecture
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
upaas/
|
|
├── cmd/upaasd/ # Application entry point
|
|
├── internal/
|
|
│ ├── config/ # Configuration via Viper
|
|
│ ├── database/ # SQLite database with migrations
|
|
│ ├── docker/ # Docker client for builds/deploys
|
|
│ ├── globals/ # Build-time variables (version, etc.)
|
|
│ ├── handlers/ # HTTP request handlers
|
|
│ ├── healthcheck/ # Health status service
|
|
│ ├── logger/ # Structured logging (slog)
|
|
│ ├── middleware/ # HTTP middleware (auth, logging, CORS)
|
|
│ ├── models/ # Active Record style database models
|
|
│ ├── server/ # HTTP server and routes
|
|
│ ├── service/
|
|
│ │ ├── app/ # App management service
|
|
│ │ ├── auth/ # Authentication service
|
|
│ │ ├── deploy/ # Deployment orchestration
|
|
│ │ ├── notify/ # Notifications (ntfy, Slack)
|
|
│ │ └── webhook/ # Gitea webhook processing
|
|
│ └── ssh/ # SSH key generation
|
|
├── static/ # Embedded CSS/JS assets
|
|
└── templates/ # Embedded HTML templates
|
|
```
|
|
|
|
### Dependency Injection
|
|
|
|
Uses Uber fx for dependency injection. Components are wired in this order:
|
|
|
|
1. `globals` - Build-time variables
|
|
2. `logger` - Structured logging
|
|
3. `config` - Configuration loading
|
|
4. `database` - SQLite connection + migrations
|
|
5. `healthcheck` - Health status
|
|
6. `auth` - Authentication service
|
|
7. `app` - App management
|
|
8. `docker` - Docker client
|
|
9. `notify` - Notification service
|
|
10. `deploy` - Deployment service
|
|
11. `webhook` - Webhook processing
|
|
12. `middleware` - HTTP middleware
|
|
13. `handlers` - HTTP handlers
|
|
14. `server` - HTTP server
|
|
|
|
### Request Flow
|
|
|
|
```
|
|
HTTP Request
|
|
│
|
|
▼
|
|
chi Router ──► Middleware Stack ──► Handler
|
|
│
|
|
(Logging, Auth, CORS, etc.)
|
|
│
|
|
▼
|
|
Handler Function
|
|
│
|
|
▼
|
|
Service Layer (app, auth, deploy, etc.)
|
|
│
|
|
▼
|
|
Models (Active Record)
|
|
│
|
|
▼
|
|
Database
|
|
```
|
|
|
|
### Key Patterns
|
|
|
|
- **Closure-based handlers**: Handlers return `http.HandlerFunc` allowing one-time initialization
|
|
- **Active Record models**: Models encapsulate database operations (`Save()`, `Delete()`, `Reload()`)
|
|
- **Async deployments**: Webhook triggers deploy via goroutine with `context.WithoutCancel()`
|
|
- **Embedded assets**: Templates and static files embedded via `//go:embed`
|
|
|
|
## Development
|
|
|
|
### Prerequisites
|
|
|
|
- Go 1.25+
|
|
- golangci-lint
|
|
- Docker (for running)
|
|
|
|
### Commands
|
|
|
|
```bash
|
|
make fmt # Format code
|
|
make lint # Run comprehensive linting
|
|
make test # Run tests with race detection
|
|
make check # Verify everything passes (lint, test, build, format)
|
|
make build # Build binary
|
|
```
|
|
|
|
### Commit Requirements
|
|
|
|
**All commits must pass `make check` before being committed.**
|
|
|
|
Before every commit:
|
|
|
|
1. **Format**: Run `make fmt` to format all code
|
|
2. **Lint**: Run `make lint` and fix all errors/warnings
|
|
- Do not disable linters or add nolint comments without good reason
|
|
- Fix the code, don't hide the problem
|
|
3. **Test**: Run `make test` and ensure all tests pass
|
|
- Fix failing tests by fixing the code, not by modifying tests to pass
|
|
- Add tests for new functionality
|
|
4. **Verify**: Run `make check` to confirm everything passes
|
|
|
|
```bash
|
|
# Standard workflow before commit:
|
|
make fmt
|
|
make lint # Fix any issues
|
|
make test # Fix any failures
|
|
make check # Final verification
|
|
git add .
|
|
git commit -m "Your message"
|
|
```
|
|
|
|
The Docker build runs `make check` and will fail if:
|
|
- Code is not formatted
|
|
- Linting errors exist
|
|
- Tests fail
|
|
- Code doesn't compile
|
|
|
|
This ensures the main branch always contains clean, tested, working code.
|
|
|
|
## Configuration
|
|
|
|
Environment variables:
|
|
|
|
| Variable | Description | Default |
|
|
|----------|-------------|---------|
|
|
| `PORT` | HTTP listen port | 8080 |
|
|
| `UPAAS_DATA_DIR` | Data directory for SQLite and keys | ./data |
|
|
| `UPAAS_DOCKER_HOST` | Docker socket path | unix:///var/run/docker.sock |
|
|
| `DEBUG` | Enable debug logging | false |
|
|
| `SENTRY_DSN` | Sentry error reporting DSN | "" |
|
|
| `METRICS_USERNAME` | Basic auth for /metrics | "" |
|
|
| `METRICS_PASSWORD` | Basic auth for /metrics | "" |
|
|
|
|
## Running with Docker
|
|
|
|
```bash
|
|
docker run -d \
|
|
-p 8080:8080 \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-v upaas-data:/var/lib/upaas \
|
|
upaas
|
|
```
|
|
|
|
Session secrets are automatically generated on first startup and persisted to `$UPAAS_DATA_DIR/session.key`.
|
|
|
|
## License
|
|
|
|
WTFPL
|