# 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= - 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: 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: 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 `` with a strong database password and `` 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.