ci: migrate workflows from Forgejo to Gitea Actions
This commit is contained in:
@@ -100,4 +100,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
test -n "$DEPLOY_HOST" || (echo "DEPLOY_HOST must be set" && exit 1)
|
test -n "$DEPLOY_HOST" || (echo "DEPLOY_HOST must be set" && exit 1)
|
||||||
test -n "$DEPLOY_SSH_PRIVATE_KEY" || (echo "DEPLOY_SSH_PRIVATE_KEY must be set" && exit 1)
|
test -n "$DEPLOY_SSH_PRIVATE_KEY" || (echo "DEPLOY_SSH_PRIVATE_KEY must be set" && exit 1)
|
||||||
bash scripts/deploy-forgejo.sh
|
bash scripts/deploy-actions.sh
|
||||||
105
AGENTS.md
105
AGENTS.md
@@ -89,32 +89,109 @@ validation_checklist:
|
|||||||
- screenshot artifact captured when useful
|
- screenshot artifact captured when useful
|
||||||
- relevant logs checked
|
- relevant logs checked
|
||||||
|
|
||||||
forgejo_ci:
|
gitea_ci:
|
||||||
|
repo:
|
||||||
|
base_url: "http://tea.uncloud.vpn"
|
||||||
|
api_base_url: "http://tea.uncloud.vpn/api/v1"
|
||||||
|
owner: "bu5hm4nn"
|
||||||
|
name: "vault-dash"
|
||||||
|
full_name: "bu5hm4nn/vault-dash"
|
||||||
|
ssh_remote: "ssh://git@tea.uncloud.vpn:2223/bu5hm4nn/vault-dash.git"
|
||||||
|
auth:
|
||||||
|
preferred_method: "tea login"
|
||||||
|
notes:
|
||||||
|
- "`tea` is already logged in for this repo and should be preferred for API access"
|
||||||
|
- "`tea api` works against this Gitea 1.25.5 instance even when higher-level `tea actions` commands are unavailable"
|
||||||
|
- A raw token may exist separately, but automation should not assume one unless the user says so
|
||||||
|
shell_setup: |
|
||||||
|
export GITEA_URL="${GITEA_URL:-http://tea.uncloud.vpn}"
|
||||||
|
export GITEA_API="${GITEA_API:-$GITEA_URL/api/v1}"
|
||||||
|
export GITEA_REPO="bu5hm4nn/vault-dash"
|
||||||
|
tea login list >/dev/null
|
||||||
|
command -v jq >/dev/null
|
||||||
|
triage:
|
||||||
|
preferred_method: "use `tea api` for run/job/log discovery; use the Gitea web UI as fallback"
|
||||||
|
tested_on: "2026-04-07"
|
||||||
|
tested_behavior:
|
||||||
|
- "`tea actions runs list` refuses to run because the server is older than 1.26.0"
|
||||||
|
- "`tea api` works and authenticates via the stored tea login"
|
||||||
|
- Gitea 1.25.5 exposes Actions run, job, log, workflow, artifact, and runner endpoints in swagger
|
||||||
|
- The new server supports `/repos/{owner}/{repo}/actions/runs/{run}/jobs` and `/repos/{owner}/{repo}/actions/jobs/{job_id}/logs`
|
||||||
|
response_shape_note:
|
||||||
|
- Gitea Actions list endpoints return object wrappers such as `.workflow_runs` or `.jobs`
|
||||||
|
- In jq, prefer `(.workflow_runs // .)` for runs and `(.jobs // .)` for jobs
|
||||||
|
default_scope:
|
||||||
|
- Prefer latest run for `git rev-parse HEAD`
|
||||||
|
- If there is no run for HEAD yet, inspect the latest failed run on the current branch
|
||||||
|
- If CI failed, Build and Deploy will not run because deploy is triggered after CI success
|
||||||
|
query_recipes:
|
||||||
|
list_recent_runs_current_branch: |
|
||||||
|
branch="$(git branch --show-current)"
|
||||||
|
tea api -l tea.uncloud.vpn "/repos/bu5hm4nn/vault-dash/actions/runs?branch=$branch&limit=20" | jq '(.workflow_runs // .) | map({id, workflow_id, status, event, head_branch, head_sha, created_at, html_url})'
|
||||||
|
latest_run_for_head_sha: |
|
||||||
|
branch="$(git branch --show-current)"
|
||||||
|
sha="$(git rev-parse HEAD)"
|
||||||
|
tea api -l tea.uncloud.vpn "/repos/bu5hm4nn/vault-dash/actions/runs?branch=$branch&limit=50" | jq -r --arg sha "$sha" '((.workflow_runs // .) | map(select((.head_sha // .commit_sha) == $sha)) | sort_by(.created_at // .run_started_at // .id) | reverse | .[0])'
|
||||||
|
latest_failed_run_current_branch: |
|
||||||
|
branch="$(git branch --show-current)"
|
||||||
|
tea api -l tea.uncloud.vpn "/repos/bu5hm4nn/vault-dash/actions/runs?branch=$branch&status=failure&limit=20" | jq -r '((.workflow_runs // .) | sort_by(.created_at // .run_started_at // .id) | reverse | .[0])'
|
||||||
|
list_jobs_for_run: |
|
||||||
|
run_id="<RUN_ID>"
|
||||||
|
tea api -l tea.uncloud.vpn "/repos/bu5hm4nn/vault-dash/actions/runs/$run_id/jobs" | jq '(.jobs // .) | map({id, name, status, conclusion, started_at, completed_at})'
|
||||||
|
first_failed_job_for_run: |
|
||||||
|
run_id="<RUN_ID>"
|
||||||
|
tea api -l tea.uncloud.vpn "/repos/bu5hm4nn/vault-dash/actions/runs/$run_id/jobs" | jq -r '((.jobs // .) | map(select((.conclusion // .status) == "failure" or .status == "failure")) | sort_by(.started_at // .id) | .[0])'
|
||||||
|
download_job_log: |
|
||||||
|
job_id="<JOB_ID>"
|
||||||
|
tea api -l tea.uncloud.vpn "/repos/bu5hm4nn/vault-dash/actions/jobs/$job_id/logs"
|
||||||
viewing_job_logs:
|
viewing_job_logs:
|
||||||
web_ui:
|
web_ui:
|
||||||
url: "http://git.uncloud.vpn:3000/bu5hm4nn/vault-dash/actions"
|
url: "http://tea.uncloud.vpn/bu5hm4nn/vault-dash/actions"
|
||||||
steps:
|
steps:
|
||||||
- Navigate to Actions tab in Forgejo UI (VPN access required)
|
- Navigate to Actions tab in the Gitea UI
|
||||||
- Click on the workflow run to see job status
|
- Open the run from its `html_url` returned by `tea api`
|
||||||
- Expand failing job (lint/test/type-check/build/deploy)
|
- Expand the failing job (lint/test/type-check/build/deploy)
|
||||||
- Click on failed step to see detailed logs
|
- Click the failed step to inspect detailed logs
|
||||||
runner_logs:
|
api:
|
||||||
ssh: "ssh root@5.75.141.4"
|
preferred_cli: "tea api"
|
||||||
command: "docker logs forgejo-runner --tail 100"
|
notes:
|
||||||
job_workspace: "/opt/forgejo-runner/data/"
|
- High-level `tea actions` commands may be version-gated by the CLI
|
||||||
|
- "`tea api` is the stable fallback for this Gitea instance"
|
||||||
|
workflows:
|
||||||
|
CI:
|
||||||
|
file: ".gitea/workflows/ci.yaml"
|
||||||
|
jobs: [lint, type-check, test]
|
||||||
|
triggers: [push main, pull_request main]
|
||||||
|
Build_and_Deploy:
|
||||||
|
file: ".gitea/workflows/deploy.yaml"
|
||||||
|
jobs: [build, deploy]
|
||||||
|
triggers:
|
||||||
|
- workflow_run after CI succeeds on main
|
||||||
|
- manual workflow_dispatch
|
||||||
common_failures:
|
common_failures:
|
||||||
missing_dependency:
|
missing_dependency:
|
||||||
symptom: "ModuleNotFoundError: No module named 'X'"
|
symptom: "ModuleNotFoundError: No module named 'X'"
|
||||||
fix: "Add package to requirements.txt AND .forgejo/workflows/deploy.yaml (test + type-check jobs)"
|
fix:
|
||||||
|
- Add runtime deps to requirements.txt
|
||||||
|
- Add dev/test tooling deps to requirements-dev.txt
|
||||||
|
- CI installs requirements-dev.txt, so keep CI-critical deps there
|
||||||
|
playwright_version_drift:
|
||||||
|
symptom: "Playwright browser/package mismatch or local vs CI behavior differs"
|
||||||
|
fix:
|
||||||
|
- Keep Python Playwright pinned in requirements-dev.txt
|
||||||
|
- Keep the pin aligned with `.gitea/workflows/ci.yaml` Playwright container image tag
|
||||||
|
tea_version_gate:
|
||||||
|
symptom: "`tea actions ...` says the server is older than 1.26.0"
|
||||||
|
fix: "Use `tea api` directly against the Actions endpoints instead of high-level tea subcommands"
|
||||||
type_error:
|
type_error:
|
||||||
symptom: "error: Incompatible types..."
|
symptom: "error: Incompatible types..."
|
||||||
fix: "Run `mypy app --ignore-missing-imports` locally to reproduce"
|
fix: "Run `mypy app/core app/models app/strategies app/services app/domain --show-error-codes --show-traceback` locally to reproduce"
|
||||||
test_failure:
|
test_failure:
|
||||||
symptom: "FAILED test_name"
|
symptom: "FAILED test_name"
|
||||||
fix: "Run failing test locally with pytest -xvs"
|
fix: "Run failing test locally with `pytest -xvs <test_path_or_nodeid>`"
|
||||||
|
|
||||||
pre_merge_checklist:
|
pre_merge_checklist:
|
||||||
- run `pytest tests/ -v --tb=short` locally and ensure all new/changed tests pass
|
- run `pytest tests/ -v --tb=short` locally and ensure all new/changed tests pass
|
||||||
- run `/review` to get implementation review and QA validation
|
- run `/review` to get implementation review and QA validation
|
||||||
- verify CI passes on Forgejo (lint, test, type-check, build, deploy)
|
- verify CI passes on Gitea (lint, test, type-check, build, deploy)
|
||||||
- address all review comments before merging to main
|
- address all review comments before merging to main
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# Deployment Guide
|
# Deployment Guide
|
||||||
|
|
||||||
This project uses Forgejo Actions for CI/CD, building a Docker image and deploying to a VPN-reachable VPS over SSH.
|
This project uses Gitea Actions for CI/CD, building a Docker image and deploying to a VPN-reachable VPS over SSH.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Deployment workflow:
|
Deployment workflow:
|
||||||
|
|
||||||
1. **CI** (`.forgejo/workflows/ci.yaml`): Lint, test, type-check on every push
|
1. **CI** (`.gitea/workflows/ci.yaml`): lint, test, type-check on every push
|
||||||
2. **Deploy** (`.forgejo/workflows/deploy.yaml`): Build, scan, and deploy on main branch
|
2. **Build and Deploy** (`.gitea/workflows/deploy.yaml`): build and deploy on `main` after CI succeeds, or manually via workflow dispatch
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -20,14 +20,15 @@ Deployment workflow:
|
|||||||
- SSH access via VPN
|
- SSH access via VPN
|
||||||
- Python 3.11+ (for healthcheck script)
|
- Python 3.11+ (for healthcheck script)
|
||||||
|
|
||||||
### Forgejo Instance Setup
|
### Gitea Instance Setup
|
||||||
|
|
||||||
1. Enable Actions in Forgejo admin settings
|
1. Enable Actions in Gitea admin settings
|
||||||
2. Register a runner (or use Forgejo's built-in runner)
|
2. Enable Actions for the repository
|
||||||
|
3. Register an Actions runner
|
||||||
|
|
||||||
### Runner Setup
|
### Runner Setup
|
||||||
|
|
||||||
Forgejo supports both built-in runners and self-hosted Docker runners. For Docker-in-Docker builds, ensure the runner has:
|
Gitea Actions uses `act_runner`. For Docker-based builds, ensure the runner host has:
|
||||||
|
|
||||||
- Docker installed and accessible
|
- Docker installed and accessible
|
||||||
- `docker` and `docker compose` commands available
|
- `docker` and `docker compose` commands available
|
||||||
@@ -35,34 +36,40 @@ Forgejo supports both built-in runners and self-hosted Docker runners. For Docke
|
|||||||
Example runner registration:
|
Example runner registration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# On your Forgejo server
|
# On the runner host
|
||||||
forgejo actions generate-runner-token > token.txt
|
./act_runner register --no-interactive --instance http://tea.uncloud.vpn --token <registration-token>
|
||||||
forgejo-runner register --instance-addr http://localhost:3000 --token $(cat token.txt)
|
./act_runner daemon
|
||||||
forgejo-runner daemon
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Repository, organization, and instance runner tokens can be created from the Gitea web UI under Actions runner settings.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Required Secrets
|
## 2. Required Secrets
|
||||||
|
|
||||||
Configure in **Settings → Secrets and variables → Actions**:
|
Configure in **Settings → Secrets and variables → Actions**.
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
| Secret | Description |
|
| Secret | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `DEPLOY_SSH_PRIVATE_KEY` | SSH key for VPS access |
|
| `DEPLOY_SSH_PRIVATE_KEY` | SSH key for VPS access |
|
||||||
| `DEPLOY_HOST` | VPS IP/hostname (VPN-reachable) |
|
|
||||||
| `DEPLOY_USER` | Deploy user (default: `deploy`) |
|
|
||||||
| `DEPLOY_PORT` | SSH port (default: 22) |
|
|
||||||
| `DEPLOY_PATH` | Deploy path (default: `/opt/vault-dash`) |
|
|
||||||
| `NICEGUI_STORAGE_SECRET` | Session secret |
|
|
||||||
| `REGISTRY_PASSWORD` | Container registry token (if needed) |
|
| `REGISTRY_PASSWORD` | Container registry token (if needed) |
|
||||||
|
| `DOCKERHUB_TOKEN` | Docker Hub token |
|
||||||
|
| `TURNSTILE_SECRET_KEY` | Turnstile secret key |
|
||||||
|
| `DATABENTO_API_KEY` | Databento API key |
|
||||||
|
|
||||||
### Optional Variables
|
### Variables
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
|
| `DEPLOY_HOST` | VPS IP/hostname (VPN-reachable) |
|
||||||
|
| `DEPLOY_USER` | Deploy user (default: `deploy`) |
|
||||||
|
| `DEPLOY_PORT` | SSH port (default: `22`) |
|
||||||
|
| `DEPLOY_PATH` | Deploy path (default: `/opt/vault-dash`) |
|
||||||
| `REGISTRY` | Container registry URL |
|
| `REGISTRY` | Container registry URL |
|
||||||
| `EXTERNAL_HEALTHCHECK_URL` | Public health check URL |
|
| `DOCKERHUB_USERNAME` | Docker Hub username |
|
||||||
|
| `TURNSTILE_SITE_KEY` | Turnstile site key |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -127,7 +134,7 @@ export DEPLOY_SSH_PRIVATE_KEY="$(cat ~/.ssh/deploy_key)"
|
|||||||
export APP_IMAGE="registry.example.com/vault-dash:latest"
|
export APP_IMAGE="registry.example.com/vault-dash:latest"
|
||||||
|
|
||||||
# Run deploy script
|
# Run deploy script
|
||||||
bash scripts/deploy.sh
|
bash scripts/deploy-actions.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -150,22 +157,12 @@ vault.uncloud.vpn {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Future: OAuth Integration
|
## 7. Troubleshooting
|
||||||
|
|
||||||
When ready to expose publicly:
|
|
||||||
|
|
||||||
1. Set up OAuth provider (Authentik, Keycloak, etc.)
|
|
||||||
2. Configure `CORS_ORIGINS` for public URL
|
|
||||||
3. Add OAuth middleware to FastAPI
|
|
||||||
4. Enable HTTPS via Let's Encrypt
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Troubleshooting
|
|
||||||
|
|
||||||
### Runner can't build Docker images
|
### Runner can't build Docker images
|
||||||
|
|
||||||
Ensure runner has Docker access:
|
Ensure runner has Docker access:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm hello-world
|
docker run --rm hello-world
|
||||||
```
|
```
|
||||||
@@ -192,4 +189,4 @@ cd /opt/vault-dash
|
|||||||
PREVIOUS=$(cat .last_successful_image)
|
PREVIOUS=$(cat .last_successful_image)
|
||||||
sed -i "s|^APP_IMAGE=.*|APP_IMAGE=$PREVIOUS|" .env
|
sed -i "s|^APP_IMAGE=.*|APP_IMAGE=$PREVIOUS|" .env
|
||||||
docker compose -f docker-compose.deploy.yml up -d
|
docker compose -f docker-compose.deploy.yml up -d
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
# Deploy script for Forgejo Actions
|
# Deploy script for Gitea Actions
|
||||||
# Uses Forgejo environment variables instead of GitLab CI variables
|
# Uses Git hosting Actions environment variables instead of GitLab CI variables
|
||||||
|
|
||||||
: "${DEPLOY_USER:?DEPLOY_USER is required}"
|
: "${DEPLOY_USER:?DEPLOY_USER is required}"
|
||||||
: "${DEPLOY_HOST:?DEPLOY_HOST is required}"
|
: "${DEPLOY_HOST:?DEPLOY_HOST is required}"
|
||||||
Reference in New Issue
Block a user