Como Automatizar a Sincronização Entre um Repositório Git Local e um Servidor (S)FTP
Aprenda a montar um sistema Python que compara, envia e baixa arquivos entre um repositório Git local e um servidor FTP ou SFTP — com relatório HTML automático e commits inteligentes, sem depender de CI/CD externo.
Em muitos ambientes corporativos — especialmente em estruturas legadas ou altamente restritas — o uso de Git hospedado (GitHub, GitLab, Bitbucket) ou pipelines de CI/CD é simplesmente proibido. Mesmo assim, o desenvolvimento continua acontecendo, e manter arquivos atualizados no servidor pode se transformar em um trabalho cansativo, repetitivo e propenso a erros.
Fazer upload manual via FTP, baixar alterações feitas diretamente no servidor e tentar conciliar tudo com o repositório Git local rapidamente se torna um pesadelo. A boa notícia: é possível automatizar todo esse fluxo usando apenas o seu computador e acesso ao servidor (S)FTP.
Neste artigo você vai aprender como montar um sistema que:
- 🔄 Compara arquivos entre Git local e FTP
- ⬇️ Baixa arquivos quando o servidor está mais atualizado
- ⬆️ Envia arquivos quando o Git local está mais novo
- 📝 Gera relatórios HTML ou TXT
- 🏷️ Registra automaticamente mudanças no Git
Tudo isso sem depender de nenhum serviço externo.
🎯 Por que automatizar?
Imagine este cenário:
- Um time precisa atualizar arquivos em um servidor legado via FTP
- Vários desenvolvedores têm acesso ao mesmo ambiente
- Mudanças às vezes são feitas diretamente no servidor por outro departamento
- Não existe integração contínua
- Não existe ambiente de testes
Sem automação, esse fluxo depende de disciplina — e sorte. Com automação, você ganha:
Automatizar a sincronização transforma um processo arriscado em um fluxo de trabalho consistente.
🧩 Como funciona a lógica da sincronização?
A ideia é simples e poderosa: comparar a versão do arquivo no Git com a versão no servidor (S)FTP.
Sempre que um arquivo é encontrado, três cenários são possíveis:
- A versão remota é mais nova → Baixar e registrar no Git
- A versão local é mais nova → Enviar para o FTP e registrar no relatório
- As versões são iguais → Ignorar para poupar tempo
Esse fluxo garante que sempre exista um único ponto de verdade, mesmo em ambientes descentralizados.
📊 Diagrama do fluxo
[Git Local] → Verifica arquivo → [FTP]
↙ ↓ ↘
FTP é novo Igual Git é novo
Baixar Nada Enviar
Commit | |
Relatório ←-----
sync-flow-diagram.svg com o fluxo visual.
⚙️ Comparando arquivos: hash, timestamps e integridade
Para decidir qual versão está mais atual, é possível usar diferentes estratégias:
No exemplo a seguir utilizamos hash + timestamp para máxima confiabilidade.
🐍 Exemplo de automação com Python
O script abaixo percorre arquivos rastreados pelo Git, compara com a versão existente no FTP, baixa ou envia conforme a necessidade, gera um relatório HTML e adiciona commits automáticos quando há atualizações vindas do servidor.
📌 Código completo (pronto para uso)
import os
import hashlib
from ftplib import FTP
from datetime import datetime
from git import Repo
# CONFIG
LOCAL_PATH = './my-local-repo'
FTP_HOST = 'ftp.example.com'
FTP_USER = 'youruser'
FTP_PASS = 'yourpass'
REMOTE_PATH = '/remote/dir'
# Git Repo
repo = Repo(LOCAL_PATH)
report = []
# Connect to FTP
ftp = FTP(FTP_HOST)
ftp.login(FTP_USER, FTP_PASS)
ftp.cwd(REMOTE_PATH)
def md5_local(file_path):
with open(file_path, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
def md5_remote(filename):
from io import BytesIO
buf = BytesIO()
try:
ftp.retrbinary(f'RETR {filename}', buf.write)
return hashlib.md5(buf.getvalue()).hexdigest()
except:
return None
for file in repo.git.ls_files().split('\n'):
local_file = os.path.join(LOCAL_PATH, file)
local_hash = md5_local(local_file)
remote_hash = md5_remote(file)
if remote_hash is None:
# Upload new file
with open(local_file, 'rb') as f:
ftp.storbinary(f'STOR {file}', f)
report.append(f"[UPLOAD] {file} was not on remote.")
elif remote_hash != local_hash:
# Compare timestamps
remote_time = ftp.sendcmd(f"MDTM {file}")[4:].strip()
remote_dt = datetime.strptime(remote_time, '%Y%m%d%H%M%S')
local_dt = datetime.fromtimestamp(os.path.getmtime(local_file))
if remote_dt > local_dt:
# Download newer remote version
with open(local_file, 'wb') as f:
ftp.retrbinary(f'RETR {file}', f.write)
repo.index.add([file])
repo.index.commit(f"[SYNC] Pulled newer {file} from FTP")
report.append(f"[DOWNLOAD] {file} - FTP was newer")
else:
# Upload newer local version
with open(local_file, 'rb') as f:
ftp.storbinary(f'STOR {file}', f)
report.append(f"[UPLOAD] {file} - Local was newer")
else:
report.append(f"[SKIP] {file} is up-to-date")
# Write report
with open(os.path.join(LOCAL_PATH, 'sync-report.html'), 'w') as report_file:
report_file.write("<h2>Sync Report</h2><ul>")
for line in report:
report_file.write(f"<li>{line}</li>")
report_file.write("</ul>")
ftp.quit()
print("✅ Sync complete.")
pip install gitpython. A biblioteca ftplib já faz parte da biblioteca padrão do Python.
📄 Relatório automático
O script gera automaticamente um arquivo sync-report.html contendo:
- Arquivos enviados ao servidor
- Arquivos baixados do servidor
- Arquivos ignorados (sem alteração)
- Data e hora da execução
- Resumo da atividade
Esse relatório pode ser arquivado, enviado por e-mail ou compartilhado com outros times de TI para fins de auditoria.
🚀 Possíveis evoluções
Se quiser tornar o sistema mais completo, você pode implementar as seguintes melhorias:
paramiko oferece suporte completo ao protocolo SFTP com autenticação por chave pública.Sincronização confiável em ambientes legados
Automatizar a sincronização entre o repositório Git local e um servidor (S)FTP elimina o risco de conflitos, perda de alterações e inconsistências. O processo se torna mais seguro, organizado e confiável — mesmo sem CI/CD ou serviços hospedados.
Se você trabalha em ambientes legados ou restritos, essa solução representa um enorme avanço em produtividade e segurança operacional. O investimento em configurar o script uma única vez se paga rapidamente em tempo economizado e erros evitados.
Categorias