Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/deploy-railway.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Deploy Railway

on:
workflow_dispatch:
inputs:
public_api_url:
description: "URL pública para validar o /health (ex: https://seu-app.up.railway.app)"
required: false
type: string
push:
branches: ["main"]

jobs:
deploy:
runs-on: ubuntu-latest
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
PUBLIC_API_URL_SECRET: ${{ secrets.PUBLIC_API_URL }}
PUBLIC_API_URL_INPUT: ${{ inputs.public_api_url }}
RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
RAILWAY_ENVIRONMENT_ID: ${{ secrets.RAILWAY_ENVIRONMENT_ID }}
RAILWAY_SERVICE_ID: ${{ secrets.RAILWAY_SERVICE_ID }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install Railway CLI
run: npm i -g @railway/cli

- name: Link Railway project (optional)
if: ${{ env.RAILWAY_PROJECT_ID != '' && env.RAILWAY_ENVIRONMENT_ID != '' && env.RAILWAY_SERVICE_ID != '' }}
run: |
railway link \
--project "$RAILWAY_PROJECT_ID" \
--environment "$RAILWAY_ENVIRONMENT_ID" \
--service "$RAILWAY_SERVICE_ID"

- name: Resolve public API URL
run: |
if [ -n "$PUBLIC_API_URL_INPUT" ]; then
echo "PUBLIC_API_URL=$PUBLIC_API_URL_INPUT" >> "$GITHUB_ENV"
elif [ -n "$PUBLIC_API_URL_SECRET" ]; then
echo "PUBLIC_API_URL=$PUBLIC_API_URL_SECRET" >> "$GITHUB_ENV"
fi

- name: Deploy to Railway and verify healthcheck
run: ./scripts/deploy_real.sh
32 changes: 32 additions & 0 deletions RAILWAY_DEPLOY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,38 @@

---

## ⚡ Deploy real em 1 comando (CLI)

Se você já tem o projeto vinculado no Railway, use o script automatizado:

```bash
# Linux/macOS
export RAILWAY_TOKEN=seu_token
./scripts/deploy_real.sh
```

```powershell
# Windows (PowerShell)
$env:RAILWAY_TOKEN="seu_token"
.\scripts\deploy_real.ps1
```

Ele executa testes, faz deploy com `railway up --ci` e só finaliza quando `/health` responde 200.

---

## 🤖 Deploy direto pelo GitHub (sem terminal local)

1. No GitHub: **Settings → Secrets and variables → Actions**
2. Adicione os secrets:
- `RAILWAY_TOKEN` (obrigatório)
- `PUBLIC_API_URL` (recomendado)
- `RAILWAY_PROJECT_ID`, `RAILWAY_ENVIRONMENT_ID`, `RAILWAY_SERVICE_ID` (opcionais para link explícito)
3. Vá em **Actions → Deploy Railway → Run workflow**
4. O workflow usa `.github/workflows/deploy-railway.yml` e executa `./scripts/deploy_real.sh` automaticamente.

---

## 🚀 PASSO A PASSO — Deploy mínimo (sem Stripe/DB ainda)

### 1. Push das correções pro GitHub
Expand Down
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,63 @@ curl -X POST http://localhost:8000/api/v1/analyze \
└─────────────┴────────────┘
```


## 🚀 Deploy real (produção)

Para publicar a API de forma **real** no Railway com validação automática de healthcheck:

```bash
# Linux/macOS
npm i -g @railway/cli
export RAILWAY_TOKEN=seu_token
./scripts/deploy_real.sh
```

```powershell
# Windows (PowerShell)
npm i -g @railway/cli
$env:RAILWAY_TOKEN="seu_token"
.\scripts\deploy_real.ps1
```

Se o PowerShell bloquear execução de script local, rode antes:

```powershell
Set-ExecutionPolicy -Scope Process Bypass
```

O script:
- valida pré-requisitos e estado limpo do Git;
- roda testes (`pytest -q`) antes da publicação;
- executa `railway up --ci`;
- detecta domínio público e valida `/health` até ficar online.

Se a detecção automática do domínio falhar, informe manualmente:

```bash
PUBLIC_API_URL=https://seu-app.up.railway.app ./scripts/deploy_real.sh
```

```powershell
.\scripts\deploy_real.ps1 -PublicApiUrl "https://seu-app.up.railway.app"
```

### Deploy direto pelo GitHub Actions

Você também pode publicar sem rodar nada localmente:

1. No GitHub do repositório, vá em **Settings → Secrets and variables → Actions** e crie:
- `RAILWAY_TOKEN` (**obrigatório**)
- `PUBLIC_API_URL` (recomendado, ex: `https://seu-app.up.railway.app`)
- `RAILWAY_PROJECT_ID` (opcional)
- `RAILWAY_ENVIRONMENT_ID` (opcional)
- `RAILWAY_SERVICE_ID` (opcional)
2. Vá em **Actions → Deploy Railway → Run workflow**.
3. (Opcional) Preencha `public_api_url` no dispatch manual.
4. O workflow executa `./scripts/deploy_real.sh`, roda testes, faz deploy e valida `/health`.

Arquivo do workflow: `.github/workflows/deploy-railway.yml`.

## 🧪 Testing

```bash
Expand Down
117 changes: 117 additions & 0 deletions scripts/deploy_real.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
param(
[int]$HealthcheckTimeoutSeconds = 240,
[int]$HealthcheckIntervalSeconds = 8,
[string]$PublicApiUrl = ""
)

$ErrorActionPreference = 'Stop'

function Step($message) {
Write-Host "`n==> $message"
}

