Processo de Deploy (Re-)Automatizado para Aplicações Javascript

Publicado por Luciano Marcelino no dia dev

botão para shipar

Aqui na Resultados Digitais nós temos alguns scripts que os clientes colocam em suas páginas para enviar alguma informação ao RD Station. Nós atualizamos esses scripts frequentemente, e para manter nosso fluxo de desenvolvimento ágil, automatizamos o processo de deploy desses scripts. Já existe, aqui no Ship It!, um post dedicado a explicar o funcionamento desse deploy automatizado de scripts. Em suma, utilizamos o Grunt para executar as tarefas abaixo com apenas um comando.

Eis que existe uma quinta tarefa que nós continuávamos fazendo manualmente: a invalidação do cache da CDN referente ao script deployado. Vamos destrinchar isso um pouco mais?

Deploy semi-automatizado

É muito lindo digitar grunt deploy e ter seu script na Amazon em questão de segundos. Acontece que após o deploy, vêm os testes em produção. Para fazer esses testes, eu preciso do script atualizado na página que vou testar. O script está lá, mas está desatualizado. Todos os links para nossos scripts utilizam nossa CDN e ela tem cache nos arquivos. Quando se altera um arquivo na S3 da amazon, é preciso invalidar o cache da CDN correspondente para ver as alterações através dela. Considerando que eu via meu processo como automatizado, pensar em invalidar o cache já dá um baita trabalho. Então eu mudei:

Como era antes Como é agora
grunt deploy grunt deploy
Fazer login no console da amazon (2 steps authentication) Testar minhas alterações
Ir até a CDN do bucket em que eu mexi
Criar uma invalidação da CDN
Testar minhas alterações

Code is cheap, show me the talk!

O deploy automatizado

Eu queria manter o deploy automatizado, e queria também que o cache fosse invalidado automaticamente. Pesquisei alguns plugins e encontrei o grunt-aws, um plugin mais atual se comparado ao utilizado anteriormente. Além disso, ele já possui implementadas tanto a tarefa de deploy, como a de invalidar o cache. Não bastasse isso, ainda tem uma documentação bem detalhada, o que facilitou muito a minha vida. Vou descrever brevemente os passos de como eu o instalei e o configurei para que lhe sirva de base, quando precisar.

Instalando e configurando o plugin

Para instalar o plugin, digite no terminal:

1
npm install grunt-aws-s3 --save-dev

No seu Gruntfile, adicione:

1
grunt.loadNpmTasks('grunt-aws');

Nós utilizamos um arquivo de configurações, o aws_config.json, que contém as chaves de acesso AWSAccessKeyId, AWSAccessKeyKey, o Bucket utilizado na S3 e o distributionId da CDN correspondente. Para utilizar as informações desse arquivo, basta carregá-lo no Gruntfile assim:

1
aws: grunt.file.readJSON('.aws_config.json'),

Aqui na RD, temos alguns repositórios que são públicos, então não é seguro mantermos nele informações de dados de acesso. Para resolver esse problema, esse arquivo é mantido localmente. Deixamos um modelo no repositório para que os novos usuários possam configurá-lo e o colocamos no gitignore. Esse é o arquivo de configuração:

1
2
3
4
5
6
7
  {
    "AWSAccessKeyId" : "AKxxxxxxxxxx",
    "AWSSecretKey"   : "super-secret-key",
    "bucket"         : "current-bucket",
    "destination"    : "js/integration",
    "distributionId" : "XXXXXXXXXXXXX"
  }

Configurando o Deploy

O plugin permite diversas configurações que podem ser vistas na própria documentação. No meu caso, não precisei definir nada especial, apenas o que é obrigatório e o acesso do script para read-only. Minha configuração ficou desse jeito:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
s3: {
  options: {
    accessKeyId: '<%= aws.AWSAccessKeyId %>',
    secretAccessKey: '<%= aws.AWSSecretKey %>',
    bucket: '<%= aws.bucket %>',
    access: 'public-read',
    region: 'sa-east-1'
  },
  beta: {
    cwd: 'build/',
    src: ['<%= pkg.name %>.min.js'],
    dest: '<%= aws.destination %>/beta/'
  },
  stable: {
    cwd: 'build/',
    src: ['<%= pkg.name %>.min.js'],
    dest: '<%= aws.destination %>/stable/'
  }
},

Traduzindo: O grunt irá fazer o seguinte:

  • Entrar na pasta local build e selecionar o arquivo nome-do-projeto.min.js;
  • Acessar o bucket descrito no aws_config com as respectivas credenciais;
  • Ir até a pasta descrita no aws_config;
  • Entrar na pasta beta ou stable, dependendo da task chamada;
  • Fazer upload do arquivo para essa pasta com a privacidade pública para leitura..

Configurando o cache invalidation

Novamente, o plugin permite diversas configurações, ainda assim, eu precisei configurar apenas as credenciais e os caminhos para invalidação. A identificação dos buckets da S3 da Amazon no Cloudfront é feita via um distributionId. Você pode observar que esse dado estava no aws_config mostrado acima. É possível invalidar o cache de toda a distribuição (todo o bucket), ou apenas alguma pasta ou algum arquivo dela. Como o deploy é feito sempre para uma pasta específica (beta ou stable), eu preciso invalidar apenas o cache dessa pasta. Eis a configuração:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cloudfront: {
  options: {
    accessKeyId: '<%= aws.AWSAccessKeyId %>',
    secretAccessKey: '<%= aws.AWSSecretKey %>',
    distributionId: '<%= aws.distributionId %>',
  },
  beta: {
    options: {
      invalidations: [
        '/js/integration/beta/*'
      ],
    }
  },
  stable: {
    options: {
      invalidations: [
        '/js/integration/stable/*'
      ],
    }
  }
}

Traduzindo: O grunt irá fazer o seguinte: - Acessar o Cloudfront da Amazon e ir até a distribuição configurada no aws_config - Invalidar o cache de todos os arquivos existentes na pasta js/integration/beta ou stable, quando for o caso.

Criando as tasks

Criei uma task para fazer deploy do script no ambiente beta e uma outra task para deploy em ambiente stable. Foi mais fácil do que fazer um Hello World em Ruby ;)

1
2
grunt.registerTask('deploy:beta', ['s3:beta', 'cloudfront:beta']);
grunt.registerTask('deploy:stable', ['s3:stable', 'cloudfront:stable']);

Traduzindo: Ao digitar grunt deploy:beta, o grunt executa a task beta aninhada na task s3 e em seguida a task beta, aninhada na task cloudfront. O mesmo vale para a task grunt deploy:stable.

Criei um alias para deploy em beta e também tasks apenas para invalidar o cache, caso eu faça upload do arquivo manualmente em algum momento:

1
2
3
4
grunt.registerTask('deploy', ['deploy:beta']);

grunt.registerTask('invalidate-cache:beta', ['cloudfront:beta']);
grunt.registerTask('invalidate-cache:stable', ['cloudfront:stable']);

E aí?

Foi assim que facilitei ainda mais o processo de deploy de aplicações Javascript aqui na RD.

Após essa empreitada, entendi a importância de pesquisar bem o plugin que vou utilizar. O mesmo vale para ruby gems, frameworks e afins. Se eu tivesse pesquisado melhor no ano passado, teria encontrado o plugin grunt-aws e automatizado a invalidação de cache desde então.

Você já fez alguma automatização parecida? Ficou com alguma dúvida? Deixe um comentário! ;)

Luciano Marcelino

Luciano Marcelino

Tech Leader

Comentários