Configure a integração contínua


Configurar a Integração Contínua (CI) do seu track é muito importante, pois ajuda a detectar erros.

GitHub Actions

Os repositórios do Exercism (incluindo os repositórios de tracks) usam o GitHub Actions para rodar sua CI. O GitHub Actions se baseia em fluxos de trabalho, que definem scripts para rodar automaticamente sempre que um evento específico acontece (por exemplo, fazer push de um commit). Para saber mais sobre os fluxos de trabalho do GitHub Actions, consulte a documentação de fluxos de trabalho.

Fluxos de trabalho pré-instalados

Os tracks já vêm com vários fluxos de trabalho pré-instalados, e na maioria deles você não deve mexer (são os chamados fluxos de trabalho compartilhados). Há um fluxo de trabalho que você deve alterar, no entanto: o fluxo de trabalho test.yml.

Fluxo de trabalho de testes

O objetivo do fluxo de trabalho test.yml é verificar se os exercícios do track estão em ordem. O fluxo de trabalho está configurado para rodar automaticamente (na terminologia do GitHub Actions: é disparado) quando se faz um push para o branch main ou para o branch de um pull request.

O fluxo de trabalho em si não deve fazer muita coisa, além de:

  • Fazer o checkout do código (já implementado)
  • Instalar as dependências (por exemplo, instalar pacotes; opcional)
  • Instalar as ferramentas (por exemplo, instalar um SDK; opcional)
  • Rodar o script de verificação dos exercícios (já implementado)

Implemente o script de verificação dos exercícios

Como mencionado, os exercícios são verificados por meio de um script, o script bin/verify-exercises (bash). Esse script está quase pronto e faz o seguinte:

  • Itera sobre todos os diretórios de exercícios
  • Para cada diretório de exercícios, ele então:
    • Copia a solução de exemplo/exemplar para os arquivos de solução (stub) (já implementado)
    • Chama a função unskip_tests, na qual você pode reativar os testes que estavam pulados nos seus arquivos de teste (opcional)
    • Chama a função run_tests, na qual você deve rodar os testes (obrigatório)

As funções run_tests e unskip_tests são as únicas coisas que você precisa implementar.

Reativar os testes

Se o seu track permite pular testes, precisamos garantir que nenhum teste seja pulado ao verificar a solução de exemplo/exemplar de um exercício. Em geral, há duas formas pelas quais os tracks permitem "reativar" os testes:

  1. Remover anotações/código/texto dos arquivos de teste. Por exemplo, mudar test.skip para test.
  2. Fornecer uma variável de ambiente. Por exemplo, definir SKIP_TESTS=false.

Remover anotações/código/texto dos arquivos de teste

Se pular testes for baseado em arquivo (a primeira opção mencionada acima), edite a função unskip_tests para modificar os arquivos de teste (o código existente já cuida de iterar sobre os arquivos de teste).

Note

A função unskip_test roda sobre uma cópia do diretório de um exercício, então fique à vontade para modificar os arquivos como achar melhor.

Exemplo

O bin/verify-exercises file do track Arturo usa sed para reativar os testes dentro dos arquivos de teste:

unskip_tests() {
    jq -r '.files.test[]' .meta/config.json | while read -r test_file; do
        sed -i 's/test.skip/test/g' "${test_file}"
    done
}

Fornecer uma variável de ambiente

Caution

Se reativar os testes exigir a definição de uma variável de ambiente, certifique-se de que ela esteja definida na função run_tests.

Rodar os testes

A função run_tests é responsável por rodar os testes de um exercício. Quando a função é chamada, os arquivos de exemplo/exemplar já terão sido copiados para os arquivos de solução (stub), então você só precisa chamar o comando certo para rodar os testes.

A função deve retornar zero como código de saída se todos os testes passarem; caso contrário, deve retornar um código de saída diferente de zero.

Note

A função run_tests roda sobre uma cópia do diretório de um exercício, então fique à vontade para modificar os arquivos como achar melhor.

Opção 1: usar as ferramentas da linguagem

A opção padrão para o script de verificação dos exercícios é usar as ferramentas da linguagem (SDK/binário/etc.), que é o que a maioria dos tracks usa. Cada track terá sua própria forma de rodar os testes, mas normalmente é só um único comando.

Exemplo

O bin/verify-exercises file do track Arturo modifica a função run_tests para simplesmente chamar o comando arturo no arquivo de teste:

run_tests() {
    arturo tester.art
}

Opção 2: usar a imagem Docker do test runner

A segunda opção é verificar os exercícios rodando o test runner do track. Isso, é claro, depende de o track ter um test runner que funcione.

Se o seu track ainda não tem um test runner, você pode:

  • criar um test runner que funcione, ou
  • usar a opção 1 e usar as ferramentas da linguagem diretamente

As seguintes modificações precisam ser feitas no script bin/verify-exercises padrão:

  1. Verificar se o comando docker está disponível
  2. Fazer o pull (baixar) da imagem Docker do test runner
  3. Usar docker run para rodar a imagem Docker do test runner em cada exercício
  4. Usar jq para verificar se o arquivo results.json retornado pelo contêiner Docker indica que todos os testes passaram
  5. Remover a função unskip_test e a chamada a essa função
