DEV Community

Cover image for GitHub Actions - Elixir
Vitor Leal for Ingresse

Posted on

GitHub Actions - Elixir

O Actions é uma ferramenta integrada aos seus repositórios do GitHub para automatizar os seus workflows de CI/CD (continuous delivery / continuous deploy). Você consegue rodar esses workflows baseado em diversos eventos do GitHub, como um novo push em uma branch a criação de uma nova issue ou uma nova release publicada.

Contexto

Desde que o GitHub lançou a versão beta do Actions, a Ingresse vem a testando. No final do ano passado o Actions saiu da versão beta e a gente decidiu finalmente migrar nossos workflows para lá.

Já que Elixir é a nossa linguagem principal na Ingresse, começamos a migrar primeiro os workflows dos serviços em Elixir para o Actions e aqui tem algumas dicas de como a gente faz.

Básico


Antes de tudo vou apresentar um pouco do básico do GitHub Actions, se você já tem familiaridade pode seguir passar direto essa seção.


O Actions vai ler os arquivos .yml ou .yaml dentro da pasta .github/workflows na raiz do seu projeto.

Primeiro vamos criar a pasta .github com a pasta workflows dentro:

mkdir -p .github/workflows
Enter fullscreen mode Exit fullscreen mode

Agora vamos criar um arquivo que a gente pode chamar de ci.yml:

touch .github/workflows/ci.yml
Enter fullscreen mode Exit fullscreen mode

Agora vamos adicionar o seguinte conteúdo nele:

name: Meu Workflow

on: push

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v1
Enter fullscreen mode Exit fullscreen mode

Vamos aos detalhes do conteúdo desse arquivo.

name

O nome do seu workflow. É um campo opcional.


on

O nome do evento do GitHub que vai iniciar esse workflow.

Pode ser um evento apenas:

# Vai rodar sempre que alguém realizar um push no repositório
on: push
Enter fullscreen mode Exit fullscreen mode

Pode ser uma lista de eventos:

# Vai rodar sempre que alguém realizar um push no repositório
# e quando for criado um novo pull request
on: [push, pull_request]
Enter fullscreen mode Exit fullscreen mode

Pode ser customizado por evento:

on:
  # Vai rodar sempre que um pull request for criado
  pull_request:
    # Mas quando estiver apontando para a master
    branches:
      - master

  # Vai rodar sempre que uma release for criada
  release:
    # Mas se ela estiver com o status de publicada
    types: 
      - published
Enter fullscreen mode Exit fullscreen mode

jobs

Um workflow pode ter um ou mais jobs. Por padrão, múltiplos jobs rodam em paralelo.


jobs.<id-do-job>.run-on

Em que tipo de máquina o job vai ser executado.

Pode ser em um tipo de máquina:

jobs:
  test:
    runs-on: ubuntu-latest
Enter fullscreen mode Exit fullscreen mode

Podem ser em múltiplos tipos de máquinas, incluindo ubuntu, macos e windows:

jobs:
  test:
    strategy:
      matrix:
        platform: [ubuntu-latest, macos-latest, windows-latest]

    runs-on: ${{ matrix.platform }}
Enter fullscreen mode Exit fullscreen mode

jobs.<id-do-job>.steps

Um job pode ter uma ou mais tarefas que vão ser executadas. Uma tarefa pode executar comandos, rodar setups ou rodar outras actions. O próprio GitHub tem uma série de actions publicadas que facilitam muitos trabalhos. Nesse exemplo estamos usando a action actions/checkout na versão v1 para clonar o repositório do projeto.

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      # Primeira tarefa a ser executada vai ser clonar o repositório
      - uses: actions/checkout@v1
Enter fullscreen mode Exit fullscreen mode

Próximos passos

Bom agora que a gente já sabe um pouco mais sobe a estrutura básica de um workflow do GitHub Actions vamos começar a moldar ele para executar por exemplo uma tarefa de mix format e uma tarefa de mix test em um projeto Elixir.

Para evitar ter que instalar o Elixir no processo de build, vamos utilizar o atributo container para baixar uma imagem Docker já existente.

name: Meu Workflow

on: push

jobs:
  test:
    runs-on: ubuntu-latest

    container:
      image: elixir:1.10

    steps:
      - uses: actions/checkout@v1
Enter fullscreen mode Exit fullscreen mode

jobs.<id-do-job>.container

Em qual imagem Docker vai rodar as tarefas desse job.

jobs:
  test:
    container:
      image: elixir:1.10
Enter fullscreen mode Exit fullscreen mode

Agora que nosso ambiente já tem Elixir, podemos criar as tarefas para executar o mix format e mix test.

name: Meu Workflow

on: push

