Executando verificação de segurança...
2

[TUTORIAL] Deploy para iniciantes: Como montar um fluxo Git (Maquina Local, repositorio Hub e pasta Producao)

Voce ja suou frio na hora de mandar uma atualizacao para o servidor ?

Ou pior, ja editou um arquivo via SSH direto na producao, esquecendo de salvar na sua maquina local e perdeu tudo numa atualizacao seguinte ?

O DEV que nunca detonou alguma alteracao assim que atire a primeira pedra.

Hoje vou compartilhar uma topologia de deploy que considero pratica para o dia-a-dia.

Ela eh simples, nao depende de pipelines complexas (como GitHub Actions) e te ajuda no controle do codigo.

O cenario inicial nao usa GitHub, mas nada impede de adicionar depois (inclusive mostro como fazer isso).

Fase 1. O Circuito Fechado (Local -> Hub -> Producao)

Vamos dividir a arquitetura inicial em tres partes:

  1. Local: A sua maquina, onde voce faz o desenvolvimento, testa e quebra as coisas sem medo.

  2. Hub (Servidor): Um repositorio Git que fica dentro da sua VPS (ex: na pasta ~/projetos/meu-app). Ele serve como a "porta de entrada" das suas atualizacoes.

  3. Producao: A pasta publica onde o seu app roda para o usuario final (ex: /var/www/html/meu-app).

Notou que nao tem GitHub no processo ? Depois falamos dele (Fase 2).

1.1. O Fluxo proposto

Voce desenvolve tudo local, normal.

Ficou satisfeito ? Beleza, agora manda (via git) para o Hub (que fica no servidor) e auto-magicamente ele entra em producao no diretorio real do app.

1.2. Criando o repositorio Hub do servidor

A primeira coisa eh preparar o terreno no servidor.

Acesse sua VPS via SSH, crie a pasta do projeto e inicie o Git la dentro com uma configuracao especial para aceitar o push:

mkdir -p ~/projetos/meu-app

cd ~/projetos/meu-app

git init

git config receive.denyCurrentBranch updateInstead

Notou que nao tem --bare no git init ? Depois falamos dele (item 2.4.1.).

1.3. Atrelando teu desenvolvimento local ao Hub remoto

Vai na pasta do teu projeto local (na sua maquina) e configure o Hub (do servidor) como destino principal:

git remote add origin usuario@ip-do-servidor:~/projetos/meu-app

Quando a feature ta pronta e testada, voce simplesmente roda:

git push origin main

NOTA: Em alguns casos ao inves de main pode ser master.

1.4. O "Hook" Magico no Servidor que leva do Hub para producao

Qual a magica para o que foi enviado para o Hub ir direto para producao ?

Usando Git Hooks, que permite executar scripts automaticamente apos receber um push.

Vai na pasta do Hub do servidor, e dentro dele na pasta .git/hooks

Crie um arquivo chamado post-receive (ou seja .git/hooks/post-receive)

O script nao tem muita firula (mas voce pode incrementar conforme a necessidade):

#!/bin/bash
# Onde os arquivos do site vao ficar visiveis para o publico
TARGET="/var/www/html/meu-app"

echo "Inicio deploy"

# Forca a pasta alvo a extrair os arquivos exatos que vieram no push
git --work-tree=$TARGET  checkout -f

# Opcional: Rodar comandos especificos, como build
# cd $TARGET && npm run build

echo "Deploy concluido com sucesso !"

Nao esqueca de rodar chmod +x post-receive para dar permissao de execucao ao script

NOTA: caso de erro Fatal: not a git repository, pode ser necessario colocar o --git-dir de forma explicita, neste caso use:

GIT_DIR="/home/usuario/projetos/meu-app/.git"
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f

1.5. E agora ?

So isso.

Edita local, comita e push.

No VSCode, apos comitar (local) ele aparece a opcao sync changes que faz o push.


Fase 2. Colocando o GitHub na jogada

O projeto cresceu, voce quer compartilhar... uma opcao eh adicionar o GitHub.

Nesta topologia o GitHub entra apenas como um espelho paralelo, sem quebrar ou atrasar o seu deploy de producao.

Seu ambiente Local continua sendo o centro de tudo.

