Skip to content

Análise de Estrutura do Projeto #13

Análise de Estrutura do Projeto

Análise de Estrutura do Projeto #13

name: "Análise de Estrutura do Projeto"
on:
push:
branches: [main, develop]
paths:
- "app/src/**"
- "supabase/**"
pull_request:
branches: [main, develop]
paths:
- "app/src/**"
- "supabase/**"
schedule:
- cron: "0 8 * * 1" # toda segunda às 8h UTC
workflow_dispatch:
permissions:
contents: read
issues: write
pull-requests: write
jobs:
analyze-structure:
name: Análise de Estrutura
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verificar estrutura esperada
id: structure
run: |
WARNINGS=""
ERRORS=""
INFO=""
echo "🔍 Analisando estrutura do projeto..."
# --- Verificar diretórios obrigatórios ---
REQUIRED_DIRS=(
"app/src/components"
"app/src/pages"
"app/src/services"
"app/src/contexts"
"app/src/routes"
"app/src/types"
"app/src/utils"
"app/src/layout"
"app/public"
"supabase"
)
for dir in "${REQUIRED_DIRS[@]}"; do
if [ ! -d "$dir" ]; then
if [ "$dir" = "app/public" ]; then
WARNINGS+="⚠️ Diretório recomendado ausente: \`$dir\`\n"
else
WARNINGS+="⚠️ Diretório ausente: \`$dir\`\n"
fi
fi
done
# --- Verificar arquivos críticos ---
REQUIRED_FILES=(
"app/src/App.tsx"
"app/src/main.tsx"
"app/package.json"
"app/tsconfig.json"
"app/vite.config.ts"
"app/index.html"
"app/env.example"
)
for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$file" ]; then
ERRORS+="❌ Arquivo crítico ausente: \`$file\`\n"
fi
done
# --- Verificar arquivos de config na raiz ---
ROOT_FILES=(".gitignore" "README.md")
for file in "${ROOT_FILES[@]}"; do
if [ ! -f "$file" ]; then
WARNINGS+="⚠️ Arquivo recomendado ausente na raiz: \`$file\`\n"
fi
done
# --- Contar arquivos por tipo ---
TSX_COUNT=$(find app/src -name "*.tsx" 2>/dev/null | wc -l)
TS_COUNT=$(find app/src -name "*.ts" 2>/dev/null | wc -l)
CSS_COUNT=$(find app/src -name "*.css" 2>/dev/null | wc -l)
SQL_COUNT=$(find supabase -name "*.sql" 2>/dev/null | wc -l)
INFO+="### 📊 Métricas do Projeto\n\n"
INFO+="| Tipo | Quantidade |\n"
INFO+="|------|------------|\n"
INFO+="| Componentes (.tsx) | $TSX_COUNT |\n"
INFO+="| TypeScript (.ts) | $TS_COUNT |\n"
INFO+="| CSS (.css) | $CSS_COUNT |\n"
INFO+="| Arquivos SQL | $SQL_COUNT |\n\n"
# --- Verificar arquivos grandes (>300 linhas) ---
LARGE_FILES=""
while IFS= read -r file; do
if [ -f "$file" ]; then
LINES=$(wc -l < "$file")
if [ "$LINES" -gt 300 ]; then
LARGE_FILES+="⚠️ \`$file\` — $LINES linhas (considerar dividir)\n"
fi
fi
done < <(find app/src -name "*.tsx" -o -name "*.ts" 2>/dev/null)
if [ -n "$LARGE_FILES" ]; then
WARNINGS+="\n### 📏 Arquivos Grandes (>300 linhas)\n\n$LARGE_FILES\n"
fi
# --- Verificar imports circulares potenciais ---
BARREL_EXPORTS=$(find app/src -name "index.ts" -o -name "index.tsx" 2>/dev/null | wc -l)
if [ "$BARREL_EXPORTS" -gt 10 ]; then
WARNINGS+="⚠️ Muitos barrel exports ($BARREL_EXPORTS index files) — risco de imports circulares\n"
fi
# --- Verificar se .env está no repo ---
if [ -f "app/.env" ]; then
ERRORS+="❌ **SEGURANÇA:** \`app/.env\` está no repositório! Adicione ao .gitignore\n"
fi
if [ -f ".env" ]; then
ERRORS+="❌ **SEGURANÇA:** \`.env\` está no repositório! Adicione ao .gitignore\n"
fi
# --- Verificar dependências esperadas ---
if [ -f "app/package.json" ]; then
HAS_SUPABASE=$(grep -c "supabase" app/package.json || true)
HAS_REACT_ROUTER=$(grep -c "react-router" app/package.json || true)
HAS_TAILWIND=$(grep -c "tailwindcss" app/package.json || true)
if [ "$HAS_SUPABASE" -eq 0 ]; then
WARNINGS+="⚠️ \`@supabase/supabase-js\` não encontrado no package.json\n"
fi
if [ "$HAS_REACT_ROUTER" -eq 0 ]; then
WARNINGS+="⚠️ \`react-router-dom\` não encontrado no package.json\n"
fi
if [ "$HAS_TAILWIND" -eq 0 ]; then
WARNINGS+="⚠️ \`tailwindcss\` não encontrado no package.json\n"
fi
fi
# --- Verificar estrutura Supabase ---
if [ -d "supabase" ]; then
SQL_COUNT=$(ls supabase/*.sql 2>/dev/null | wc -l)
INFO+="### 🗃️ Arquivos Supabase\n\n"
if [ "$SQL_COUNT" -gt 0 ]; then
INFO+="| Arquivo | Status |\n"
INFO+="|---------|--------|\n"
for f in supabase/*.sql; do
[ -f "$f" ] || continue
NAME=$(basename "$f")
INFO+="| ✅ | \`$NAME\` |\n"
done
INFO+="\n"
else
WARNINGS+="⚠️ Nenhum arquivo SQL encontrado em \`supabase/\`\n"
fi
fi
# --- Verificar páginas principais ---
EXPECTED_PAGES=("LoginPage" "DashboardPage" "GoalsPage" "ProjectsPage")
MISSING_PAGES=""
for page in "${EXPECTED_PAGES[@]}"; do
if [ ! -f "app/src/pages/${page}.tsx" ]; then
MISSING_PAGES+="⚠️ Página principal ausente: \`${page}.tsx\`\n"
fi
done
if [ -n "$MISSING_PAGES" ]; then
WARNINGS+="\n### 📄 Páginas Principais\n\n$MISSING_PAGES\n"
fi
# --- Verificar services esperados ---
EXPECTED_SERVICES=("supabase" "goals")
MISSING_SERVICES=""
for svc in "${EXPECTED_SERVICES[@]}"; do
if [ ! -f "app/src/services/${svc}.ts" ]; then
MISSING_SERVICES+="⚠️ Service esperado ausente: \`${svc}.ts\`\n"
fi
done
if [ -n "$MISSING_SERVICES" ]; then
WARNINGS+="\n### 🔧 Services Esperados\n\n$MISSING_SERVICES\n"
fi
# --- Montar resultado final ---
HAS_ERRORS="false"
if [ -n "$ERRORS" ]; then
HAS_ERRORS="true"
fi
HAS_WARNINGS="false"
if [ -n "$WARNINGS" ]; then
HAS_WARNINGS="true"
fi
echo "has_errors=$HAS_ERRORS" >> $GITHUB_OUTPUT
echo "has_warnings=$HAS_WARNINGS" >> $GITHUB_OUTPUT
BODY="## 🏗️ Análise de Estrutura — Virex\n\n"
BODY+="**Branch:** \`${{ github.ref_name }}\`\n"
BODY+="**Data:** $(date '+%Y-%m-%d %H:%M UTC')\n\n"
BODY+="---\n\n"
BODY+="$INFO"
if [ -n "$ERRORS" ]; then
BODY+="### ❌ Erros Críticos\n\n$ERRORS\n"
fi
if [ -n "$WARNINGS" ]; then
BODY+="### ⚠️ Avisos e Melhorias\n\n$WARNINGS\n"
fi
if [ -z "$ERRORS" ] && [ -z "$WARNINGS" ]; then
BODY+="### ✅ Tudo OK!\n\nA estrutura do projeto está alinhada com a arquitetura definida.\n"
fi
BODY+="\n---\n"
BODY+="*Análise automática baseada na estrutura do projeto Virex*"
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "body<<$EOF" >> $GITHUB_OUTPUT
echo -e "$BODY" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
- name: Criar/Atualizar Issue de Estrutura
if: (steps.structure.outputs.has_errors == 'true' || steps.structure.outputs.has_warnings == 'true') && github.event_name != 'pull_request'
env:
STRUCTURE_BODY: ${{ toJSON(steps.structure.outputs.body) }}
uses: actions/github-script@v7
with:
script: |
const body = JSON.parse(process.env.STRUCTURE_BODY);
const existingIssues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'structure,ci-automated'
});
const title = '🏗️ Análise de Estrutura — Pendências Detectadas';
const structureIssue = existingIssues.data.find(
i => i.title.includes('Análise de Estrutura')
);
if (structureIssue) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: structureIssue.number,
body
});
console.log(`Issue #${structureIssue.number} atualizada`);
} else {
const issue = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
labels: ['structure', 'ci-automated']
});
console.log(`Issue #${issue.data.number} criada`);
}
- name: Comentar no PR
if: github.event_name == 'pull_request'
env:
STRUCTURE_BODY: ${{ toJSON(steps.structure.outputs.body) }}
uses: actions/github-script@v7
with:
script: |
const body = JSON.parse(process.env.STRUCTURE_BODY);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
});
- name: Fechar issue se tudo OK
if: steps.structure.outputs.has_errors == 'false' && steps.structure.outputs.has_warnings == 'false' && github.event_name != 'pull_request'
uses: actions/github-script@v7
with:
script: |
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'structure,ci-automated'
});
for (const issue of issues.data) {
if (issue.title.includes('Análise de Estrutura')) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: '✅ **Estrutura OK!** Todas as pendências foram resolvidas.'
});
}
}
- name: Resumo
run: |
echo "📋 Análise de estrutura concluída"
echo "Erros: ${{ steps.structure.outputs.has_errors }}"
echo "Avisos: ${{ steps.structure.outputs.has_warnings }}"