Deploy do MX Reverse no Heroku

No texto anterior, comentei sobre minhas escolhas para montar um site para verificar o DNS reverso (https://eduardoklosowski.wordpress.com/2014/12/08/mx-reverse-e-escolhas-de-frameworks/). Agora comentarei sobre a hospedagem do mesmo no Heroku.

Primeiro fiz a aplicação funcionar localmente, que bastava juntar a página escrita no AngularJS para ficar disponível no WSGI. Não é uma boa prática servir arquivos estáticos pelo WSGI, porém é fica mais simples para o desenvolvimento ter apenas um servidor HTTP e é desta forma que o Heroku trabalha. Para isto reutilizei o virtualenv do Python que havia criado para fazer o desenvolvimento, com a biblioteca do DNS e o Gunicorn (servidor WSGI) já instalado, adicionei o pacote static3 e alterei no código.

Com tudo devidamente testado no Gunicorn, criei o arquivo requirements.txt que é um padrão de projetos Python para dizer as dependências, no meu caso eram todos os pacotes instalados via pip, então foi só executar pip freeze > requirements.txt, porém o mesmo pode ser editado para remover alguma coisa ou ajustes de verão dos pacotes sem problemas.

Bastava ainda dizer como iniciar a aplicação, já que o Gunicorn tem vários parâmetros e é necessário informar onde está a função WSGI dentro do código Python. No Heroku esses parâmetros ficam dentro de um arquivo chamado Procfile, que consiste em um nome mais a linha de comando, podendo ter vários processos em diferentes linhas, no meu caso fiz apenas web: gunicorn wsgi:application --access-logfile - --error-logfile -. É possível utilizar esse arquivo localmente como o Heroku faz utilizando um programa escrito em Ruby chamado Foreman, porém optei pela versão escrita em Python chamada Honcho, com ele devidamente instalado bastou um honcho start e minha aplicação estava funcionando localmente já na forma necessária para subir no Heroku.

Criei uma conta, sem mistérios. Como o Heroku trabalha com o deploy baseado em Git, é necessário cadastrar uma Chave SSH, da mesma forma que ocorre no GitHub e outros serviços que utilizam o protocolo do SSH. Como já tenho a minha chave, não precisei criar uma nova, porém quem precisar pode utilizar o comando ssh-keygen no GNU/Linux. Copiei o conteúdo do arquivo id_rsa.pub e adicionei em SSH Keys dentro do Manage Account.

Próximo passo é criar o aplicativo, que pode ser feito dentro de Personal Apps, clicando no ícone de mais. Incrivelmente esse cadastro é bem simples, o nome do aplicativo, que caso não seja especificado será gerado automaticamente e em qual região você deseja que o seu aplicativo fique rodando.

Agora a parte mais importante, enviar seu código para o servidor. Existe a opção tradicional via Git ou via Dropbox (em beta neste momento). Na opção via Dropbox, basta copiar seu código para o diretório criado, ir no Heroku e mandar ele buscar a nova versão. Como eu tenho experiência com o Git, já tinha meu código nele, achei muito mais simples adicionar um repositório remoto e fazer o push. Vendo as configurações do aplicativo, achei a URL em Settings (git@heroku.com:mxreverse.git), adicionei no repositório (git add remote heroku git@heroku.com:mxreverse.git) e enviei o código (git push heroku master).

Depois desses simples passos já tinha a minha aplicação rodando em https://mxreverse.herokuapp.com/. Obviamente por ser uma aplicação simples, sem banco de dados, não precisei fazer uma série de configurações, bem como o uso da ferramenta de linha de comando do Heroku.

Uma das vantagens do Heroku sobre outras soluções como OpenShift é a possibilidade de criar vários aplicativos de graça, uma ótima opção para quem esta desenvolvendo e quer deixar sua aplicação rodando ou um demo, depois se a quantidade de acessos crescer, você pode escalar sua aplicação lá mesmo ou mudá-la para outro lugar. Porém já ter sua aplicação disponível na internet logo no início pode motivá-lo a continuar o desenvolvimento ou trazer mais pessoas para o projeto, já que ela não terá o trabalho de fazer o deploy para ver seu projeto e a facilidade para apresentar a outras pessoas.

Estrutura interna do Git – Parte 2

Não exatamente uma sequencia, porém ainda ficaram alguns pontos que eu acho muito interessante do Git que levarei em conta quando precisar desenvolver algo.

Além dos comandos normalmente utilizados, que para quem utiliza o bash completion possui autocompletamento, existem vários comandos “ocultos”, porém documentados. O comando git cat-file do texto anterior é um exemplo. Isso ocorre pela forma que os comandos do Git foram feitos, existe um conjunto de comandos básicos para executar pequenas tarefas, assim como a filosofia UNIX (uma única coisa, porém bem feita). Ou seja, comandos para adicionar arquivos na estrutura do Git, recuperá-los, calcular diferenças, aplicá-las, lidar com braches e outros.

No capítulo 9 do Pro Git temos um exemplo melhor desses comandos, e até como fazer um commit sem utilizar o git commit. Isso mostra que até mesmos comandos internos são implementados utilizando outros comandos, dividindo o problema em partes menores.

Agora imagina que você está desenvolvendo um sistema que utiliza alguma parte do Git, como o GitHub para visualizar o código, ou o OpenShift para receber o código e fazer deploy da aplicação. Como existem várias operações você não precisa desenvolver algo para ler a estrutura do Git, pode simplesmente reutilizar esses comandos, ou seja, a API já está pronta, basta adaptá-la para a sua linguagem.

Porém não precisamos ir muito longe para tirarmos proveito disso. No meu texto Verificador de estilo de código no VIM, comentei sobre os verificadores de estilo e podemos utilizá-las em conjunto com o Git. Imagine que seu projeto tem um estilo que todo o código deve ser seguido, porém as vezes alguém acaba deixando passar um espaço a mais, troca espaço por tab. No Git podemos fazer a validação do código é permitir o commit apenas caso ele esteja de acordo com o estilo.

Para fazer essa verificação precisamos utilizar outro recurso chamado de Hook, que são scripts executados em determinados momentos (consulte o capítulo 7.3 do Pro Git). Inicialmente fiz a verificação apenas local para lembrar caso tenha alguma coisa fora do padrão, mas ainda possível forçar o commit se assim for desejado. Como os projetos tinham vários arquivos fora do estilo, queria verificar apenas os alertas referentes as minhas mudanças, até por questão de performance, se eu não alterei o arquivo não preciso ser lembrado que ele tem algum erro, deixando a saída até menos poluída e posso verificar todos os arquivos caso esteja atrás de erros.

Primeira coisa, preciso saber quais os arquivos que foram alterados no commit, para tanto utilizei o comando git diff –raw –cached, uma informação um pouco mais bruta dos arquivos selecionados com git add. Porém eu posso ter executado o git add e alterar o arquivo novamente antes de fazer o commit, então não posso simplesmente fazer a validação do código do arquivo no diretório, para isso utilizei a hash do git diff para pegar o arquivo que será commitado com git show e fazer a validação em cima dele.

Quem quiser ver como tudo ficou, tenho esse código no meu GitHub (https://github.com/eduardoklosowski/codelint), está escrito em Python, porém tenho que trabalhar melhor a documentação e ter decidido fazer tudo em inglês não ajudou, porém assim que estudar melhor as formas de documentação e opções que tenho para compartilhá-la irei fazer isso, mas se alguém for usar e tiver alguma dúvida deixe nos comentários, até ficarei feliz em saber que alguém está utilizando (testando). Report de bugs, melhorias no código ou funcionalidades novas são bem vindas. Pode ser utilizado com outras ferramentas de controle de versão, só não tenho conhecimento e necessidade para isso agora, mas pode ser facilmente desenvolvido já que o Git foi feito a parte também.

Depois de criar os arquivos de configuração em ~/.config/codelint, preparo tudo para o commit e executo git codelint, isso iniciará os validadores. Um detalhe que todos os comandos no PATH que começam com git-, exemplo git-codelint podem ser executados como comandos do git dessa forma e com autocompletamento.

Agora só falta colocar no Hook executado antes do commit, o pre-commit, seu conteúdo é simples.

#!/usr/bin/env bash
git-codelint

Caso queira verificar os arquivos antes de commitar posso executar git codelint e quando der o git commit será verificado, se tiver algum erro o commit será interrompido para correção, ou posso forçar com git commit -n.

Estrutura interna do Git

Existem diversos tutoriais e guias sobre como utilizar o Git e seu funcionamento, como por exemplo o Try Git e o Pro Git. Todos esses materiais auxiliam nos primeiros passos com a ferramenta, porém quando se começa a trabalhar com recursos mais avançados, principalmente branches e merge/rebase as coisas tentem a ficarem mais confusas para quem está iniciando.

Eu particularmente li o livro Pro Git, aprendi muito sobre o funcionamento e seus recursos. Como faz algum tempo quase um ano que fiz esta leitura, irei fazer um resumo para relembrar e compartilhar com vocês sobre a parte interna do Git, que foi o mais importante, no meu caso, para aprender ou imaginar como os demais recursos funcionam. Como exemplo irei utilizar um código publicado do meu Git Hub (https://github.com/eduardoklosowski/webprint), assim todos podem visualizar os mesmos resultados.

Talvez o mais importante a se entender é que o Git possui um sistema para armazenar objetos, que na verdade é tudo o que está na sua estrutura interna, como arquivos, diretórios e commits, podendo ser acessados por uma hash SHA1. Um exemplo prático, na data de publicação deste texto, o último commit deste repositório era o “cb584a1992fef3c286c5b36d2aba1c66bfe9b3a2”, o que significa que existe um commit que pode ser acessado com essa hash dentro da estrutura do Git, podendo ser visualizado com o comando git cat-file -p cb584a1992fef3c286c5b36d2aba1c66bfe9b3a2:

tree 73e7173b7f13853343b3bbad766f2b54852e8995
parent ff12eb157275a886feb9a039819cfc067e7292d5
author Eduardo Klosowski 1406755582 -0300
committer Eduardo Klosowski 1406755582 -0300

Correção da exclusão da imagem

Além das informações de quem fez as modificações, quem fez o commit, suas respectivas datas e mensagem, temos duas informações importantes. A primeira é o “parent”, seu valor é o hash do commit anterior, que pode ser verificado com um git log, é possível seguir esse valor dos commits até primeiro, que não fará referência a outro justamente por ser o primeiro. Se em algum comento tiver uma ramificação no histórico dos commits, o ponto de união apresentará mais de um parent, justamente para mostrar essa união.

A segunda informação importante e o “tree” que é um objeto de diretório, assim como existe na maioria dos sistemas de arquivos, é possível considerar essa estrutura interna do Git como um sistema de arquivos, porém específica para controle de versão, existem outros tipos de objetos além de arquivos e diretórios, mas não quem é o domo, data de criação e modificação por exemplo, isso torna as vezes mais rápido colocar um arquivo na estrutura do Git que no sistema de arquivos do HD por exemplo. Utilizando o mesmo comando para visualizar o objeto do diretório (git cat-file -p 73e7173b7f13853343b3bbad766f2b54852e8995) temos:

100644 blob 7821b4a1a8bc6e6d29368e25bb1d2a4b42f3754e .gitignore
100644 blob bcca729f3ce131d32dbe9af737e8d9ed049fe25d LICENSE
100644 blob d45ebab4a0fb6137cd519a6448cb4313a8e0ba2a README.md
100755 blob 3f3fdf92237f708dd3494b63b7fce452d406d870 manage.py
040000 tree f6e01702b079c29f3798ed54349c2218e4131fc3 webprint

Essas informações, pela ordem das colunas são: permissões do objeto (os últimos 3 dígitos são a mesmas permissões do EXT4 por exemplo), o tipo de objeto (lembrando que tree é outro diretório e blob são arquivos), o hash do objeto referenciado e finalmente seu nome.

Os arquivos também podemos acessar pelo hash, no caso git cat-file -p bcca729f3ce131d32dbe9af737e8d9ed049fe25d teremos conteúdo do arquivo “LICENSE” desse commit específico. Quem já trabalhou com ponteiros e estruturas em C já deve ter entendido como o Git funciona internamente, já que é a mesma lógica, de ter referências (ponteiros) para outros objetos (estruturas).

Sendo um pouco atento você poderá notar que o Git não gravou a diferença entre os commits em sua estrutura em nenhum momento, o motivo é simples, ele não faz isso. Toda vez que precisar a diferença os objetos são acessados e calculados na hora, pode ser um pouco mais lento que simplesmente ter o resultado já armazenados, porém se você tiver vários commits entre os objetos que for comparar é mais simples ter os dois e calcular a diferença que juntar todas as diferenças de cada commit.

Outro ponto importante é que cada commit tem referência para todos os arquivos que estão no projeto naquele momento, assim como um “snapshot”, então mesmo que algum arquivo ou diretório não tenham sofrido mudanças, eles estarão em ambos os commits, porém como não houve mudanças, os dois commits (ou mais) podem apontar para os mesmos objetos da estrutura, assim já otimizando o espaço e evitando objetos duplicados.

Agora se você tiver um arquivo com centenas de linhas e alterar apenas uma, você terá uma cópia completa do arquivo com apenas essa uma linha alterada dentro da estrutura do Git, a princípio parece desperdício de espaço, porém mesmo esse arquivo exista dentro da estrutura do Git, não quer dizer que ele existirá no sistema de arquivos, quando existem vários objetos semelhantes, o Git junta esses objetos em um “pack”, um único arquivo para esses vários objetos, sendo elegido um para estar na integra dentro do pack e os demais apenas as diferenças.

Então é verdade que o Git guarda diferenças de arquivos, porém no sistema de arquivos e não em sua estrutura de objetos, assim não tendo o problema de arquivos quase duplicados. Outro ponto importante que não é necessariamente a primeira ou última versão do arquivo que estará na integra no pacote, podendo ser uma versão intermediária que gerarias diferenças menores, reduzindo ainda mais o tamanho do pack no sistema de arquivos.

Então para calcular a diferença de dois arquivos com vários commits de modificação, na pior das hipóteses terá que ler dois arquivos do pack, aplicar duas diferenças e calcular as diferenças entre eles, em vez de somar as diferenças de cada commit, ganhando assim mais desempenho.

Sobre os branches, nada mais são que referências aos commits, ou seja, guardam o hash de algum commit, que podem ser visualizados dentro do diretório “.git” do projeto com o comando cat .git/refs/heads/master por exemplo.

Todos os processos que alteram o histórico do Git, como rebase e cherry-pick são alterações nessas referências, porém se os commits forem alterados, serão gerados novos commits com outros hashs, os antigos não deixam de existir de imediato, apenas não tem mais nenhuma referência apontando direta ou indiretamente para eles, por isso não aparecem mais, porém ainda estão na estrutura do Git e podem ser acessados pelos seus hash, a menos que tenham sido removidos automaticamente pelo Git depois de algum tempo ou com o comando git prune.

Esse é um assunto extenso, e só é possível se dominar com o uso do Git, porém entender isso me ajudou e ajuda muito quando preciso fazer algo no Git. Recomendo a leitura do Pro Git para todos, já que ele vai do básico ao avançado, é gratuito e está em português, além de mostrar muito mais exemplos como esse descrito aqui.