No seu terminal local, como o nome origin ja esta sendo usado pelo seu servidor de producao, voce adiciona o GitHub como um remote secundario (vamos chamar de meugithub):

# Adiciona o GitHub como seu espelho
git remote add meugithub https://github.com/seu-usuario/seu-projeto.git

Sua rotina de trabalho ganha dois caminhos independentes:

  • Quer botar a versao nova no ar ? (ou o padrao do sync do VSCode)

    git push origin main (Manda pro Hub e atualiza a Producao).
    
  • Fez o backup do dia ou quer abrir um Pull Request ?

    git push meugithub main (Manda pro GitHub).
    

Se o GitHub (ou GitLab) ficarem fora do ar, voce continua publicando normalmente, pois o caminho Local -> Hub eh direto maquina a maquina.

NOTA: Por padrao no VSCode a opcao sync changes que faz o push envia para o origin. Se quiser ter mais controle, use o terminal.

2.1. O detalhe crucial: .gitignore

O .gitignore é teu amigo, coloque nele os arquivos ou diretorios que NAO DEVEM IR PARA O REPOSITORIO HUB/PRODUCAO !!!

Por exemplo voce nao quer mandar o .env para o GitHub, entao adicione ele no .gitignore do seu ambiente Local: que eh de onde voce envia para o GitHub.

Eu costumo tambem colocar coisas que nao devem ir para GitHub no .gitignore do Hub por precaucao (caso em algum momento precisar enviar do Hub para o GitHub)

2.2. Alguem fez uma Pull Request no GitHub, e agora ?

O GitHub passa a ser o seu "ponto de integracao".

Se alguem (um colaborador ou ate voce mesmo em outro computador) abrir um PR no GitHub, eh so fazer este "triangulo":

  • Revisao e Merge: Voce entra no site do GitHub, revisa o codigo e clica no botao "Merge Pull Request". Agora, o codigo atualizado esta no GitHub, mas a sua maquina Local e o seu Servidor (Producao) ainda estao desatualizados.

  • Sincroniza o Local: No seu terminal local, voce puxa as novidades do GitHub:

git pull meugithub main
  • Deploy para Producao: Agora que a sua maquina local tem o codigo do PR voce pode testar localmente para garantir que nada quebrou, e so entao envia para o servidor:
git push origin main

Resumo da opera: O PR entra pelo GitHub -> Desce para o seu PC -> Sobe para a Producao. O seu ambiente Local continua sendo o "ponto focal" do trabalho.

2.3. Como faco para o VSCode usar o GitHub no sync changes ?

Para mudar isso e fazer o VSCode usar o GitHub (meugithub) como destino padrao do botao sync changes, rode no seu terminal local:

git push -u meugithub main

A flag -u significa --set-upstream e ela diz pro Git: "A partir de agora, o padrao desta branch local eh o meugithub".

O que acontece depois de rodar isso?

Quando voce clicar no "Sync Changes" no VSCode, ele vai enviar (push) e puxar (pull) do GitHub

Para mandar para a producao, voce tera que abrir o terminal e digitar manualmente:

git push origin main

Se quiser voltar a usar o servidor de producao como destino principal do botao Sync, basta rodar:

git push -u origin main

2.4. Aviso aos navegantes

Para varias situacoes, as sugestoes que dei ate aqui podem parecer inadequadas.

Vamos aos apontamentos as mais comuns:

2.4.1. O Hub nao poderia ter sido inicializado com git init --bare ?

Sim, eh verdade - e ai nao precisa de updateInstead. Entretanto, contudo, todavia, eu gosto da ideia do repositorio Hub ter a mesma estrutura do projeto.

2.4.2. Usando checkout -f sozinho nao tem risco de ficarem arquivos orfaos antigos ?

Eventualmente pode acontecer. Alguns autores recomendam:

git --git-dir="$GIT_DIR" --work-tree="$TARGET" reset --hard

git --git-dir="$GIT_DIR" --work-tree="$TARGET" clean -fdn > /var/log/deploy_orfaos.log

git --git-dir="$GIT_DIR" --work-tree="$TARGET" clean -fd

Prefiro diff e limpar manualmente (mas pode ser inviavel em projetos grandes...)

O grande perigo do git clean -fd eh que ele confia cegamente no seu .gitignore.

