fix: robust git commit hash detection via fs, env vars and docker support

This commit is contained in:
2026-10-15 14:58:06 -03:00
parent d8500cf433
commit 3dfc430c85
4 changed files with 95 additions and 8 deletions
+1
View File
@@ -0,0 +1 @@
version.json export-subst
+3
View File
@@ -1,6 +1,9 @@
# Estágio 1: Build da aplicação React
FROM node:20-alpine AS builder
# Instala git para resolução da versão de commit durante o build
RUN apk add --no-cache git
WORKDIR /app
# Copia manifestos de dependência
+3
View File
@@ -0,0 +1,3 @@
{
"commit": "d8500cf"
}
+88 -8
View File
@@ -1,19 +1,99 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import fs from 'fs'
import path from 'path'
import { execSync } from 'child_process'
let commitHash = process.env.COMMIT_REF
? process.env.COMMIT_REF.slice(0, 7)
: '';
function resolveCommitHash() {
// 1. Variáveis de ambiente de plataformas de hospedagem comuns (Netlify, Vercel, Cloudflare, Coolify, CI)
const envVars = [
'COMMIT_REF',
'VERCEL_GIT_COMMIT_SHA',
'CF_PAGES_COMMIT_SHA',
'COOLIFY_COMMIT_SHA',
'SOURCE_COMMIT',
'GIT_COMMIT',
'GITHUB_SHA',
'CI_COMMIT_SHA',
'GITEA_ACTIONS_SHA',
'VITE_COMMIT_HASH',
'COMMIT_HASH'
];
if (!commitHash) {
try {
commitHash = execSync('git rev-parse --short HEAD').toString().trim();
} catch (e) {
commitHash = 'dev';
for (const key of envVars) {
const val = process.env[key];
if (val && typeof val === 'string' && val.trim().length >= 7) {
return val.trim().slice(0, 7);
}
}
// 2. Leitura direta dos arquivos .git via Node.js fs (dispensa ter o binário 'git' instalado no container)
try {
let gitDir = path.resolve('.git');
if (fs.existsSync(gitDir)) {
const stat = fs.statSync(gitDir);
if (stat.isFile()) {
const content = fs.readFileSync(gitDir, 'utf8');
const match = content.match(/gitdir:\s*(.+)/i);
if (match) gitDir = path.resolve(match[1].trim());
}
const headFile = path.join(gitDir, 'HEAD');
if (fs.existsSync(headFile)) {
const headContent = fs.readFileSync(headFile, 'utf8').trim();
if (headContent.startsWith('ref:')) {
const refPath = headContent.replace(/^ref:\s*/, '').trim();
const refFile = path.join(gitDir, refPath);
if (fs.existsSync(refFile)) {
const sha = fs.readFileSync(refFile, 'utf8').trim();
if (sha.length >= 7) return sha.slice(0, 7);
}
// Se estiver compactado em packed-refs
const packedRefsFile = path.join(gitDir, 'packed-refs');
if (fs.existsSync(packedRefsFile)) {
const lines = fs.readFileSync(packedRefsFile, 'utf8').split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('^')) {
const parts = trimmed.split(' ');
if (parts.length >= 2 && parts[1] === refPath) {
return parts[0].slice(0, 7);
}
}
}
}
} else if (/^[0-9a-f]{7,40}$/i.test(headContent)) {
return headContent.slice(0, 7);
}
}
}
} catch (e) {
// Continua para o próximo método se falhar
}
// 3. Execução do comando git caso o binário esteja disponível
try {
const hash = execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
if (hash && hash.length >= 7) return hash.slice(0, 7);
} catch (e) {
// git não instalado ou erro
}
// 4. Arquivo version.json estático se existir
try {
if (fs.existsSync('version.json')) {
const v = JSON.parse(fs.readFileSync('version.json', 'utf8'));
if (v.commit && !v.commit.startsWith('$Format') && v.commit.length >= 7) {
return v.commit.slice(0, 7);
}
}
} catch (e) {}
return 'dev';
}
const commitHash = resolveCommitHash();
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],