function Fail($message) {
Write-Host "`n❌ $message"
exit 1
}

function Require-Cmd($name) {
if (-not (Get-Command $name -ErrorAction SilentlyContinue)) {
Fail "Comando obrigatório não encontrado: $name"
}
}

Step "Validando pré-requisitos"
Require-Cmd python
Require-Cmd pip
Require-Cmd curl
Require-Cmd git
Require-Cmd railway

if (-not $env:RAILWAY_TOKEN) {
Fail "Defina RAILWAY_TOKEN para deploy não-interativo."
}

& git diff --quiet
if ($LASTEXITCODE -ne 0) {
Fail "Há alterações não commitadas. Faça commit antes do deploy."
}

& git diff --cached --quiet
if ($LASTEXITCODE -ne 0) {
Fail "Há alterações em staging não commitadas. Faça commit antes do deploy."
}

Step "Instalando dependências de runtime/teste"
& pip install -r requirements.txt | Out-Null
if ($LASTEXITCODE -ne 0) { Fail "Falha ao instalar requirements.txt" }

& pip install pytest | Out-Null
if ($LASTEXITCODE -ne 0) { Fail "Falha ao instalar pytest" }

Step "Executando testes"
& pytest -q
if ($LASTEXITCODE -ne 0) { Fail "Testes falharam" }

Step "Publicando nova versão no Railway"
& railway up --ci
if ($LASTEXITCODE -ne 0) { Fail "Falha no railway up --ci" }

Step "Detectando URL pública"
if ([string]::IsNullOrWhiteSpace($PublicApiUrl)) {
try {
$domainOutput = & railway domain 2>$null
if ($LASTEXITCODE -eq 0 -and $domainOutput) {
foreach ($line in $domainOutput) {
if ($line -match '(https?://[^\s]+|[\w.-]+\.(?:up\.railway\.app|railway\.app))') {
$detected = $Matches[1]
if ($detected -match '^https?://') {
$PublicApiUrl = $detected
} else {
$PublicApiUrl = "https://$detected"
}
break
}
}
}
} catch {
# fallback handled below
}
}

if ([string]::IsNullOrWhiteSpace($PublicApiUrl)) {
Fail "Não foi possível detectar domínio automaticamente. Informe -PublicApiUrl e rode novamente."
}

$healthUrl = "{0}/health" -f $PublicApiUrl.TrimEnd('/')
Step "Aguardando healthcheck em $healthUrl"

