fix(listener): skip version-bump commits to prevent deploy loop
Parse Gitea webhook payload and skip the deploy when all commits match 'chore(deploy): bump version'. This breaks the infinite cycle where the deploy script's own version-bump push re-triggered a deploy, causing repeated container restarts and 502 errors for users. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ const LOG_FILE = '/home/deploy/stack/webhook-listener/deploy.log';
|
|||||||
|
|
||||||
function log(message) {
|
function log(message) {
|
||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
const logMessage = `[${timestamp}] ${message}\n`;
|
const logMessage = '[' + timestamp + '] ' + message + '\n';
|
||||||
console.log(message);
|
console.log(message);
|
||||||
fs.appendFileSync(LOG_FILE, logMessage, 'utf8');
|
fs.appendFileSync(LOG_FILE, logMessage, 'utf8');
|
||||||
}
|
}
|
||||||
@@ -27,14 +27,14 @@ try {
|
|||||||
let isBuilding = false;
|
let isBuilding = false;
|
||||||
|
|
||||||
const server = http.createServer((req, res) => {
|
const server = http.createServer((req, res) => {
|
||||||
// Apenas aceita requisições POST na rota /webhook
|
// Apenas aceita requisicoes POST na rota /webhook
|
||||||
const urlPath = req.url.split('?')[0];
|
const urlPath = req.url.split('?')[0];
|
||||||
if (req.method !== 'POST' || urlPath !== '/webhook') {
|
if (req.method !== 'POST' || urlPath !== '/webhook') {
|
||||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||||
return res.end(JSON.stringify({ error: 'Rota não encontrada' }));
|
return res.end(JSON.stringify({ error: 'Rota nao encontrada' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token de validação nos headers (Gitea envia como x-gitea-token) ou query string (?token=...)
|
// Token de validacao nos headers (Gitea envia como x-gitea-token) ou query string (?token=...)
|
||||||
const tokenHeader = req.headers['x-gitea-token'];
|
const tokenHeader = req.headers['x-gitea-token'];
|
||||||
|
|
||||||
// Parsear query params manualmente de forma simples
|
// Parsear query params manualmente de forma simples
|
||||||
@@ -55,35 +55,54 @@ const server = http.createServer((req, res) => {
|
|||||||
const token = tokenHeader || tokenQuery;
|
const token = tokenHeader || tokenQuery;
|
||||||
|
|
||||||
if (!token || token !== SECRET_TOKEN) {
|
if (!token || token !== SECRET_TOKEN) {
|
||||||
log('⚠️ Tentativa de acesso não autorizada: Token inválido ou ausente.');
|
log('Tentativa de acesso nao autorizada: Token invalido ou ausente.');
|
||||||
res.writeHead(401, { 'Content-Type': 'application/json' });
|
res.writeHead(401, { 'Content-Type': 'application/json' });
|
||||||
return res.end(JSON.stringify({ error: 'Não autorizado: Token inválido' }));
|
return res.end(JSON.stringify({ error: 'Nao autorizado: Token invalido' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBuilding) {
|
if (isBuilding) {
|
||||||
log('⏳ Requisição de build recebida, mas já existe um build em andamento. Ignorando para evitar conflito de concorrência.');
|
log('Requisicao de build recebida, mas ja existe um build em andamento. Ignorando para evitar conflito de concorrencia.');
|
||||||
res.writeHead(429, { 'Content-Type': 'application/json' });
|
res.writeHead(429, { 'Content-Type': 'application/json' });
|
||||||
return res.end(JSON.stringify({ error: 'Um build já está sendo executado neste momento' }));
|
return res.end(JSON.stringify({ error: 'Um build ja esta sendo executado neste momento' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retorna resposta de confirmação imediata (HTTP 202 Accepted) para evitar timeout do Gitea
|
// Ler o body completo para inspecionar o payload do Gitea
|
||||||
|
let body = '';
|
||||||
|
req.on('data', function(chunk) { body += chunk.toString(); });
|
||||||
|
req.on('end', function() {
|
||||||
|
// Verificar se todos os commits sao bumps de versao automaticos (evita loop infinito)
|
||||||
|
var payload = {};
|
||||||
|
try { payload = JSON.parse(body); } catch (e) {}
|
||||||
|
|
||||||
|
var commits = payload.commits || [];
|
||||||
|
var headMsg = (payload.head_commit || {}).message || '';
|
||||||
|
var allMessages = commits.map(function(c) { return c.message || ''; }).concat(headMsg).filter(function(m) { return m.trim() !== ''; });
|
||||||
|
var VERSION_BUMP_RE = /^chore\(deploy\):\s*bump version/i;
|
||||||
|
if (allMessages.length > 0 && allMessages.every(function(msg) { return VERSION_BUMP_RE.test(msg); })) {
|
||||||
|
log('Commit de bump de versao detectado -- deploy ignorado para evitar loop de CI.');
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
return res.end(JSON.stringify({ status: 'skipped', reason: 'version-bump commit' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retorna resposta de confirmacao imediata (HTTP 202 Accepted) para evitar timeout do Gitea
|
||||||
res.writeHead(202, { 'Content-Type': 'application/json' });
|
res.writeHead(202, { 'Content-Type': 'application/json' });
|
||||||
res.end(JSON.stringify({ status: 'ok', message: 'Deploy automático iniciado em segundo plano' }));
|
res.end(JSON.stringify({ status: 'ok', message: 'Deploy automatico iniciado em segundo plano' }));
|
||||||
|
|
||||||
// Inicia o processo de deploy
|
// Inicia o processo de deploy
|
||||||
isBuilding = true;
|
isBuilding = true;
|
||||||
log('🚀 Iniciando deploy-prod-builder.sh...');
|
log('Iniciando deploy-prod-builder.sh...');
|
||||||
|
|
||||||
exec(SCRIPT_PATH, (error, stdout, stderr) => {
|
exec(SCRIPT_PATH, function(error, stdout, stderr) {
|
||||||
isBuilding = false;
|
isBuilding = false;
|
||||||
if (error) {
|
if (error) {
|
||||||
log(`❌ Erro no processo de deploy: ${error.message}`);
|
log('Erro no processo de deploy: ' + error.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log('✅ Processo de deploy finalizado.');
|
log('Processo de deploy finalizado.');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(PORT, HOST, () => {
|
server.listen(PORT, HOST, function() {
|
||||||
log(`📡 Webhook listener ativo em http://${HOST}:${PORT}/webhook`);
|
log('Webhook listener ativo em http://' + HOST + ':' + PORT + '/webhook');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user