Note

O principal benefício dessa abordagem é que ela imita melhor como os testes rodam em produção (no site). Com essa abordagem, é menos provável que coisas que passaram na CI falhem em produção. A desvantagem dessa abordagem é que ela costuma ser mais lenta, por causa do download da imagem Docker e da sobrecarga do Docker.

Exemplo

O bin/verify-exercises file do track Unison adiciona a verificação de que o comando docker também está instalado:

required_tool docker

Em seguida, ele baixa a imagem do test runner do track:

docker pull exercism/unison-test-runner

Depois, ele modifica a função run_tests para usar docker run e rodar o test runner no exercício atual (que está no diretório de trabalho), seguido de um comando jq para checar se o status está correto:

run_tests() {
    local slug

    slug="${1}"

    docker run \
        --rm \
        --network none \
        --mount type=bind,src="${PWD}",dst=/solution \
        --mount type=bind,src="${PWD}",dst=/output \
        --tmpfs /tmp:rw \
        exercism/unison-test-runner "${slug}" "/solution" "/output"
    jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1
}

Por fim, precisamos modificar a chamada do comando run_tests, já que agora ele exige o slug:

run_tests "${slug}"

Implemente o fluxo de trabalho de testes

Agora que o script verify-exercises está pronto, é hora de finalizar o fluxo de trabalho test.yml. Como fazer isso depende da opção escolhida para a implementação do script verify-exercises.

Opção 1: usar as ferramentas da linguagem

Se o script verify-exercises usa diretamente as ferramentas da linguagem, o fluxo de trabalho de testes precisará instalar:

  • As dependências das ferramentas da linguagem, como openssh ou um compilador de C/C++.
  • As ferramentas da linguagem, como um SDK ou um binário. Se a instalação das ferramentas da linguagem não adicionar o(s) binário(s) instalado(s) ao path, certifique-se de adicioná-lo ao system path do GitHub Actions.

Depois disso, o verify-exercises deve funcionar como esperado, e pronto: você configurou a CI com sucesso!

Para ver um exemplo, confira o fluxo de trabalho test.yml do track Arturo:

name: Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  ci:
    runs-on: ubuntu-22.04

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev libmpfr-dev

      - name: Install Arturo
        run: bin/install-arturo
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Verify all exercises
        run: bin/verify-exercises

Opção 2: usar a imagem Docker do test runner

A segunda opção é verificar os exercícios rodando o test runner do track. Essa opção exige que duas coisas sejam verdadeiras:

  1. O track tem um test runner que funciona
  2. O script verify-exercises usa a imagem Docker do test runner para rodar os testes de um exercício

Se o seu track ainda não tem um test runner, você pode:

  • criar um test runner que funcione, ou
  • usar a opção 1 e usar as ferramentas da linguagem diretamente

Essa abordagem tem algumas vantagens:

  1. Você não precisa instalar nenhuma dependência/ferramenta dentro do fluxo de trabalho de testes (elas já terão sido instaladas dentro da imagem Docker)
  2. A abordagem imita melhor como os testes rodam em produção (no site), o que reduz a chance de problemas em produção.

A principal desvantagem é que ela provavelmente é mais lenta, por causa do download da imagem Docker e da sobrecarga do Docker.

Há algumas formas de baixar a imagem Docker do test runner:

  1. Baixar a imagem dentro do arquivo verify-exercises. É a abordagem usada pelo track Unison.
  2. Baixar a imagem dentro do fluxo de trabalho. É a abordagem usada pelo track Standard ML.
  3. Construir a imagem dentro do fluxo de trabalho. É a abordagem usada pelo track 8th.

Então, qual abordagem usar? Recomendamos implementar pelo menos a opção número 1, para que o script verify-exercises seja autônomo. Se a sua imagem for especialmente grande, pode valer a pena implementar também a opção 3, que guarda a imagem Docker construída no cache do GitHub Actions. As execuções seguintes podem então apenas ler a imagem Docker do cache, em vez de baixá-la, o que pode ser melhor para o desempenho (meça para ter certeza).

Opção 3: rodar o script de verificação dos exercícios dentro da imagem Docker do test runner

Uma terceira opção, alternativa, é um híbrido das duas opções anteriores. Aqui também usamos a imagem Docker do test runner, só que desta vez rodamos o script verify-exercises dentro dessa imagem Docker. Para habilitar essa opção, precisamos definir o contêiner do fluxo de trabalho como o test runner:

container:
  image: exercism/vimscript-test-runner

Podemos então pular as etapas de instalação de dependências e ferramentas (elas já terão sido instaladas dentro da imagem Docker do test runner) e seguir rodando o script bin/verify-exercises.

Exemplo

O fluxo de trabalho test.yml do track vimscript usa essa opção:

name: Verify Exercises

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

jobs:
  ci:
    runs-on: ubuntu-24.04
    container:
      image: exercism/vimscript-test-runner

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Verify all exercises
        run: bin/verify-exercises