Entrega e integração contínua de aplicações Vue

CI é um acrônimo para Continuous Integration, que surgiu para resolver problemas bem específicos de versionamento de código. Quanto mais espaçadas as integrações, mais conflitos no merge e mais riscos vão existir. A ideia é que ocorram integração de forma contínua, ou seja, que os diversos ambientes (por exemplo: testes, aceitação do usuário e produção) estejam sempre atualizados com o código mais recente do repositório do projeto. CD é um acrônimo para Continuous Delivery e, combinada com a Integração Contínua(CI), nos permite configurar uma série de etapas (pipeline) para que possamos entregar nosso software com rapidez e agilidade (afinal, o próprio manifesto ágil diz: "Entregar frequentemente software funcionando", em tradução livre).
Vamos então entender algumas estratégias e boas práticas para criar um ecossistema saudável para as nossas aplicações Vue, garantindo entregas rápidas, com feedback constante num ambiente colaborativo.
É muito importante que alguns padrões sejam definidos, para o time ou para o projeto, como por exemplo:
- estratégia de ramificação
- automação de testes
- automação de builds
- configuração dos ambientes (stages)

E, depois que tudo esteja bem alinhado entre os times (devops é sobre colaboração!), podemos configurar a pipeline em si.
Nesse exemplo, nós vamos utilizar o Github Pages. Mas vale ressaltar que existem ferramentas de todo o tipo que cobrem a grande maioria dos cenários (Circle CI, Gitlab, Azure Pipelines, etc). O mais importante aqui é entender os conceitos.
Utilizando uma aplicação recém criada, precisamos customizar o caminho para a pasta pública. Isso se faz necessário para que a aplicação funcione corretamente no Github Pages e é muito simples de ser feita:
Criamos um arquivo chamado vue.config.js na raiz do projeto, com o seguinte conteúdo:
// vue.config.js
module.exports = {
publicPath: '/vue-ci-cd-exemplo/'
}
Assim, quando gerarmos os builds, a importação dos scripts será algo parecido com:

Com o projeto pronto e configurado, vamos criar um arquivo chamado deploy.yml dentro de .github/workflows, desse jeito:

Confira o arquivo na íntegra nesse gist
Passo a passo, vamos entender o que essas configurações estão fazendo. Primeiro, definimos o nome da ação (Github Action) que será executada.
name: Executar publicação para o GithubPages
Depois, configuramos o evento que vai servir de gatilho para a ação. No nosso caso, quando fizemos PUSH para a branch develop. Um outro exemplo de gatilho é o pull_request
on:
push:
branches:
- develop
A seguir, configuramos as tarefas(jobs) do deploy. Um dos passos, por exemplo, pode ser a execução dos testes de unidade.
jobs:
deploy:
name: Publicando a aplicação
Também informamos qual container será utilizado:
runs-on: ubuntu-latest
E então vamos para os passos da publicação. Nos dois primeiros passos estamos utilizando Github Actions do marketplace, para facilitar o nosso fluxo de deploy saiba mais aqu.
- name: Checkout
uses: actions/checkout@master
- name: Criar ambiente do node
uses: actions/setup-node@v1
with:
node-version: 10.x
Com o ambiente preparado, seguimos com a instalação das dependências e a geração dos arquivos compilados da nossa aplicação:
- name: Instalar dependências e executar o script de build
run: |
npm install i
npm run build
E, por fim, configuramos uma ação (também do marketplace) para enviar os arquivos de build para nossa branch master:
- name: Publicando o build gerado
uses: s0/git-publish-subdir-action@develop
env:
REPO: self
BRANCH: master
FOLDER: dist
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Agora, ao fazermos git push para a branch de develop, podemos acompanhar a nossa recém criada pipeline:

No fim, um último ajuste para configurar a Github Page:

Assim que salvarmos, apontando para a raiz da nossa branch master (configuramos isso no nosso último passo do build), teremos acesso ao link e finalmente temos a aplicação rodando:

O endereço da página é https://[seu-nome-de-usuario].github.io/[nome-do-projeto]/
Agora, imagine todas as técnicas combinadas:
Integração contínua, testes automatizados (de unidade, de integração, etc…) e deploy contínuo? Podemos fazer pequenos deploys a qualquer momento, configurando, por exemplo, que caso algum dos testes falhe, a publicação será interrompida.
Agora, podemos ter todo o ciclo funcionando.
Gostou do conteúdo e quer saber mais? Então eu te recomendo esses dois cursos com o instrutor Nico Steppat de Integração Contínua e Entrega Contínua que explora maravilhosamente bem esses conceitos. E, falando específicamente de Vue, temos o curso Vue.js: Testes unitários automatizados com Jest e o artigo Desmistificando testes de unidade no Vue sobre testes de unidade.
Vida longa e próspera!