Se alguem na equipe (ou voce num dia de pressa) remover a pasta de uploads (p.ex. storage) do .gitignore local e der o push, o git clean no servidor vai olhar e dizer: "Opa, isso aqui nao ta ignorado e nao ta no repositório. Vou apagar !" e la se vao os arquivos dos seus clientes - pois eh...

Uma alternativa seria este codigo no post-receive usando rsync:

#!/bin/bash
TEMP_DIR="/var/www/meu-app-temp"
TARGET="/var/www/html/meu-app"

# 1. Joga o codigo novo numa pasta limpa e segura
git --work-tree=$TEMP_DIR checkout -f main

# 2. Sincroniza apagando os orfaos, mas ignorando diretorios especificos: USE COM CUIDADO !!!
rsync -av --delete \
      --exclude='.env' \
      --exclude='storage/' \
      --exclude='node_modules/' \
      $TEMP_DIR/ $TARGET/

Outra alternativa eh o deploy atomico (item 2.4.5. abaixo)

2.4.3. E as branchs ?

Sim, se tiver desenvolvimento em multiplas branchs, cabem algumas analises extras (p.ex. checkout apenas da main).

2.4.4. O uso de origin e meugithub vai contra as convencoes ?

De fato alguns autores recomendam:

origin repositorio principal (ou canonico) (normalmente GitHub/GitLab)

deploy repositorio de producao (~/projetos/meu-app em usuario@ip-do-servidor)

Aqui eu bato o pe: nao gosto da ideia do GitHub como principal, mas entendo este tipo de uso em projetos grandes.

2.4.5. Deploy atomico (e rollback por release)

Ao inves de fazer o checkout direto para a pasta de producao, uma opcao eh usar uma pasta de releases, por exemplo:

  • extrai nova release em pasta nova (/var/www/releases/20260325-143000)
  • roda build
  • poe links de arquivos e/ou diretorios estaticos entre releases (.env, storage)
  • valida
  • troca symlink current (ln -s /var/www/releases/20260325-143000 /var/www/html/meu-app)
  • voltar rollback = voltar symlink

Concordo que em projetos grandes faz sentido.

2.4.6. E se precisar fazer um Rollback via git ?

Liste os commits, pega o ID e revert

git log --oneline

git revert <commit>

git push origin main

2.4.7. Ta maluco em sugerir rodar cd $TARGET && npm run build ?

Sim, existem riscos: lixo, build parcial, dependencias.

Usar o Deploy atomico (item 2.4.5. acima) ou pelo menos uma pasta intermediaria para build.

2.4.8. Como faco para nao precisar usar usuario/senha no git remote ?

Na sua maquina (p.ex. linux)

/etc/ssh/ssh_config
host meurepo
  HostName servidor.com.br
  User usuario
  IdentityFile ~/.ssh/id_ed25519

E coloque no servidor (/home/usuario/.ssh/authorized_keys) o conteudo de ~/.ssh/id_ed25519.pub

Ai da para usar:

git remote add origin meurepo:~/projetos/meu-app

2.4.9. Nao era melhor usar um pipeline mais robusto ?

Afinal, do jeito que esta:

  • producao recebe push direto

  • pouca governanca

  • sem aprovacao automatica

  • sem testes automatizados

  • sem trilha forte de release

Sim, tudo isso eh verdade: esse fluxo funciona para projetos pequenos, VPS simples, sites pessoais, apps internos ou MVPs. Para projetos maiores o ideal evoluir para CI/CD, build reprodutivel, releases e ambientes separados.

2.4.10. Exemplo de uma topologia mais purista

  • servidor (com bare)
mkdir -p /home/usuario/repos/meu-app.git
cd /home/usuario/repos/meu-app.git
git init --bare
  • na sua maquina (usando o GtHub como principal/origin e deploy do servidor)
git remote add deploy deploy@meu-servidor:/home/usuario/repos/meu-app.git
git remote add origin [email protected]:seu-usuario/meu-app.git
  • hook post-receive (basico com clean)
#!/bin/bash
set -e

GIT_DIR="/home/deploy/repos/meu-app.git"
TARGET="/var/www/meu-app"
BRANCH="refs/heads/main"