jobs:
  test:
    runs-on: ubuntu-latest

    container:
      image: elixir:1.10

    steps:
      - uses: actions/checkout@v1

      - name: Formatar
        run: mix format --check-formatted

      - name: Instalar as dependências
        run: mix deps.get

      - name: Testar
        run: mix test
Enter fullscreen mode Exit fullscreen mode

Pronto já temos um workflow que clona nosso repositório, verifica se nosso código está formatado de acordo com as configurações do projeto, instala as dependências e roda nossos testes.


Utilizando serviços

O cenário anterior funciona perfeitamente para quando a gente tem um lib escrita em Elixir, mas para casos que a gente esteja trabalhando em um projeto com Phoenix por exemplo, a gente vai precisar provavelmente conectar em um banco de dados para poder executar alguns testes.

Nesse momento a gente consegue utilizar os services para criar containers de um banco de dados Postgres, por exemplo, para que os nossos jobs possam ter onde se conectar.

jobs.<id-do-job>.services

services:
  postgres:
    image: postgres

    # Mapeia a porta 5432 no container Docker para a porta 5432 no container do Postgres
    ports:
      - 5432:5432

    # Configura os dados de acesso ao banco de teste
    env:
      POSTGRES_USER: usuario
      POSTGRES_PASSWORD: senha
      POSTGRES_DB: banco
Enter fullscreen mode Exit fullscreen mode

Agora que o nosso workflow vai criar o banco de dados pra gente poder conectar, tem um detalhe que temos que alterar nas nossas tarefas que vão rodar para conectar nesse banco. No caso, a nossa tarefa mix test tem que receber uma variável de ambiente para poder conectar corretamente no banco criado pelo Actions.

- name: Testar
  run: mix test
   env:
     POSTGRES_HOST: postgres
     POSTGRES_PORT: $❴❴ job.services.postgres.ports[5672] ❵❵
Enter fullscreen mode Exit fullscreen mode

O exemplo completo fica assim:

name: Meu Workflow

on: push

jobs:
  test:
    runs-on: ubuntu-latest

    container:
      image: elixir:1.10

    services:
      postgres:
        image: postgres

        ports:
          - 5432:5432

        env:
          POSTGRES_USER: usuario
          POSTGRES_PASSWORD: senha
          POSTGRES_DB: banco

    steps:
      - uses: actions/checkout@v1

      - name: Formatar
        run: mix format --check-formatted

      - name: Instalar as dependências
        run: mix deps.get

      - name: Testar
        run: mix test
        env:
          POSTGRES_HOST: postgres
          POSTGRES_PORT: $❴❴ job.services.postgres.ports[5432] ❵❵
Enter fullscreen mode Exit fullscreen mode

Agora basta alterar as configurações da nosso ambiente de teste para receber utilizar as variáveis de ambiente:

import Config

config :meu_app, Meu.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "usuario",
  password: "senha",
  database: "banco",
  port: String.to_integer(System.get_env("POSTGRES_PORT", "5432"))
  hostname: System.get_env("POSTGRES_HOST"),
  pool: Ecto.Adapters.SQL.Sandbox
Enter fullscreen mode Exit fullscreen mode

Docker Compose

Se você já utiliza Docker Compose nos seus projetos você não precisa criar os services no arquivo do Actions e pode criar esses serviços direto no seu arquivo do Docker Compose de teste.

Exemplo do arquivo docker-compose-test.yml:

version: "3.3"
services:
  meu_servico:
    build: .
    depends_on:
      - meu_postgres
    ports:
      - "4000:4000"
    environment:
      - MIX_ENV=test
    command: mix phx.server

  postgres:
    image: postgres
    container_name: meu_postgres
    environment:
      - POSTGRES_DB=banco
      - POSTGRES_USER=usuario
      - POSTGRES_PASSWORD=senha
    ports:
      - "5433:5432"
Enter fullscreen mode Exit fullscreen mode

Para rodar os testes com o Docker Compose, o ideal é mudar a imagem que roda no Actions para uma imagem que tenha já tenha o compose instalado.

jobs:
    container:
      image: docker/compose
Enter fullscreen mode Exit fullscreen mode

O exemplo completo fica assim:

name: Meu Workflow

on: push

jobs:
  test:
    runs-on: ubuntu-latest

    container:
      image: docker/compose

    steps:
      - uses: actions/checkout@v1

      - name: Formatar
        run: docker-compose -f docker-compose-test.yml run meu_servico mix format --check-formatted

      - name: Testar
        run: docker-compose -f docker-compose-test.yml run meu_servico mix test
Enter fullscreen mode Exit fullscreen mode

Espero que tenha ajudado vocês a entender um pouco melhor como rodar projetos Elixir no GitHub Actions.

Até a próxima!

Top comments (0)