diff --git a/deploy.ps1 b/deploy.ps1 new file mode 100644 index 0000000..5881b8c --- /dev/null +++ b/deploy.ps1 @@ -0,0 +1,105 @@ +<# +.SYNOPSIS + Publica o Carinha no Orange Pi: compila o painel, empacota o código, faz backup na placa, + instala e reinicia o serviço. Configurações e bancos da placa nunca são sobrescritos. + +.EXAMPLE + .\deploy.ps1 # compila, publica em root@192.168.1.35:/opt/carinha-pc e reinicia + .\deploy.ps1 -SoPacote # só gera o pacote local (nada vai para a placa) + .\deploy.ps1 -Placa 192.168.1.40 -Usuario carinha +#> +param( + [string]$Placa = '192.168.1.35', + [string]$Usuario = 'root', + [string]$Destino = '/opt/carinha-pc', + [int]$Porta = 8100, + [switch]$SemCompilar, + [switch]$SoPacote +) +# Comandos nativos (npm, tar, scp, ssh) escrevem avisos no stderr; o PowerShell 5.1 trataria isso +# como erro fatal. Por isso o sucesso de cada um é decidido pelo $LASTEXITCODE. +$ErrorActionPreference = 'Continue' +Set-Location $PSScriptRoot -ErrorAction Stop + +# Código que vai para a placa. Ficam de fora: configurações (*.json da placa), bancos (*.db), +# chaves, vendor/ (modelos já instalados na placa) e o fonte do painel (só web/dist vai). +$arquivos = @( + 'kiosk-server.py', 'comum.py', 'eventos.py', 'controle_pc.py', 'sinalizacao.py', 'servos.py', 'braco.py', + 'cerebro.py', 'conversa.py', 'gemini.py', 'treinamento.py', 'figurinhas.py', 'api_config.py', + 'carinha.html', 'camera.html', 'reacoes-render.js', 'rastreador-worker.js', 'version.json', + 'obter-rastreamento.py', 'android/app/assets/qualidade-auto.js' +) +if (Test-Path 'android/carinha-rosto.apk') { $arquivos += 'android/carinha-rosto.apk' } + +# Telas antigas (até 0.7) que ainda podem estar na placa; vão para o backup e saem da pasta. +$legado = 'dashboard.html dashboard.css dashboard.js overview.js painel.html painel.css painel-controle.js ' + + 'painel-video.js painel-rastreamento.js painel-conversa.js conversa.html conversa.css emojis.html biblioteca.js ' + + 'treinamento.html treinamento.js treinamento.css braco.html braco.css braco.js braco-calibracao.js ' + + 'braco-movimentos.js movimentos-core.js servos.html servos.css servos.js api.html api-painel.js ' + + 'rastreamento-core.js audio-robot.js' + +if (-not $SemCompilar) { + Write-Host '== Compilando o painel (web/)' + Push-Location web + try { + if (-not (Test-Path node_modules)) { npm ci; if ($LASTEXITCODE) { throw 'npm ci falhou' } } + npm run check; if ($LASTEXITCODE) { throw 'svelte-check encontrou erros' } + npm test; if ($LASTEXITCODE) { throw 'testes do painel falharam' } + npm run build; if ($LASTEXITCODE) { throw 'build do painel falhou' } + } finally { Pop-Location } +} +if (-not (Test-Path 'web/dist/index.html')) { throw 'web/dist não existe: rode sem -SemCompilar' } + +$versao = (Get-Content version.json -Raw -ErrorAction Stop | ConvertFrom-Json).versao +$carimbo = Get-Date -Format 'yyyyMMdd-HHmmss' +$pacote = Join-Path $env:TEMP "carinha-$versao-$carimbo.tar.gz" +Write-Host "== Empacotando $versao em $pacote" +$faltando = $arquivos | Where-Object { -not (Test-Path $_) } +if ($faltando) { throw "Arquivos ausentes: $($faltando -join ', ')" } +& tar -czf $pacote @arquivos web/dist +if ($LASTEXITCODE) { throw 'tar falhou' } +$hash = (Get-FileHash $pacote -Algorithm SHA256 -ErrorAction Stop).Hash.ToLower() +Write-Host " SHA256 $hash" + +if ($SoPacote) { + & tar -tzf $pacote | Measure-Object | ForEach-Object { Write-Host " $($_.Count) arquivos no pacote" } + Write-Host 'Pacote pronto (nada enviado à placa).' + return +} + +$alvo = "$Usuario@$Placa" +$remoto = "/tmp/carinha-$versao-$carimbo.tar.gz" +Write-Host "== Enviando para $alvo" +& scp -q $pacote "${alvo}:$remoto" +if ($LASTEXITCODE) { throw 'scp falhou' } + +# Na placa: backup do código atual, remove telas antigas, extrai o novo e reinicia. +$script = @" +set -e +cd '$Destino' +backup='$Destino/backup-$carimbo' +mkdir -p "`$backup" +for f in $($arquivos -join ' ') $legado; do [ -e "`$f" ] && cp -a --parents "`$f" "`$backup/" || true; done +[ -d web/dist ] && mkdir -p "`$backup/web" && cp -a web/dist "`$backup/web/" || true +echo "$hash $remoto" | sha256sum -c --quiet +rm -f $legado +rm -rf web/dist +tar -xzf '$remoto' -C '$Destino' +rm -f '$remoto' +systemctl restart carinha-pc +echo "backup em `$backup" +"@ +Write-Host '== Instalando na placa' +$script -replace "`r", '' | & ssh $alvo 'bash -s' +if ($LASTEXITCODE) { throw 'instalação na placa falhou (o backup foi feito antes de qualquer remoção)' } + +Write-Host '== Conferindo' +$url = "http://${Placa}:$Porta" +for ($i = 0; $i -lt 10; $i++) { + try { + $saude = Invoke-RestMethod "$url/app/health" -TimeoutSec 3 + if ($saude.versao -eq $versao) { Write-Host "OK: $url/ responde versão $versao"; return } + } catch { } + Start-Sleep -Seconds 1 +} +throw "O serviço não respondeu a versão $versao em $url. Veja: ssh $alvo journalctl -u carinha-pc -n 50" diff --git a/iniciar.ps1 b/iniciar.ps1 index 29dd3c6..4b0bd0e 100644 --- a/iniciar.ps1 +++ b/iniciar.ps1 @@ -1,4 +1,4 @@ -param([string]$Placa = '192.168.1.35', [int]$Porta = 8100, [switch]$SemAbrir) +param([string]$Placa = '192.168.1.35', [int]$Porta = 8100, [switch]$SemAbrir) $ErrorActionPreference = 'Stop' $url = "http://${Placa}:$Porta" try {