upaas/README.md

182 lines
5.4 KiB
Markdown

# upaas
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.24+
- 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
MIT