while read oldrev newrev refname; do
  if [ "$refname" = "$BRANCH" ]; then
    echo "Deploy da main iniciado..."

    short_sha=$(echo "$newrev" | cut -c1-8)
    release_name="$(date '+%Y%m%d%H%M%S')-$short_sha"

    mkdir -p "$TARGET"

    git --git-dir="$GIT_DIR" --work-tree="$TARGET" checkout -f main

    git --git-dir="$GIT_DIR" --work-tree="$TARGET" clean -fdn > /var/log/deploy_orfaos${release_name}.log

    git --git-dir="$GIT_DIR" --work-tree="$TARGET" clean -fd

    # Exemplo:
    # cd "$TARGET"
    # npm run build

    echo "Deploy concluído."
  fi
done
  • hook post-receive (basico com release/symlink)
#!/bin/bash
set -euo pipefail

GIT_DIR="/home/usuario/repos/meu-app.git"
APP_ROOT="/var/www/meu-app"
RELEASES_DIR="$APP_ROOT/releases"
SHARED_DIR="$APP_ROOT/shared"
CURRENT_LINK="$APP_ROOT/current"
BRANCH="refs/heads/main"

while read -r oldrev newrev refname; do
  if [ "$refname" = "$BRANCH" ]; then
    mkdir -p "$RELEASES_DIR"
    mkdir -p "$SHARED_DIR"

    short_sha=$(echo "$newrev" | cut -c1-8)

    release_name="$(date '+%Y%m%d%H%M%S')-$short_sha"

    release_path="$RELEASES_DIR/$release_name"

    mkdir -p "$release_path"

    git --git-dir="$GIT_DIR" archive "$newrev" | tar -x -C "$release_path"

    # para cada arquivo/pasta no SHARED_DIR, cria um link na release
    find "$SHARED_DIR" -mindepth 1 -maxdepth 1 | while read -r shared_path; do
      item_name="$(basename "$shared_path")"
      release_item="$release_path/$item_name"
      rm -rf "$release_item"
      ln -sfn "$shared_path" "$release_item"
    done

    # cd "$release_path"
    # npm ci
    # npm run build
    # if teste automatizado OK
    ln -sfn "$release_path" "$CURRENT_LINK"
    # fi
  fi
done

Assim, se precisar voltar um release:

  • releases disponiveis
ls -1dt /var/www/meu-app/releases/*
  • retorna aquele especifico
ln -sfn /var/www/meu-app/releases/20260325142001-9f8e7d6c /var/www/meu-app/current

Conclusao

Nao eh uma estrutura sofisticada, mas da para o gasto do dia-a-dia.

Eh possivel criar algo mais complexo, digamos um ambiente intermediario de homologacao contendo um espelho do que tem em producao e talvez com arquivos de dados reduzido para fazer testes mais elaborados/automatizados e deploy atomico (spoiler: eu costumo fazer algo semelhante com projetos mais casca-grossa).

NOTA: Claro que vao sugerir outros fluxos nos comentarios, e de boa, a ideia eh exatamente essa: pega um insight, olha o que serve para teu caso e adapta, transforma, expande, melhora. Nem de longe este eh o 'the best' ou unico fluxo, eh so o primeiro passo. Por exemplo, nao gosto de mandar tudo para o GitHub direto, por isso uso um Hub meu para controle e so envio para o GitHub depois de maturado, mas ja vi fluxos que mandam direto para la.

Espero que sirva de inspiracao no uso do dia-a-dia.

Saude e Sucesso !

Carregando publicação patrocinada...
2

Lembrei dos sites em PHP, onde o pessoal editava o arquivo direto em produção via FTP.

No mais, achei o processo meio trabalhoso para eu não fazer um CI/CD, mas cada um é cada um.

1

Meus 2 cents extendidos,

Obrigado pelo comentario !

Apesar do artigo um pouco longo (tentei cobrir varios pontos), na pratica o uso se constitui apenas de:

a) 3 comandos para iniciar o repositorio no servidor

b) Criacao de 1 script (.git/hooks/post-receive) para copia do repositorio para pasta de producao

c) Uso no dia-a-dia com commit e push no repositorio (o que ja eh normal dentro do vscode, etc)

Talvez tenha parecido um pouco complexo porque falei de varios outros aspectos - acabei me empolgando um pouco aqui.

Quanto a um fluxo CI/CD, eh uma ideia para um proximo artigo - so preciso manter simples.

Saude e Sucesso !