--- name: easypanel-cicd description: Configuracao de CI/CD com Gitea Actions para auto-deploy no EasyPanel via webhook — setup de workflows, secrets e validacao. --- # /easypanel-cicd - CI/CD Gitea Actions para EasyPanel Configuracao completa de pipelines CI/CD que ligam repositorios Gitea ao EasyPanel para auto-deploy. --- ## Quando usar - Configurar auto-deploy de um repositorio Gitea para EasyPanel - Criar workflow Gitea Actions com testes + deploy - Obter e configurar webhook URL de um servico - Diagnosticar pipelines CI/CD falhados - Adicionar CI/CD a projectos existentes no EasyPanel ## Quando nao usar - Para deploy manual (usar `/easypanel-deploy`) - Para scaffold de projecto novo (usar `/easypanel-init`) - Para troubleshooting de containers (usar `/easypanel-troubleshoot`) --- ## Sintaxe ```bash /easypanel-cicd # Setup completo (workflow + secrets + webhook) /easypanel-cicd --webhook-only # Apenas obter/configurar webhook /easypanel-cicd --diagnose # Diagnosticar pipeline falhado ``` --- ## Arquitectura ``` Gitea (push main) --> Gitea Actions Runner --> Testes + Build --> Webhook POST --> EasyPanel Redeploy (3 runners activos) (deploy URL) (build + container) ``` **Componentes:** - **Gitea Actions Runners:** 3 replicas activas (`descomplicar_gitea-runner`) - **Webhook URL:** Cada servico EasyPanel tem `deploymentUrl` unico - **Formato:** `http://:3000/api/deploy/` --- ## Workflow completo ### 1. Obter webhook URL do servico ```bash TOKEN=$(cat /etc/easypanel/.api-token) # Via inspectService — campo deploymentUrl INPUT='{"json":{"projectName":"descomplicar","serviceName":"SERVICE_NAME"}}' ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$INPUT'))") curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=$ENCODED" \ -H "Authorization: Bearer $TOKEN" \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result']['data']['json']['deploymentUrl'])" ``` **Formato da URL:** `http://178.63.18.51:3000/api/deploy/` **Nota:** O token e unico por servico e nao expira. Nao confundir com o API token geral. ### 2. Configurar secret no repositorio Gitea Via MCP Gitea: ``` mcp__gitea__upsert_repo_action_secret({ owner: "ealmeida", repo: "REPO_NAME", secretname: "EASYPANEL_DEPLOY_URL", body: { data: "http://178.63.18.51:3000/api/deploy/" } }) ``` Secrets adicionais recomendados: - `EASYPANEL_DEPLOY_URL` — webhook URL (obrigatorio) - `DEPLOY_DOMAIN` — dominio do servico (opcional, para health check) ### 3. Criar workflow Gitea Actions Criar `.gitea/workflows/deploy.yml` no repositorio: #### Template basico (apenas deploy) ```yaml name: Deploy to EasyPanel on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Trigger EasyPanel Deploy run: | RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${{ secrets.EASYPANEL_DEPLOY_URL }}") if [ "$RESPONSE" != "200" ]; then echo "Deploy trigger failed with HTTP $RESPONSE" exit 1 fi echo "Deploy triggered successfully" ``` #### Template completo (testes + build + deploy + health check) ```yaml name: CI/CD Pipeline on: push: branches: - main pull_request: branches: - main jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' - name: Install dependencies run: npm ci - name: Lint run: npm run lint --if-present - name: Test run: npm test --if-present - name: Build run: npm run build deploy: needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Trigger EasyPanel Deploy run: | echo "Triggering deploy..." RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${{ secrets.EASYPANEL_DEPLOY_URL }}") if [ "$RESPONSE" != "200" ]; then echo "Deploy trigger failed with HTTP $RESPONSE" exit 1 fi echo "Deploy triggered (HTTP $RESPONSE)" - name: Wait for deployment run: sleep 120 - name: Health check if: ${{ secrets.DEPLOY_DOMAIN != '' }} run: | for i in 1 2 3 4 5; do STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://${{ secrets.DEPLOY_DOMAIN }}/health" || echo "000") echo "Health check $i/5: HTTP $STATUS" if [ "$STATUS" = "200" ]; then echo "Service healthy" exit 0 fi sleep 15 done echo "Health check failed after 5 attempts" exit 1 ``` #### Template Python ```yaml name: CI/CD Pipeline (Python) on: push: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - run: pip install -r requirements.txt - run: python -m pytest --if-present deploy: needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Trigger EasyPanel Deploy run: | curl -sf -X POST "${{ secrets.EASYPANEL_DEPLOY_URL }}" || exit 1 ``` ### 4. Commit e push ```bash git add .gitea/workflows/deploy.yml git commit -m "ci: add Gitea Actions deploy pipeline" git push origin main ``` ### 5. Validar pipeline ```bash # Via MCP Gitea — verificar runs mcp__gitea__list_repo_action_runs({ owner: "ealmeida", repo: "REPO_NAME", limit: 5 }) ``` Ou verificar logs: ```bash mcp__gitea__list_repo_action_run_jobs({ owner: "ealmeida", repo: "REPO_NAME", run_id: RUN_ID }) ``` --- ## Diagnostico (--diagnose) ### Pipeline nao dispara 1. Verificar se o ficheiro esta em `.gitea/workflows/` (nao `.github/workflows/`) 2. Verificar se o branch e `main` (ou o configurado no `on.push.branches`) 3. Verificar se os runners estao activos: ```bash docker service ls | grep gitea-runner # Esperado: 3/3 replicas ``` ### Webhook falha (HTTP != 200) 1. Verificar se o `deploymentUrl` esta correcto (servico pode ter sido recriado) 2. Verificar se o EasyPanel esta acessivel internamente: ```bash curl -s http://localhost:3000/api/trpc/projects.listProjects -H "Authorization: Bearer $TOKEN" ``` 3. Verificar se o token do servico nao mudou (re-obter via inspectService) ### Build falha no EasyPanel 1. Verificar Dockerfile: ```bash /easypanel-validate ``` 2. Verificar logs do build: ```bash /easypanel-troubleshoot SERVICE_NAME ``` ### Testes falham no runner 1. Verificar logs do job: ```bash mcp__gitea__get_repo_action_job_log_preview({ owner: "ealmeida", repo: "REPO_NAME", job_id: JOB_ID }) ``` 2. Verificar se dependencias estao no `package.json`/`requirements.txt` 3. Verificar se env vars de teste estao configuradas --- ## Servicos com CI/CD activo (inventario) Para listar todos os servicos com source git (candidatos a CI/CD): ```bash TOKEN=$(cat /etc/easypanel/.api-token) curl -s "http://localhost:3000/api/trpc/projects.listProjectsAndServices" \ -H "Authorization: Bearer $TOKEN" \ | python3 -c " import sys,json d=json.load(sys.stdin)['result']['data']['json'] for s in d.get('services',[]): src=s.get('source',{}) or {} if src.get('type')=='git' and s.get('type')=='app': repo=src.get('repo','').split('@')[-1] if '@' in src.get('repo','') else src.get('repo','') print(f\"{s['projectName']}/{s['name']} | repo={repo} | ref={src.get('ref','?')}\") " ``` --- ## Checklist de execucao - [ ] Identificar servico EasyPanel alvo (projectName + serviceName) - [ ] Obter deploymentUrl via inspectService (SSH ao servidor easy) - [ ] Criar secret EASYPANEL_DEPLOY_URL no repo Gitea (via MCP) - [ ] Criar secret DEPLOY_DOMAIN no repo Gitea (opcional, para health check) - [ ] Criar `.gitea/workflows/deploy.yml` com template adequado - [ ] Commit e push do workflow - [ ] Verificar que o primeiro run dispara (list_repo_action_runs) - [ ] Verificar que o deploy completa no EasyPanel - [ ] Health check do servico apos deploy --- ## MCPs necessarios - `ssh-unified` — Acesso ao servidor easy para obter deploymentUrl - `gitea` — Criar secrets e verificar action runs --- ## Skills relacionadas | Skill | Quando usar | |-------|-------------| | `/easypanel-deploy` | Deploy manual com validacao completa | | `/easypanel-validate` | Validar projecto antes de configurar CI/CD | | `/easypanel-init` | Scaffold novo projecto (ja inclui workflow template) | | `/easypanel-troubleshoot` | Diagnosticar deploy falhado | | `/easypanel-api` | Referencia API tRPC | --- *Skill v1.0.0 | 24-03-2026 | Descomplicar(r)* --- ## Healing Log Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar. ```jsonl {"date":"","issue":"","fix":"","source":"user|auto"} ``` *Adicionar nova linha após cada erro corrigido.*