Some checks failed
- Extract login/dashboard from billing module into core (matching admin pattern) - Add merchant auth API with path-isolated cookies (path=/merchants) - Add merchant base layout with sidebar/header partials and Alpine.js init - Add frontend detection and login redirect for MERCHANT type - Wire merchant token in shared api-client.js (get/clear) - Migrate billing templates to merchant base with dark mode support - Fix Tailwind: rename shop→storefront in sources and config - DRY Makefile tailwind targets with TAILWIND_FRONTENDS loop - Rebuild all Tailwind outputs (production minified) - Add Gitea Actions CI workflow (ruff, pytest, architecture, docs) - Add Gitea deployment documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
303 lines
7.9 KiB
Markdown
303 lines
7.9 KiB
Markdown
# Gitea CI/CD Deployment Guide
|
|
|
|
This document describes how to **self-host Gitea** on an external server and migrate CI/CD from GitLab to **Gitea Actions** (GitHub Actions-compatible).
|
|
|
|
---
|
|
|
|
## Why Gitea?
|
|
|
|
- Lightweight, self-hosted Git forge (single binary or Docker image)
|
|
- Built-in CI/CD via **Gitea Actions** (GitHub Actions-compatible YAML)
|
|
- Built-in migration tool imports repos, issues, and PRs from GitLab
|
|
- Low resource usage compared to GitLab
|
|
|
|
---
|
|
|
|
## 1. Server Prerequisites
|
|
|
|
```bash
|
|
# Ubuntu/Debian
|
|
sudo apt update && sudo apt install -y docker.io docker-compose-plugin
|
|
sudo systemctl enable --now docker
|
|
sudo usermod -aG docker $USER # log out/in after this
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Gitea Server Setup (Docker Compose)
|
|
|
|
Create a directory on your server:
|
|
|
|
```bash
|
|
mkdir -p ~/gitea && cd ~/gitea
|
|
```
|
|
|
|
Create `docker-compose.yml`:
|
|
|
|
```yaml
|
|
services:
|
|
gitea:
|
|
image: gitea/gitea:latest
|
|
container_name: gitea
|
|
restart: always
|
|
environment:
|
|
- USER_UID=1000
|
|
- USER_GID=1000
|
|
- GITEA__database__DB_TYPE=postgres
|
|
- GITEA__database__HOST=gitea-db:5432
|
|
- GITEA__database__NAME=gitea
|
|
- GITEA__database__USER=gitea
|
|
- GITEA__database__PASSWD=<CHANGE_ME>
|
|
- GITEA__server__ROOT_URL=https://git.yourdomain.com/
|
|
- GITEA__server__SSH_DOMAIN=git.yourdomain.com
|
|
- GITEA__server__DOMAIN=git.yourdomain.com
|
|
- GITEA__actions__ENABLED=true
|
|
volumes:
|
|
- gitea-data:/data
|
|
- /etc/timezone:/etc/timezone:ro
|
|
- /etc/localtime:/etc/localtime:ro
|
|
ports:
|
|
- "3000:3000" # Web UI
|
|
- "2222:22" # SSH (Git over SSH)
|
|
depends_on:
|
|
gitea-db:
|
|
condition: service_healthy
|
|
|
|
gitea-db:
|
|
image: postgres:15
|
|
container_name: gitea-db
|
|
restart: always
|
|
environment:
|
|
POSTGRES_DB: gitea
|
|
POSTGRES_USER: gitea
|
|
POSTGRES_PASSWORD: <CHANGE_ME>
|
|
volumes:
|
|
- gitea-db-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U gitea"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# Gitea Actions runner — executes CI workflows
|
|
gitea-runner:
|
|
image: gitea/act_runner:latest
|
|
container_name: gitea-runner
|
|
restart: always
|
|
environment:
|
|
GITEA_INSTANCE_URL: http://gitea:3000
|
|
GITEA_RUNNER_REGISTRATION_TOKEN: <RUNNER_TOKEN>
|
|
GITEA_RUNNER_NAME: default-runner
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
- gitea-runner-data:/data
|
|
depends_on:
|
|
- gitea
|
|
|
|
volumes:
|
|
gitea-data:
|
|
gitea-db-data:
|
|
gitea-runner-data:
|
|
```
|
|
|
|
!!! warning "Replace placeholders"
|
|
Replace `<CHANGE_ME>` with a strong database password and `<RUNNER_TOKEN>` with the token from step 4.
|
|
|
|
Start Gitea and the database first:
|
|
|
|
```bash
|
|
docker compose up -d gitea gitea-db
|
|
```
|
|
|
|
Visit `http://your-server-ip:3000` and complete the initial setup wizard.
|
|
|
|
---
|
|
|
|
## 3. Reverse Proxy with HTTPS (Nginx + Let's Encrypt)
|
|
|
|
```bash
|
|
sudo apt install -y nginx certbot python3-certbot-nginx
|
|
```
|
|
|
|
Create `/etc/nginx/sites-available/gitea`:
|
|
|
|
```nginx
|
|
server {
|
|
server_name git.yourdomain.com;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
client_max_body_size 100M;
|
|
}
|
|
}
|
|
```
|
|
|
|
Enable and get a certificate:
|
|
|
|
```bash
|
|
sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/
|
|
sudo certbot --nginx -d git.yourdomain.com
|
|
sudo systemctl reload nginx
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Enable Actions & Register the Runner
|
|
|
|
1. Go to **Site Administration > Runners** in the Gitea web UI.
|
|
2. Click **Create new Runner** and copy the registration token.
|
|
3. Paste the token into `docker-compose.yml` as `GITEA_RUNNER_REGISTRATION_TOKEN`.
|
|
4. Start the runner:
|
|
|
|
```bash
|
|
docker compose up -d gitea-runner
|
|
```
|
|
|
|
Verify the runner appears as **Online** in the admin panel.
|
|
|
|
---
|
|
|
|
## 5. Migrate Your Repository
|
|
|
|
### Option A: Git push (code only)
|
|
|
|
```bash
|
|
# On your local machine
|
|
cd /path/to/letzshop-product-import
|
|
git remote add gitea ssh://git@git.yourdomain.com:2222/your-username/letzshop-product-import.git
|
|
git push gitea --all
|
|
git push gitea --tags
|
|
```
|
|
|
|
### Option B: Gitea built-in migration (code + issues + PRs)
|
|
|
|
1. In Gitea, click **+** > **New Migration**.
|
|
2. Select **GitLab** as the source.
|
|
3. Enter your GitLab URL and a Personal Access Token.
|
|
4. Gitea will import the repository, issues, labels, milestones, and merge requests.
|
|
|
|
---
|
|
|
|
## 6. CI/CD — GitLab vs Gitea Actions
|
|
|
|
The workflow file lives in `.gitea/workflows/ci.yml` (already created in this repository).
|
|
|
|
| GitLab CI (`.gitlab-ci.yml`) | Gitea Actions (`.gitea/workflows/ci.yml`) |
|
|
|------------------------------|-------------------------------------------|
|
|
| `stages:` + `stage:` per job | Jobs run in parallel; use `needs:` for ordering |
|
|
| `services:` (top-level on job) | `services:` nested under each job with `options:` |
|
|
| `allow_failure: true` | `continue-on-error: true` |
|
|
| `rules: - if:` | `on:` triggers + `if:` conditionals per job |
|
|
| `artifacts: paths:` | `actions/upload-artifact@v4` |
|
|
| `cache: paths:` | `actions/cache@v4` |
|
|
| `coverage: '/regex/'` | Use coverage action or parse in step |
|
|
| CI/CD Variables (UI) | Repository **Settings > Secrets** |
|
|
|
|
---
|
|
|
|
## 7. CI/CD Secrets
|
|
|
|
Configure these in your Gitea repository under **Settings > Actions > Secrets**:
|
|
|
|
| Secret | Description | Used by |
|
|
|--------|-------------|---------|
|
|
| `SSH_PRIVATE_KEY` | Private key for deployment server | Deploy job (if added) |
|
|
| `SERVER_HOST` | Production server IP/hostname | Deploy job |
|
|
| `SERVER_USER` | SSH user on production server | Deploy job |
|
|
| `SERVER_PATH` | App directory on server | Deploy job |
|
|
|
|
---
|
|
|
|
## 8. Pipeline Overview
|
|
|
|
The CI pipeline (`.gitea/workflows/ci.yml`) runs:
|
|
|
|
```
|
|
push/PR to master
|
|
├── ruff (lint)
|
|
├── pytest (tests + PostgreSQL service)
|
|
├── architecture (architecture validation)
|
|
├── dependency-scanning (pip-audit, non-blocking)
|
|
├── audit (custom audit, non-blocking)
|
|
└── docs (mkdocs build, master-only, after lint+test pass)
|
|
```
|
|
|
|
All jobs run in parallel except `docs`, which waits for `ruff`, `pytest`, and `architecture` to pass.
|
|
|
|
---
|
|
|
|
## 9. Adding a Deploy Job (Optional)
|
|
|
|
To add automated deployment via SSH (similar to the GitLab deploy stage), add this job to `.gitea/workflows/ci.yml`:
|
|
|
|
```yaml
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
needs: [ruff, pytest, architecture]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Deploy via SSH
|
|
uses: appleboy/ssh-action@v1
|
|
with:
|
|
host: ${{ secrets.SERVER_HOST }}
|
|
username: ${{ secrets.SERVER_USER }}
|
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
script: |
|
|
cd ${{ secrets.SERVER_PATH }}
|
|
git pull origin master
|
|
source .venv/bin/activate
|
|
uv sync --frozen
|
|
python -m alembic upgrade head
|
|
sudo systemctl restart wizamart
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Firewall Configuration
|
|
|
|
```bash
|
|
sudo ufw allow OpenSSH
|
|
sudo ufw allow 'Nginx Full'
|
|
sudo ufw enable
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Maintenance
|
|
|
|
```bash
|
|
# View Gitea logs
|
|
docker compose -f ~/gitea/docker-compose.yml logs -f gitea
|
|
|
|
# View runner logs
|
|
docker compose -f ~/gitea/docker-compose.yml logs -f gitea-runner
|
|
|
|
# Update Gitea
|
|
cd ~/gitea
|
|
docker compose pull
|
|
docker compose up -d
|
|
|
|
# Backup Gitea data
|
|
docker run --rm -v gitea-data:/data -v $(pwd):/backup alpine \
|
|
tar czf /backup/gitea-backup-$(date +%Y%m%d).tar.gz /data
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Removing GitLab (After Migration)
|
|
|
|
Once you have verified everything works on Gitea:
|
|
|
|
1. Update your local git remote:
|
|
```bash
|
|
git remote set-url origin ssh://git@git.yourdomain.com:2222/your-username/letzshop-product-import.git
|
|
```
|
|
2. The `.gitlab-ci.yml` file can be removed from the repository.
|
|
3. Archive or delete the GitLab project.
|