Usando git e patch para deployment

Isso é básico: Desde os primórdios o Unix possui os utilitários diffpatch. O primeiro cria um arquivo texto contendo apenas as modificações feitas em arquivos de um diretório (incluindo os subdiretórios) e o segundo é usado para aplicar essas modificações. Vou usar meu “projeto” sandbox, que está no github como exemplo… Lá eu tenho um diretório, chamado bash/, que contém shell scripts que uso regularmente. Um deles é o vid2mp4, que uso para converter vídeos de qualquer container e formatos de codecs para MP4 (h264/AC3).

Vamos supor que você obteve esse arquivo e o colocou em /usr/local/bin/.

Acontece que eu fiz uma modificação em vid2mp4 e quero que você atualize o arquivo na sua máquina… Mas você não sabe lidar com o git!  Só quer que seu script seja remendado por mim…

Criando um patch:

Eu posso enviar para você a diferença entre a última versão (a que você tem) e a mais atual (a minha, modificada). Faço isso, do meu lado, com:

$ git diff --no-prefix --minimal HEAD^..HEAD > \
  diff-28-Abr-2015.patch

Este comando criará um arquivo diff-28-Abr-2015.patch contendo a diferença entre o commit HEAD e o anterior (HEAD^). A opção –no-prefix diz ao gt para não usar prefixos fictícios (git cria “diretórios falsos” chamados a e b, no arquivo resultante). E a opçao –minimal tenta listar o mínimo possível para obtermos uma diferença entre os arquivos que seja significativa.

Git pode criar patches fazendo comparação entre comits, mas também entre um commit e um arquivo. No exemplo acima usei a sintaxe HEAD^..HEAD para comparar o penúltimo commit (HEAD^) com o último (HEAD), mas você poderia usar duas tags, ou uma tag e um hash, ou qualquer sintaxe permitida pelo git… Inclusive um arquivo e uma referência a um commit:

$ git diff --no-prefix --minimal HEAD vid2mp4

Essa linha compara o arquivo vid2mp4 com o último commit, mas poderiamos ter qualquer referências a commits. Aliás, a linha acima pode ser abreviada retirando-se o HEAD, que é assumido por default.

Eis o diff file gerado:

diff --git bash/vid2mp4 bash/vid2mp4
index c9a10b9..0867cf7 100755
--- bash/vid2mp4
+++ bash/vid2mp4
@@ -8,20 +8,7 @@
 EXTRA_PARAMS="-map_metadata -1" # Retira metadados do vídeo destino.

 # Usa preset do x264 com maior poder de análise.
-VPARAMS="-c:v libx264 -crf 18 -preset veryslow"
-
-# Usa tunning film ou animation. "film" é default, se o último parâmetro
-# não for informado.
-if [ $# -eq 2 ]; then
-  case $2 in
-    (film) VPARAMS="$VPARAMS -tune film";;
-    (animation) VPARAMS="$VPARAMS -tune animation";;
-    (*) echo "ERROR: tunning parameter must be not given, \"film\" or \"animation\".";
-        exit;;
-  esac
-else
-  VPARAMS="$VPARAMS -tune film"
-fi
+VPARAMS="-c:v libx264 -q:v 0"

 ABR=$(ffmpeg -i "$1" 2>&1 | grep Audio: | sed 's/^.*, \([0-9]*\) kb\/s.*$/\1/');
 if [ $ABR -le 64 ]; then ABR=64; else

Um diff file tem um formato que o utilitário patch entende. Ele começa dizendo o que usamos para criá-lo. Ao usar o comando git diff, o git usou o diff. Isso é indicado na primeira linha. A segunda linha consiste de informações para o próprio git, ignorada pelo patch. A brincadeira começa a partir da linha 3.

Não importa muito a estrutura do diff file. O que interessa é que as linhas começadas por ‘-‘ serão retiradas do arquivo original e as iniciadas com ‘+’ foram adicionadas. As outras linhas que aparecem estão ali apenas paa que o comando patch tenha uma referência…

Remendando…:

Para aplicar esse “remendo” (patch), basta fazer, do seu lado:

$ cd /usr/local/bin/
$ sudo patch -p1 < ~/diff-28-Abr-2015.patch
patching file vid2mp4

Pronto! Apenas o arquivo vid2mp4 foi modificado (a mensagem “patching file vid2mp4” mostra isso!). A opção ‘-p1’ diz ao patch que ele deve ignorar o diretório relativo bash/, contido no diff file. Precisei usar sudo aqui porque o diretório /usr/local/bin/ tem como owner o root e não permite escritas por outros usuários. O utilitário patch não é uma ferramentas administrativa, está disponível em qualquer sistema que siga o padrão POSIX.

A grande vantagem de distribuir patches é que o usuário não precisa lidar com o git e toda a árvore de diretórios do seu projeto que foi modificada estará contida no diff-file. Mas, apenas as linhas que foram modificadas!

Deployment:

Junte o recurso do diff e do patch com shell scriptstarballs e a capacidade que agora temos de criar um arquivo .run (veja posts anteriores) e você tem um meio fabuloso de distribuir não somente suas aplicações, mas bug fixes e novas features. Poderíamos, por exemplo, no ato de instalação da aplicação, criar um arquivo /etc/myproj.conf (onde myproj é o nome do seu projeto), contendo definições de variáveis de ambiente como MYPROJ_INSTALL_DIR, contendo o diretório alternativo onde sua aplicação está instalada… dentre outras variáveis de configuração… Daí, nos bug fixes, você tem como “executar” esse script para obter essas variáveis e fazer algo assim (num bugfix):

#!/bin/bash

# fix.sh

error() {
  echo "Erro aplicando BUGFIX... Seu sistema está instável!!!"
  echo "Devia ter feito backup... vocẽ fez?!"
}

if [ ! -x /etc/myproj.conf ]; then
  echo "Arquivo de configuração não pode ser encontrado"
  echo "ou há um erro de permissões."
  exit 1
fi

. /etc/myproj.conf        # carrega configurações de myproj.

# Descompacta um monte de concertos de arquivos binários do seu código.
tar xzf fixes.tar.gz -C "$MYPROJ_INSTALL_DIR" 2>&1 /dev/null
if [ #? -ne 0 ]; then
  error
  exit 1
fi

# Faz o remendo dos arquivos texto no seu projeto.
patch -d "$MYPROJ_INSTALL_DIR" -p1 < fixes.patch 2>&1 /dev/null
if [ #? -ne 0 ]; then
  error
  exit 1
fi

echo "BUGFIX completado com sucesso!"
exit 0

Agora temos os sequintes arquivos para colocar no nosso bugfix.run: fix.sh, fixes.path e fixes.tar.gz. O primeiro é o arquivo acima, os outros dois contém os “acertos” que serão aplicados… O arquivo fixes.tar.gz usei para o caso de precisar distribuir arquivos binários grandes, que serão colocados em locais fixos da aplicação. A mágica dos patches das configurações e arquivos texto em geral está no fixes.patch, criado pelo git!

Legal, huh?

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s