Add Gitea Actions workflows, CI config, and docs
This commit is contained in:
108
docs/AI.md
Normal file
108
docs/AI.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# AI Workflows — ${REPO_NAME}
|
||||
|
||||
## Overview
|
||||
|
||||
This template includes five AI-powered workflows that use the
|
||||
[OpenRabbit](https://git.hiddenden.cafe/Hiddenden/openrabbit) tooling
|
||||
to provide automated code review, issue triage, and interactive chat
|
||||
via a bot (default: `@codebot` / user `Bartender`).
|
||||
|
||||
All AI workflows check out the central `Hiddenden/openrabbit` repo at runtime
|
||||
and execute its Python-based tools. No AI code lives in your repository.
|
||||
|
||||
## Workflows
|
||||
|
||||
### 1. Enterprise AI Code Review (`enterprise-ai-review.yml`)
|
||||
|
||||
- **Trigger**: Pull request opened or updated
|
||||
- **What it does**: Automatically reviews PR diffs for code quality, bugs,
|
||||
security issues, and style. Posts findings as PR comments.
|
||||
- **Severity gating**: If the review finds HIGH severity issues, CI fails
|
||||
(optional — see the `Check Review Result` step).
|
||||
|
||||
### 2. AI Issue Triage (`ai-issue-triage.yml`)
|
||||
|
||||
- **Trigger**: Comment containing `@codebot triage` on any issue
|
||||
- **What it does**: Analyzes the issue content and applies appropriate labels,
|
||||
priority, and category suggestions.
|
||||
|
||||
### 3. AI Comment Reply (`ai-comment-reply.yml`)
|
||||
|
||||
- **Trigger**: Comment containing a specific `@codebot` command
|
||||
- **Supported commands**:
|
||||
- `@codebot help` — show available commands
|
||||
- `@codebot explain` — explain code or issue context
|
||||
- `@codebot suggest` — suggest improvements
|
||||
- `@codebot security` — security-focused analysis
|
||||
- `@codebot summarize` — summarize a thread
|
||||
- `@codebot changelog` — generate changelog entries
|
||||
- `@codebot explain-diff` — explain PR diff
|
||||
- `@codebot review-again` — re-run review
|
||||
- `@codebot setup-labels` — configure repo labels
|
||||
- **PR vs Issue**: Automatically detects whether the comment is on a PR or issue
|
||||
and dispatches accordingly.
|
||||
|
||||
### 4. AI Chat (`ai-chat.yml`)
|
||||
|
||||
- **Trigger**: Comment mentioning `@codebot` that is NOT a known command
|
||||
- **What it does**: Free-form AI chat. Ask the bot any question and it will
|
||||
respond using the codebase context and optional web search (SearXNG).
|
||||
- **Routing**: This is the fallback — only fires when no specific command matches.
|
||||
|
||||
### 5. AI Codebase Quality Review (`ai-codebase-review.yml`)
|
||||
|
||||
- **Trigger**: Manual (workflow_dispatch) or scheduled (weekly, commented out)
|
||||
- **What it does**: Full codebase analysis generating a quality report.
|
||||
- **Report types**: `full`, `security`, `quick` (selectable on manual trigger).
|
||||
|
||||
## Required Secrets
|
||||
|
||||
All AI workflows require these secrets in your repository
|
||||
(Settings → Actions → Secrets):
|
||||
|
||||
| Secret | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `AI_REVIEW_TOKEN` | Yes | Gitea PAT with repo access (to check out OpenRabbit and post comments) |
|
||||
| `OPENAI_API_KEY` | Conditional | OpenAI API key (if using OpenAI models) |
|
||||
| `OPENROUTER_API_KEY` | Conditional | OpenRouter API key (if using OpenRouter) |
|
||||
| `OLLAMA_HOST` | Conditional | Ollama server URL (if using self-hosted models) |
|
||||
| `SEARXNG_URL` | Optional | SearXNG instance URL for web search in AI chat |
|
||||
|
||||
At least one AI provider key (`OPENAI_API_KEY`, `OPENROUTER_API_KEY`, or
|
||||
`OLLAMA_HOST`) must be set.
|
||||
|
||||
## Customization
|
||||
|
||||
### Changing the Bot Name
|
||||
|
||||
The default bot is `@codebot` (Gitea user: `Bartender`). To change it:
|
||||
|
||||
1. Update the `if:` conditions in all AI workflows to match your bot's mention prefix.
|
||||
2. Update the `github.event.comment.user.login != 'Bartender'` check to your bot's username.
|
||||
3. Update `config.yml` in the OpenRabbit tooling if applicable.
|
||||
|
||||
### Loop Prevention
|
||||
|
||||
All AI workflows check `github.event.comment.user.login != 'Bartender'` to
|
||||
prevent the bot from responding to its own comments. This is critical —
|
||||
without it, the bot can trigger infinite loops.
|
||||
|
||||
### Workflow Routing
|
||||
|
||||
The three comment-triggered workflows are carefully routed to avoid duplicates:
|
||||
|
||||
```
|
||||
Issue comment with @codebot
|
||||
├── Contains "triage"? → ai-issue-triage.yml
|
||||
├── Contains known command? → ai-comment-reply.yml
|
||||
└── Free-form mention? → ai-chat.yml (fallback)
|
||||
```
|
||||
|
||||
## Enabling / Disabling
|
||||
|
||||
To disable AI workflows without deleting them, either:
|
||||
- Remove the workflow files from `.gitea/workflows/`
|
||||
- Or comment out the `on:` triggers in each file
|
||||
|
||||
To enable the scheduled codebase review, uncomment the `schedule` trigger
|
||||
in `ai-codebase-review.yml`.
|
||||
52
docs/CI.md
Normal file
52
docs/CI.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# CI Pipeline — ${REPO_NAME}
|
||||
|
||||
## Overview
|
||||
|
||||
The CI workflow (`.gitea/workflows/ci.yml`) runs on every push and pull request.
|
||||
It auto-detects the project type and runs the appropriate tools.
|
||||
|
||||
## Detection Logic
|
||||
|
||||
The workflow checks for project files in this order:
|
||||
|
||||
### Python Detection
|
||||
- **Trigger**: `requirements.txt`, `setup.py`, or `pyproject.toml` exists
|
||||
- **Actions**:
|
||||
1. Set up Python 3.x
|
||||
2. `pip install -r requirements.txt` (if present)
|
||||
3. Install dev tools: ruff, black, flake8, pytest
|
||||
4. **Lint**: Run ruff, black --check, and flake8 (each skipped if not installed)
|
||||
5. **Test**: Run pytest (only if `tests/` dir or pytest config detected)
|
||||
|
||||
### Node.js Detection
|
||||
- **Trigger**: `package.json` exists
|
||||
- **Actions**:
|
||||
1. Set up Node.js (LTS)
|
||||
2. `npm ci`
|
||||
3. **Lint**: `npm run lint` (only if "lint" script exists in package.json)
|
||||
4. **Test**: `npm test` (only if "test" script exists)
|
||||
5. **Build**: `npm run build` (only if "build" script exists)
|
||||
|
||||
### No Project Detected
|
||||
- If neither Python nor Node.js files are found, the workflow prints a message
|
||||
and exits successfully. **It never fails due to missing language detection.**
|
||||
|
||||
## Strict Mode
|
||||
|
||||
Controlled by `CI_STRICT` in `.ci/config.env`:
|
||||
|
||||
| CI_STRICT | Behavior |
|
||||
|-----------|----------|
|
||||
| `true` (default) | Lint/test failures cause the workflow to fail |
|
||||
| `false` | Failures are logged as warnings; workflow succeeds |
|
||||
|
||||
Use `CI_STRICT=false` during early development when you want visibility
|
||||
into issues without blocking merges.
|
||||
|
||||
## Adding Support for Other Languages
|
||||
|
||||
To add support for another language (Go, Rust, etc.):
|
||||
|
||||
1. Add a detection step similar to the Python/Node checks.
|
||||
2. Add setup, lint, and test steps conditional on detection.
|
||||
3. Follow the same CI_STRICT pattern for error handling.
|
||||
100
docs/CONFIG.md
Normal file
100
docs/CONFIG.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Configuration Reference — ${REPO_NAME}
|
||||
|
||||
All settings live in **`.ci/config.env`** and are loaded by every workflow at runtime.
|
||||
|
||||
## CI Settings
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_CI` | `true` | Master switch. If `false`, the CI workflow exits immediately. |
|
||||
| `CI_STRICT` | `true` | If `true`, lint/test failures cause the workflow to fail. If `false`, they are logged as warnings only. |
|
||||
| `DEFAULT_BRANCH` | `main` | The primary branch. Used by Docker and other workflows to determine branch-push behavior. |
|
||||
|
||||
## Docker Settings
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_DOCKER` | `true` | Master switch for Docker build steps. |
|
||||
| `DOCKER_PUSH` | `false` | Whether to push images to the registry. **Safe default: off.** |
|
||||
| `DOCKER_PUSH_ON_BRANCH` | `true` | Push when a commit lands on `DEFAULT_BRANCH`. Only effective if `DOCKER_PUSH=true`. |
|
||||
| `DOCKER_PUSH_ON_TAG` | `true` | Push when a semver tag (`v*`) is pushed. Only effective if `DOCKER_PUSH=true`. |
|
||||
| `REGISTRY_HOST` | `git.hiddenden.cafe` | Hostname of the container registry. |
|
||||
| `IMAGE_OWNER` | `auto` | Image owner (org/user). `auto` = derived from repository context at runtime. |
|
||||
| `IMAGE_NAME` | `auto` | Image name. `auto` = derived from repository name at runtime. |
|
||||
| `DOCKER_TAG_STRATEGY` | `semver+latest` | Controls tagging. Options: `semver+latest`, `semver`, `branch`. |
|
||||
|
||||
### Tag Strategy Details
|
||||
|
||||
| Trigger | `semver+latest` | `semver` | `branch` |
|
||||
|---------|-----------------|----------|----------|
|
||||
| `v1.2.3` tag | `:1.2.3` + `:latest` | `:1.2.3` | — |
|
||||
| Push to `main` | `:main` | `:main` | `:main` |
|
||||
| Pull request | `:pr-<N>` (local only) | `:pr-<N>` (local only) | `:pr-<N>` (local only) |
|
||||
|
||||
## Security Settings
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_SECURITY` | `false` | Master switch. Enables gitleaks, osv-scanner, and Trivy. |
|
||||
| `STRICT_SECURITY` | `false` | If `true`, any finding fails the workflow. If `false`, findings are warnings. |
|
||||
|
||||
## Renovate Settings
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_RENOVATE` | `false` | Master switch for Renovate dependency updates. |
|
||||
| `RENOVATE_SCHEDULE` | `weekly` | How often Renovate runs. |
|
||||
| `RENOVATE_PR_LIMIT` | `5` | Max open PRs Renovate can create. |
|
||||
|
||||
## Deploy Settings
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_DEPLOY` | `false` | Master switch. Deploy never runs unless `true`. |
|
||||
| `DEPLOY_MODE` | `local-runner` | How to reach the VPS: `local-runner` (runs on VPS directly) or `ssh` (SSH from any runner). |
|
||||
| `DEPLOY_RUNNER_LABEL` | `deploy-ovh` | Runner label for local-runner mode. Must match the act_runner's registered label. |
|
||||
| `DEPLOY_WORKDIR` | `/opt/${REPO_NAME}` | Working directory on the VPS where your project lives. |
|
||||
| `DEPLOY_STRATEGY` | `compose` | What to do on deploy: `compose` (docker compose up), `systemd` (restart service), or `script` (run custom script). |
|
||||
| `DEPLOY_COMPOSE_FILE` | `docker-compose.yml` | Compose file path relative to `DEPLOY_WORKDIR`. Used with `compose` strategy. |
|
||||
| `DEPLOY_SYSTEMD_SERVICE` | _(empty)_ | Systemd service name. Required if `DEPLOY_STRATEGY=systemd`. |
|
||||
| `DEPLOY_SCRIPT` | `scripts/deploy.sh` | Custom deploy script path relative to repo root. Used with `script` strategy. |
|
||||
| `DEPLOY_ON_TAG` | `false` | Also deploy when a `v*` tag is pushed. |
|
||||
|
||||
### Deploy Mode Comparison
|
||||
|
||||
| | `local-runner` | `ssh` |
|
||||
|---|---|---|
|
||||
| Secrets needed | None | `DEPLOY_SSH_KEY`, `DEPLOY_HOST`, `DEPLOY_USER` |
|
||||
| Runner location | On the VPS | Any runner (e.g., shared) |
|
||||
| Setup effort | Install act_runner on VPS | Create SSH key + add secrets |
|
||||
| Network exposure | None | SSH port must be reachable |
|
||||
|
||||
See [docs/DEPLOY.md](DEPLOY.md) for full setup instructions.
|
||||
|
||||
## Recommended Defaults
|
||||
|
||||
For a **new public project**:
|
||||
```env
|
||||
ENABLE_CI=true
|
||||
CI_STRICT=true
|
||||
ENABLE_DOCKER=true
|
||||
DOCKER_PUSH=false # Enable when ready to publish
|
||||
ENABLE_SECURITY=false # Enable after initial development
|
||||
ENABLE_RENOVATE=false # Enable after first release
|
||||
ENABLE_DEPLOY=false # Enable when VPS runner is set up
|
||||
```
|
||||
|
||||
For a **production project**:
|
||||
```env
|
||||
ENABLE_CI=true
|
||||
CI_STRICT=true
|
||||
ENABLE_DOCKER=true
|
||||
DOCKER_PUSH=true
|
||||
DOCKER_PUSH_ON_TAG=true
|
||||
ENABLE_SECURITY=true
|
||||
STRICT_SECURITY=true
|
||||
ENABLE_RENOVATE=true
|
||||
ENABLE_DEPLOY=true
|
||||
DEPLOY_MODE=local-runner
|
||||
DEPLOY_STRATEGY=compose
|
||||
```
|
||||
295
docs/DEPLOY.md
Normal file
295
docs/DEPLOY.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Deploy Workflow — ${REPO_NAME}
|
||||
|
||||
## Overview
|
||||
|
||||
The deploy workflow (`.gitea/workflows/deploy.yml`) automates deployment to a
|
||||
VPS after a push to the default branch. It supports two modes and three
|
||||
deployment strategies.
|
||||
|
||||
**Disabled by default.** Set `ENABLE_DEPLOY=true` in `.ci/config.env` to enable.
|
||||
|
||||
## Deployment Modes
|
||||
|
||||
### Mode A: Local Runner (Recommended)
|
||||
|
||||
The deploy job runs **directly on the VPS** via a self-hosted `act_runner`.
|
||||
No SSH keys or network credentials needed — the runner already has local access.
|
||||
|
||||
```
|
||||
┌──────────┐ push ┌──────────┐ runs-on: deploy-ovh ┌───────────┐
|
||||
│ Developer │ ────────────► │ Gitea │ ────────────────────────► │ VPS │
|
||||
│ │ │ Server │ │ act_runner│
|
||||
└──────────┘ └──────────┘ │ (local) │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- No SSH secrets to manage
|
||||
- No network exposure (runner is already on the VPS)
|
||||
- Simpler setup, fewer moving parts
|
||||
- Runner can access local Docker socket, systemd, files directly
|
||||
|
||||
**Cons:**
|
||||
- Requires installing `act_runner` on the VPS
|
||||
- One runner per VPS (or one runner with multiple labels)
|
||||
|
||||
### Mode B: SSH (Fallback)
|
||||
|
||||
The deploy job runs on **any runner** (e.g., shared ubuntu-latest) and SSHs
|
||||
into the VPS to execute deploy commands remotely.
|
||||
|
||||
```
|
||||
┌──────────┐ push ┌──────────┐ runs job ┌────────┐ SSH ┌─────┐
|
||||
│ Developer │ ────────────► │ Gitea │ ──────────────► │ Runner │ ────────► │ VPS │
|
||||
└──────────┘ └──────────┘ │(shared)│ └─────┘
|
||||
└────────┘
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Works without installing anything on the VPS
|
||||
- Can deploy to VPSes you don't fully control
|
||||
|
||||
**Cons:**
|
||||
- Requires SSH secrets (private key, host, user)
|
||||
- VPS SSH port must be reachable from the runner
|
||||
- More moving parts, more to go wrong
|
||||
|
||||
## Deploy Strategies
|
||||
|
||||
Regardless of mode, the workflow supports three strategies for what actually
|
||||
happens on the VPS:
|
||||
|
||||
### `compose` (default)
|
||||
|
||||
```bash
|
||||
cd /opt/myapp
|
||||
docker compose -f docker-compose.yml pull
|
||||
docker compose -f docker-compose.yml up -d
|
||||
```
|
||||
|
||||
Best for: Docker Compose-based projects. Pulls latest images and recreates
|
||||
containers with zero-downtime (depends on your compose config).
|
||||
|
||||
### `systemd`
|
||||
|
||||
```bash
|
||||
sudo systemctl restart my-service
|
||||
```
|
||||
|
||||
Best for: Applications managed as systemd services (e.g., a Go binary,
|
||||
Python app with gunicorn, etc.).
|
||||
|
||||
### `script`
|
||||
|
||||
```bash
|
||||
./scripts/deploy.sh /opt/myapp
|
||||
```
|
||||
|
||||
Best for: Custom deploy logic that doesn't fit compose or systemd.
|
||||
The script receives `DEPLOY_WORKDIR` as `$1`.
|
||||
|
||||
## Setup Guide
|
||||
|
||||
### Local Runner Mode
|
||||
|
||||
#### Step 1: Install act_runner on the VPS
|
||||
|
||||
```bash
|
||||
# Download the latest act_runner binary
|
||||
wget https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-linux-amd64
|
||||
chmod +x act_runner-linux-amd64
|
||||
sudo mv act_runner-linux-amd64 /usr/local/bin/act_runner
|
||||
```
|
||||
|
||||
#### Step 2: Register the runner with your label
|
||||
|
||||
```bash
|
||||
# Register with Gitea (get the token from: Site Admin → Runners → Create)
|
||||
act_runner register \
|
||||
--instance https://git.hiddenden.cafe \
|
||||
--token YOUR_RUNNER_TOKEN \
|
||||
--labels deploy-ovh
|
||||
|
||||
# Or for repo-level: Repository Settings → Actions → Runners → Create
|
||||
```
|
||||
|
||||
#### Step 3: Configure runner labels
|
||||
|
||||
The runner config file (usually `~/.config/act_runner/config.yaml` or next to
|
||||
the binary) contains the labels:
|
||||
|
||||
```yaml
|
||||
runner:
|
||||
labels:
|
||||
- "deploy-ovh:host"
|
||||
```
|
||||
|
||||
The `:host` suffix means the job runs directly on the host (not in a container).
|
||||
This is important for accessing Docker, systemd, and local files.
|
||||
|
||||
**After changing labels, restart the runner:**
|
||||
```bash
|
||||
sudo systemctl restart act_runner
|
||||
```
|
||||
|
||||
#### Step 4: Update workflow runs-on
|
||||
|
||||
In `.gitea/workflows/deploy.yml`, the `deploy-local-runner` job has:
|
||||
```yaml
|
||||
runs-on: deploy-ovh
|
||||
```
|
||||
|
||||
If you changed `DEPLOY_RUNNER_LABEL` in config.env, you **must also** update
|
||||
this `runs-on` value in the workflow file to match. Gitea Actions does not
|
||||
support dynamic `runs-on` from environment variables.
|
||||
|
||||
#### Step 5: Enable deploy
|
||||
|
||||
In `.ci/config.env`:
|
||||
```env
|
||||
ENABLE_DEPLOY=true
|
||||
DEPLOY_MODE=local-runner
|
||||
DEPLOY_RUNNER_LABEL=deploy-ovh
|
||||
DEPLOY_WORKDIR=/opt/myapp
|
||||
DEPLOY_STRATEGY=compose
|
||||
```
|
||||
|
||||
#### Step 6: Verify VPS prerequisites
|
||||
|
||||
For `compose` strategy:
|
||||
```bash
|
||||
# Docker and compose plugin must be installed
|
||||
docker --version
|
||||
docker compose version
|
||||
|
||||
# The deploy workdir must exist with a docker-compose.yml
|
||||
ls /opt/myapp/docker-compose.yml
|
||||
```
|
||||
|
||||
For `systemd` strategy:
|
||||
```bash
|
||||
# The service must exist
|
||||
systemctl cat my-service
|
||||
```
|
||||
|
||||
For `script` strategy:
|
||||
```bash
|
||||
# The script must be in the repo and executable
|
||||
```
|
||||
|
||||
### SSH Mode
|
||||
|
||||
#### Step 1: Create an SSH key
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "deploy@gitea" -f deploy_key -N ""
|
||||
```
|
||||
|
||||
#### Step 2: Add the public key to the VPS
|
||||
|
||||
```bash
|
||||
ssh-copy-id -i deploy_key.pub user@your-vps
|
||||
```
|
||||
|
||||
#### Step 3: Add secrets to Gitea
|
||||
|
||||
Go to **Repository Settings → Actions → Secrets** and add:
|
||||
|
||||
| Secret | Value |
|
||||
|--------|-------|
|
||||
| `DEPLOY_SSH_KEY` | Contents of `deploy_key` (private key) |
|
||||
| `DEPLOY_HOST` | VPS IP or hostname |
|
||||
| `DEPLOY_USER` | SSH username |
|
||||
| `DEPLOY_KNOWN_HOSTS` | Output of `ssh-keyscan your-vps` (recommended) |
|
||||
|
||||
#### Step 4: Enable deploy
|
||||
|
||||
In `.ci/config.env`:
|
||||
```env
|
||||
ENABLE_DEPLOY=true
|
||||
DEPLOY_MODE=ssh
|
||||
DEPLOY_WORKDIR=/opt/myapp
|
||||
DEPLOY_STRATEGY=compose
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_DEPLOY` | `false` | Master switch. Deploy never runs unless `true`. |
|
||||
| `DEPLOY_MODE` | `local-runner` | `local-runner` or `ssh` |
|
||||
| `DEPLOY_RUNNER_LABEL` | `deploy-ovh` | Runner label for local-runner mode |
|
||||
| `DEPLOY_WORKDIR` | `/opt/${REPO_NAME}` | Working directory on the VPS |
|
||||
| `DEPLOY_STRATEGY` | `compose` | `compose`, `systemd`, or `script` |
|
||||
| `DEPLOY_COMPOSE_FILE` | `docker-compose.yml` | Compose file path (relative to workdir) |
|
||||
| `DEPLOY_SYSTEMD_SERVICE` | _(empty)_ | Systemd service name (required for systemd strategy) |
|
||||
| `DEPLOY_SCRIPT` | `scripts/deploy.sh` | Deploy script path (relative to repo root) |
|
||||
| `DEPLOY_ON_TAG` | `false` | Also deploy on `v*` tag pushes |
|
||||
|
||||
## Safety
|
||||
|
||||
### Branch Protection
|
||||
|
||||
The workflow only deploys from the `DEFAULT_BRANCH` (main). This is enforced
|
||||
in two places:
|
||||
|
||||
1. **Trigger filter**: `on.push.branches: [main]`
|
||||
2. **Branch guard step**: Runtime check comparing the actual ref to `DEFAULT_BRANCH`
|
||||
|
||||
### Never on Pull Requests
|
||||
|
||||
The `pull_request` event is intentionally absent from the trigger list.
|
||||
Deploy cannot run on PRs.
|
||||
|
||||
### Protected Branches
|
||||
|
||||
For maximum safety, enable **branch protection** on `main` in Gitea:
|
||||
|
||||
1. Go to **Repository Settings → Branches → Branch Protection**
|
||||
2. Enable for `main`
|
||||
3. Require: status checks to pass, review approval, no force push
|
||||
4. This ensures only reviewed, passing code reaches main → gets deployed
|
||||
|
||||
### Secrets
|
||||
|
||||
- **Local-runner mode**: No secrets needed. The runner has local access.
|
||||
- **SSH mode**: Private key is written to a temp file, used once, then deleted.
|
||||
It is never echoed to logs.
|
||||
|
||||
## Runner Label Examples
|
||||
|
||||
| Label | Description |
|
||||
|-------|-------------|
|
||||
| `deploy-ovh` | OVH VPS runner |
|
||||
| `vps-prod` | Production VPS |
|
||||
| `deploy-hetzner` | Hetzner VPS runner |
|
||||
| `staging-runner` | Staging environment |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Local Runner Mode
|
||||
|
||||
- [ ] **Runner registered?** Check Gitea Admin → Runners or Repo Settings → Runners
|
||||
- [ ] **Label matches?** `runs-on` in workflow must match the runner's label exactly
|
||||
- [ ] **Runner online?** Check runner status in Gitea; restart if offline
|
||||
- [ ] **Label suffix correct?** Use `:host` for direct host execution (not container)
|
||||
- [ ] **Docker accessible?** Runner user must be in the `docker` group
|
||||
- [ ] **Workdir exists?** `DEPLOY_WORKDIR` must exist and be readable
|
||||
- [ ] **Compose file present?** Check `docker-compose.yml` exists in the workdir
|
||||
- [ ] **Permissions?** For systemd: runner user needs sudo for systemctl
|
||||
|
||||
### SSH Mode
|
||||
|
||||
- [ ] **Secrets set?** Check all four secrets are in repo settings
|
||||
- [ ] **Key format?** Private key must include `-----BEGIN` header
|
||||
- [ ] **SSH port open?** VPS firewall must allow port 22 (or custom port) from runner
|
||||
- [ ] **User permissions?** SSH user needs access to Docker/systemd/workdir
|
||||
- [ ] **Known hosts?** Set `DEPLOY_KNOWN_HOSTS` for production (avoids MITM risk)
|
||||
- [ ] **Key not passphrase-protected?** CI keys must have no passphrase
|
||||
|
||||
### General
|
||||
|
||||
- [ ] **ENABLE_DEPLOY=true?** Check `.ci/config.env`
|
||||
- [ ] **Pushing to main?** Deploy only triggers on `DEFAULT_BRANCH`
|
||||
- [ ] **Workflow file correct?** YAML syntax errors prevent the workflow from running
|
||||
- [ ] **Check Actions tab** in Gitea for workflow run logs
|
||||
104
docs/DOCKER.md
Normal file
104
docs/DOCKER.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Docker Build & Registry — ${REPO_NAME}
|
||||
|
||||
## Overview
|
||||
|
||||
The Docker workflow (`.gitea/workflows/docker.yml`) builds Docker images and
|
||||
optionally pushes them to the Gitea Container Registry.
|
||||
|
||||
## Gitea Container Registry Naming Convention
|
||||
|
||||
Gitea's registry follows this pattern:
|
||||
|
||||
```
|
||||
{REGISTRY_HOST}/{OWNER}/{IMAGE}:{TAG}
|
||||
```
|
||||
|
||||
Example:
|
||||
```
|
||||
git.hiddenden.cafe/myorg/myapp:1.2.3
|
||||
```
|
||||
|
||||
This is different from Docker Hub (`docker.io/library/myapp:latest`).
|
||||
The workflow enforces this format automatically.
|
||||
|
||||
## Dynamic Owner/Repo Derivation
|
||||
|
||||
The workflow dynamically determines the image owner and name so it works
|
||||
for both user repos and organization repos without hardcoding.
|
||||
|
||||
**Logic:**
|
||||
1. Determine `FULL_REPO` from (in priority order):
|
||||
- `$GITEA_REPOSITORY` (Gitea native environment variable)
|
||||
- `${{ github.repository }}` (Gitea Actions compatibility layer)
|
||||
2. Split into `OWNER` (before `/`) and `REPO` (after `/`).
|
||||
3. If `IMAGE_OWNER=auto` in config → use `OWNER`; else use the config value.
|
||||
4. If `IMAGE_NAME=auto` in config → use `REPO`; else use the config value.
|
||||
|
||||
This means you rarely need to change `IMAGE_OWNER` or `IMAGE_NAME`.
|
||||
|
||||
## Triggers & Push Behavior
|
||||
|
||||
| Event | Build? | Push? | Condition |
|
||||
|-------|--------|-------|-----------|
|
||||
| Pull Request | Yes | **No** | Never pushes on PRs |
|
||||
| Push to `main` | Yes | Conditional | `DOCKER_PUSH=true` AND `DOCKER_PUSH_ON_BRANCH=true` |
|
||||
| Tag `v1.2.3` | Yes | Conditional | `DOCKER_PUSH=true` AND `DOCKER_PUSH_ON_TAG=true` |
|
||||
|
||||
**Safe default**: `DOCKER_PUSH=false` — images are built but never pushed.
|
||||
|
||||
## Tag Strategy
|
||||
|
||||
Controlled by `DOCKER_TAG_STRATEGY` in `.ci/config.env`:
|
||||
|
||||
### `semver+latest` (default)
|
||||
- Tag `v1.2.3` → pushes `:1.2.3` and `:latest`
|
||||
- Push to `main` → pushes `:main`
|
||||
|
||||
### `semver`
|
||||
- Tag `v1.2.3` → pushes `:1.2.3` only
|
||||
- Push to `main` → pushes `:main`
|
||||
|
||||
### `branch`
|
||||
- Branch pushes only, tagged as `:branchname`
|
||||
|
||||
## Required Secrets
|
||||
|
||||
To push images, set these secrets in your Gitea repository
|
||||
(Settings → Actions → Secrets):
|
||||
|
||||
| Secret | Description |
|
||||
|--------|-------------|
|
||||
| `REGISTRY_USERNAME` | Gitea username or bot account name |
|
||||
| `REGISTRY_TOKEN` | Personal Access Token with `package:write` scope |
|
||||
|
||||
### Creating a PAT
|
||||
|
||||
1. Go to **Settings → Applications → Generate New Token**
|
||||
2. Name: e.g., `ci-docker-push`
|
||||
3. Scopes: select **`package`** (read + write)
|
||||
4. Copy the token and add it as `REGISTRY_TOKEN` in repo secrets
|
||||
|
||||
**Why PAT instead of job token?**
|
||||
Gitea Actions job tokens may not have sufficient permissions for the
|
||||
container registry in all configurations. PATs are the recommended approach.
|
||||
|
||||
## Detection
|
||||
|
||||
The workflow auto-detects how to build:
|
||||
|
||||
1. **Dockerfile** → `docker build -t <image>:<tag> .`
|
||||
2. **docker-compose.yml** → `docker compose build`
|
||||
3. **Neither** → exits 0 with a message (graceful skip)
|
||||
|
||||
## Enabling Docker Push
|
||||
|
||||
1. Set `DOCKER_PUSH=true` in `.ci/config.env`
|
||||
2. Add `REGISTRY_USERNAME` and `REGISTRY_TOKEN` secrets
|
||||
3. Push a commit or tag — the workflow will build and push
|
||||
|
||||
## Pulling Images
|
||||
|
||||
After pushing, pull images with:
|
||||
```bash
|
||||
docker pull git.hiddenden.cafe/<owner>/<repo>:latest
|
||||
```
|
||||
101
docs/RENOVATE.md
Normal file
101
docs/RENOVATE.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Renovate — Automated Dependency Updates — ${REPO_NAME}
|
||||
|
||||
## Overview
|
||||
|
||||
[Renovate](https://docs.renovatebot.com/) automatically detects outdated
|
||||
dependencies and opens PRs to update them. This keeps your project secure
|
||||
and up-to-date with minimal manual effort.
|
||||
|
||||
**Disabled by default.** Set `ENABLE_RENOVATE=true` in `.ci/config.env` to enable.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. The workflow (`.gitea/workflows/renovate.yml`) runs on a schedule (default: weekly).
|
||||
2. Renovate scans your lockfiles and config for outdated packages.
|
||||
3. It opens PRs with updates, grouped by minor/patch to reduce noise.
|
||||
4. You review and merge the PRs.
|
||||
|
||||
## Setup
|
||||
|
||||
### Step 1: Create a Bot PAT
|
||||
|
||||
1. Create a dedicated Gitea user (e.g., `renovate-bot`) or use your own account.
|
||||
2. Generate a PAT: **Settings → Applications → Generate New Token**
|
||||
3. Scopes: `repo` (full repository access)
|
||||
4. Copy the token.
|
||||
|
||||
### Step 2: Add the Secret
|
||||
|
||||
1. Go to **Repository Settings → Actions → Secrets**
|
||||
2. Add secret: `RENOVATE_TOKEN` = the PAT from step 1
|
||||
|
||||
### Step 3: Enable in Config
|
||||
|
||||
In `.ci/config.env`:
|
||||
```env
|
||||
ENABLE_RENOVATE=true
|
||||
RENOVATE_SCHEDULE=weekly
|
||||
RENOVATE_PR_LIMIT=5
|
||||
```
|
||||
|
||||
### Step 4: Commit and Push
|
||||
|
||||
Renovate will run on the next scheduled time, or you can trigger it manually
|
||||
via the Actions tab → "Renovate" → "Run workflow".
|
||||
|
||||
## Configuration
|
||||
|
||||
### Workflow Config (.ci/config.env)
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_RENOVATE` | `false` | Master switch |
|
||||
| `RENOVATE_SCHEDULE` | `weekly` | How often to run |
|
||||
| `RENOVATE_PR_LIMIT` | `5` | Max open PRs at once |
|
||||
|
||||
### Renovate Config (renovate.json)
|
||||
|
||||
The `renovate.json` file in the repo root controls Renovate's behavior:
|
||||
|
||||
- **Grouping**: Minor and patch updates are grouped into a single PR.
|
||||
- **Docker**: Base image updates (`FROM ...`) are enabled.
|
||||
- **Labels**: PRs get the `dependencies` label.
|
||||
- **Schedule**: Runs before 6am on Mondays.
|
||||
|
||||
Customize `renovate.json` to:
|
||||
- Pin specific dependencies
|
||||
- Exclude packages
|
||||
- Change grouping strategy
|
||||
- Add automerge for low-risk updates
|
||||
|
||||
### Docker Base Image Updates
|
||||
|
||||
Renovate will detect `FROM` lines in your Dockerfile and open PRs when
|
||||
newer base images are available. This is enabled by default in `renovate.json`.
|
||||
|
||||
## Noise Control
|
||||
|
||||
To reduce PR spam:
|
||||
|
||||
1. **Group updates**: Already configured — minor/patch grouped together.
|
||||
2. **Limit PRs**: `RENOVATE_PR_LIMIT=5` (adjust as needed).
|
||||
3. **Schedule**: Runs weekly by default, not on every push.
|
||||
4. **Automerge**: Add to `renovate.json` for trusted updates:
|
||||
```json
|
||||
{
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": ["patch"],
|
||||
"automerge": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
After enabling, expect:
|
||||
- An initial burst of PRs for all outdated dependencies
|
||||
- Weekly batches of 1-5 PRs (depending on updates available)
|
||||
- PRs labeled `dependencies` for easy filtering
|
||||
- Each PR includes a changelog and compatibility notes
|
||||
Reference in New Issue
Block a user