$start = Get-Date
while ($true) {
try {
$response = Invoke-WebRequest -Uri $healthUrl -Method GET -TimeoutSec 20
if ($response.StatusCode -eq 200) {
break
}
} catch {
# retry
}

$elapsed = (Get-Date) - $start
if ($elapsed.TotalSeconds -ge $HealthcheckTimeoutSeconds) {
Fail "Timeout no healthcheck (${HealthcheckTimeoutSeconds}s). Verifique logs: railway logs"
}

Start-Sleep -Seconds $HealthcheckIntervalSeconds
}

Write-Host "`n✅ Deploy concluído com sucesso."
Write-Host "URL: $PublicApiUrl"
Write-Host "Health: $healthUrl`n"

try {
$healthJson = Invoke-RestMethod -Uri $healthUrl -Method GET -TimeoutSec 20
$healthJson | ConvertTo-Json -Depth 8
} catch {
Write-Host "Não foi possível imprimir JSON do healthcheck, mas endpoint respondeu 200."
}
86 changes: 86 additions & 0 deletions scripts/deploy_real.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
set -euo pipefail

# Real deployment script for Railway with post-deploy verification.
# Usage:
# RAILWAY_TOKEN=xxx ./scripts/deploy_real.sh
# Optional vars:
# HEALTHCHECK_TIMEOUT_SECONDS=240
# HEALTHCHECK_INTERVAL_SECONDS=8
# PUBLIC_API_URL=https://your-app.up.railway.app

HEALTHCHECK_TIMEOUT_SECONDS="${HEALTHCHECK_TIMEOUT_SECONDS:-240}"
HEALTHCHECK_INTERVAL_SECONDS="${HEALTHCHECK_INTERVAL_SECONDS:-8}"

step() { printf '\n==> %s\n' "$1"; }
fail() { printf '\n❌ %s\n' "$1"; exit 1; }

require_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "Comando obrigatório não encontrado: $1"
}

step "Validando pré-requisitos"
require_cmd python
require_cmd pip
require_cmd curl
require_cmd git
require_cmd railway

if [[ -z "${RAILWAY_TOKEN:-}" ]]; then
fail "Defina RAILWAY_TOKEN para deploy não-interativo."
fi

if ! git diff --quiet || ! git diff --cached --quiet; then
fail "Há alterações não commitadas. Faça commit antes do deploy."
fi

step "Instalando dependências de runtime/teste"
pip install -r requirements.txt >/dev/null
pip install pytest >/dev/null

step "Executando testes"
pytest -q

step "Publicando nova versão no Railway"
railway up --ci

step "Detectando URL pública"
PUBLIC_API_URL="${PUBLIC_API_URL:-}"
if [[ -z "$PUBLIC_API_URL" ]]; then
if railway domain >/tmp/trusthire_railway_domain.txt 2>/dev/null; then
detected_domain="$(awk '/\.(railway|up\.railway)\.app/{print $NF; exit}' /tmp/trusthire_railway_domain.txt | tr -d '\r')"
if [[ -n "$detected_domain" ]]; then
if [[ "$detected_domain" =~ ^https?:// ]]; then
PUBLIC_API_URL="$detected_domain"
else
PUBLIC_API_URL="https://${detected_domain}"
fi
fi
fi
fi

if [[ -z "$PUBLIC_API_URL" ]]; then
fail "Não foi possível detectar domínio automaticamente. Defina PUBLIC_API_URL e rode novamente."
fi

HEALTH_URL="${PUBLIC_API_URL%/}/health"
step "Aguardando healthcheck em ${HEALTH_URL}"

start_ts="$(date +%s)"
while true; do
if curl -fsS "$HEALTH_URL" >/tmp/trusthire_health.json 2>/dev/null; then
break
fi

now_ts="$(date +%s)"
elapsed=$((now_ts - start_ts))
if (( elapsed >= HEALTHCHECK_TIMEOUT_SECONDS )); then
fail "Timeout no healthcheck (${HEALTHCHECK_TIMEOUT_SECONDS}s). Verifique logs: railway logs"
fi
sleep "$HEALTHCHECK_INTERVAL_SECONDS"
done

printf '\n✅ Deploy concluído com sucesso.\n'
printf 'URL: %s\n' "$PUBLIC_API_URL"
printf 'Health: %s\n\n' "$HEALTH_URL"
cat /tmp/trusthire_health.json