GitHub Actions vs GitLab CI. Quais são as principais diferenças?
Há alguns anos, ter um projeto de software com uma esteira de integração contínua (CI) era uma tarefa trabalhosa. Sua equipe deveria implantar e dar manutenção para um sistema de CI interno, provavelmente uma instância de Jenkins.
Nos últimos anos, no entanto, surgiram diversas soluções de SaaS para CI, com um custo de adoção bem menor: basta adicionar um arquivo de configuração em um repositório git e sua esteira está pronta para rodar.
Mais conveniente ainda, hoje alguns sistemas de hospedagem de código fonte contam com um sistema de CI embutido. Dessa forma o código fonte de seu projeto fica bem próximo de suas esteiras.
Neste post você vai ler uma análise comparativa entre duas soluções bem populares que se enquadram nessa última categoria: o GitLab CI e o GitHub Actions.
Vai começar um projeto e está na dúvida em qual escolher? Já usa uma das soluções e se pergunta se a outra opção seria melhor? Este artigo é para você.
Maturidade
Para começar, é importante destacar o tempo que essas soluções ficaram disponíveis para usarmos, ou seja, atingiram General Availability (GA).
O GitLab CI do jeito que conhecemos hoje, integrado na plataforma de hospedagem de código, está disponível desde 2015. O GitHub Actions atingiu disponibilidade geral 4 anos depois, em 2019.
Isso tem algumas implicações: com mais tempo de mercado, o GitLab CI possui mais usuários, mais conteúdo disponível (tutoriais, guias e exemplos) e também mais funcionalidades. Por outro lado, por ser mais recente, o GitHub Actions teve que trazer novas ideias para sistemas de CI.
Essa é a primeira grande diferença entre os dois: a maturidade. Como o GitHub Actions surgiu há menos de 2 anos, dependendo da complexidade da esteira que você precisar, vai esbarrar em algumas limitações da solução. Não é muito raro ter que recorrer ao Fórum de Suporte.
Note que a maturidade do GitLab CI aqui não é sinônimo de tecnologia datada. A cada versão nova a plataforma fica melhor, tem uma boa cadência de atualizações e está em evolução constante. É um sistema de CI moderno, completo e maduro.
Popularidade
De acordo com a StackShare, um serviço que permite empresas compartilharem as tecnologias que usam, o GitLab CI e o GitHub Actions são respectivamente a 4a e a 7a plataformas mais usadas. Em primeiro e segundo lugar estão o Jenkins e o Travis CI.
"Hosted internally" is the primary reason developers pick Jenkins over its competitors, while "Github integration" is the reason why Travis CI was chosen.
Vale lembrar que o Jenkins e Travis CI existem desde 2011, e, considerando o tempo em que o Jenkins era conhecido como Hudson, pode-se dizer que o Jenkins é usado desde 2005.
Segundo a pesquisa The State of Developer Ecosystem 2021 da JetBrains, feita com 31.743 pessoas desenvolvedoras de 183 países, o GitHub Actions é o segundo sistema de CI mais usado, enquanto o GitLab CI é o terceiro. O Jenkins aparece novamente em primeiro lugar.
While Jenkins is the most popular CI system for company usage, GitHub Actions is the most frequent choice for personal use.
Apesar de novo, o GitHub Actions já é uma preferência entre devs. Isso deve impactar o uso de sistemas de CI em organizações nos próximos anos.
Semelhanças
Apesar de produtos diferentes, ambos os sistemas atendem o mesmo objetivo: construir e executar esteiras de integração contínua. Portanto esses sistemas compartilham diversas capacidades, para citar algumas:
Jobs
Uma esteira de CI no GitLab CI ou GitHub Actions é composta por uma coleção de jobs. Cada job define um conjunto de passos que são executados em sequência.
Os jobs podem ser organizados para executarem sequencialmente ou em paralelo, podem ter dependências declaradas para que executem em determinada ordem e podem ser executados em máquinas distintas.
No exemplo abaixo é definido um job que imprime "Hello World" no console:
GitLab CI/CD | GitHub Actions |
---|---|
|
|
As chaves usadas no YAML mudam um pouco, mas até agora nada de muito diferente.
Runners
Em ambas as plataformas os jobs são executados por Runners. Num sistema de CI, um Runner é uma aplicação que roda em uma máquina separada. Essa aplicação está conectada ao sitema de CI e fica aguardando jobs para iniciar a sua execução.
A execução da esteira de CI depende da disponibilidade de um runner para iniciá-la. A quantidade de recursos que pode ser usada por processos definidos na sua esteira também estão diretamente ligadas à capacidade desses runners. Se a execução dos testes da sua aplicação exigirem 4GB de memória, por exemplo, não é possível rodar esse job num runner que está numa máquina com 512MB de memória.
Esse aspecto da arquitetura de um sistema de CI é mais relevante quando você é responsável por gerenciar os Runners. A alternativa é usar os runners gerenciados pela plataforma, que lida com escalabilidade para atender as demandas de seu projeto e o provisionamento de um ambiente limpo pra a sua esteira de CI.
As duas plataformas oferecem runners gerenciados e a opção de você gerenciar seus próprios runners. Independente de qual opção você escolher, é importante ter em mente que a esteira de CI será executada num ambiente com um limite bem determinado de recursos.
Para determinar o runner que executa um job, você usa a chave tags
no
GitLab CI e runs-on
no GitHub Actions:
GitLab CI/CD | GitHub Actions |
---|---|
|
|
O software usado pelos runners são Open Source:
O consumo de recursos desses runners é desprezível, o dimensionamento das máquinas usadas para sustentar os runners deve ser feito pensando nos jobs que vão executar na máquina.
Para os runners gerenciados a cobrança é feita por minuto de execução dos jobs. Note que o minuto de execução é contabilizado por processamento, não pelo tempo total (wall time) da sua esteira de CI. Uma esteira que roda 5 jobs de 2 minutos em paralelo termina em 2 minutos, mas será cobrada em 10 minutos, por exemplo.
No plano gratuito, para projetos privados o GitLab CI oferece 400 minutos por mês e o GitHub Actions oferece 2000 minutos por mês. Para projetos públicos não há limite de uso no GitHub, enquanto no GitLab há uma cota de 50.000 minutos.
Tanto o GitLab CI quanto o GitHub Actions oferecem runners gerenciados Linux, Windows ou MacOS, no entanto os runners Windows e MacOS ainda estão em beta no GitLab CI.
As máquinas Linux do GitLab CI tem 1 vCPU, 3,75GB de RAM e 25GB de HDD, as máquinas com MacOS tem 4 vCPUs e 10GB de RAM, e as máquinas Windows tem 2 vCPU e 7,5GB de RAM.
As máquinas Linux e Windows do GitHub Actions contam com 2 vCPU, 7GB de RAM e 14GB de SSD, enquanto as máquinas com MacOS tem 3 vCPU, 14GB de RAM e 14GB de SSD.
Docker
As duas opções suportam executar jobs dentro de um container Docker.
Para determinar a imagem, você usa a chave image
no GitLab CI e container
no GitHub Actions:
GitLab CI/CD | GitHub Actions |
---|---|
|
|
Variáveis de Ambiente e Secrets
Os dois sistemas permitem definir variáveis de ambiente disponíveis para uso na esteira. Algumas variáveis de ambiente já são pré-definidas, são informações específicas do versionamento do projeto e ambiente como o sha do commit, nome da branch, nome da esteira, nome do repositório, etc. Há uma lista completa na documentação:
Variáveis de Ambiente e Secrets no GitHub Actions
Na interface do GitHub é possível definir mais variáveis para usar na esteira.
No repositório, acesse o menu Settings » Secrets. Os valores definidos
nessa tela, no entanto, não são acessados da mesma forma que uma variável de
ambiente. Para usar um secret chamado SECRET_KEY
na esteira:
jobs:
job_with_secret:
steps:
- run: SECRET_CONF="${{ secrets.SECRET_KEY }}" example-cli
No exemplo acima a variável de ambiente SECRET_CONF
recebe o valor do secret
SECRET_KEY
definido pela interface nas configurações do repositório, que fica
disponível para uso pelo comando example-cli
. É possível passar variáveis de
ambiente através da chave env
em diversos escopos: para a esteira inteira,
para um job ou para um passo do job. O exemplo acima poderia ser reescrito
assim:
jobs:
job_with_secret:
steps:
- run: example-cli
env:
SECRET_CONF: ${{ secrets.SECRET_KEY }}
Variáveis de Ambiente e Secrets no GitLab CI
Também é possível definir varáveis adicionais através da tela do GitLab CI e no arquivo de configuração da esteira. Porém a distinção entre o que é secret e o que não é fica mais sutil. Tudo é chamado de variável e todos os valores estão disponíveis por padrão como variável de ambiente.
Para definir variáveis extras pela interface, na página do repositório acesse o
menu Settings » CI/CD » Variables. Para usar na esteira uma variável
chamada SECRET_CONF
definida nessa tela:
job_with_secret:
script:
- example-cli
Para entender o exemplo acima é necessário saber que o comando example-cli
usa a variável de ambiente SECRET_CONF
. Por outro lado, não é necessário
escrever nenhuma configuração a mais para isso.
Esse comportamento também varia de acordo com o ambiente. Se o job rodar em um container é necesário redefinir as variáveis, porque variáveis definidas pela interface não ficam disponíveis por padrão. Exemplo:
job_with_secret:
image: debian:bullseye
variables:
SECRET_CONF: $SECRET_CONF
script:
- example-cli
É possível definir mais variáveis de ambiente através da chave variables
para
a esteira inteira ou para um job:
variables:
TEST_FAIL_FAST: 1
test_job:
variables:
TEST_SKIP_SLOW: 1
script:
- test-cmd
No exemplo acima tanto a variável de ambiente TEST_FAIL_FAST
quanto a
TEST_SKIP_SLOW
ficam disponíveis para o comando test-cmd
.
Para evitar que o valor de uma variável apareça acidentalmente nos logs do job na interface do GitLab CI ou nos logs do runner é necessário marcar a variável como Masked. Esse comportamento vem habilitado por padrão no GitHub.
Condicionais
Uma boa esteira de CI deve ser rápida. Uma maneira de se fazer isso é evitar rodar jobs que não são necessários. As duas opções analisadas aqui oferecem formas de executar um job apenas se algumas condições forem satisfeitas.
O uso adequado de condicionais é a diferença entre uma esteira de CI que ajuda o time a entregar com velocidade e qualidade; e um pedágio caro que deve ser pago até para subir alterações pequenas.
Condicionais no GitLab CI
A chave rules
pode ser usada na definição de jobs para definir condições
para execução. Uma chave if
pode ser usada para definir condições usando os
valores das variáveis de ambiente. Exemplo:
job_with_condition:
script: echo "I'll only run for MRs to main"
rules:
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH
Esse job roda apenas se um Merge Request tiver como alvo a branch padrão do
repositório. Qualquer variável pode ser usada para compor essas condições, há
suporte para &&
(and) e ||
(or), comparação com regex usando =~
e
!~
, bem como comparações com ==
ou !=
.
A chave changes
pode ser usada para definir um job que executa apenas quando
há alterações em um ou alguns arquivos específicos:
ruby_tests:
image: ruby:3.0.2-slim-bullseye
script:
- bundle exec rake spec
rules:
changes:
- "**/*.rb"
No exemplo acima os testes de Ruby rodam apenas quando houver alguma alteração
em arquivos com extensão .rb
. Uma atualização de arquivos de documentação,
alguns arquivos .md
por exemplo, não acionaria esse job.
Usando if
e changes
dentro das rules
de um job é possível ter um
controle bem preciso dos jobs executados em uma esteira.
Condicionais no GitHub Actions
O GitHub Actions também suporta condições com if
, mas a curva de aprendizado
é um pouco maior. Ao invés de variáveis de ambiente e operações, são usados
objetos e funções. As expressões lembram menos Shell script
e mais
JavaScript
. É necessário consultar a documentação para ver as funções e o
formato (ou schema) dos objetos disponíveis para compor suas condições.
Enquanto no GitLab CI uma visita à página com a tabela de variáveis disponíveis é o suficiente para começar a escrever, no GitHub Actions você pode perder um certo tempo navegando entre páginas de documentação para descobrir qual propriedade de qual objeto você precisa para escrever uma condição. Exemplo:
on: pull_request
jobs:
job_with_condition:
if: ${{ github.event.pull_request.base.ref == github.event.repository.default_branch }}
runs-on: ubuntu-latest
steps:
- run: echo "I'll only run for PRs to main"
Esse job roda apenas se um Pull Request tiver como alvo a branch padrão do repositório. Note o acesso aos objetos aninhados. Mais um exemplo:
on: pull_request
jobs:
job_with_condition:
if: ${{ startsWith(github.event.pull_request.head.ref, 'feat') }}
runs-on: ubuntu-latest
steps:
- run: echo "I'll only run for feature branches"
No exemplo acima foi usado a função startsWith
para executar o job apenas
para pull requests de branches que começam com "feat".
A condição de arquivos alterados está atrelado ao evento de push
ou
pull_request
e pode ser determinada apenas na raiz da esteira. Ou seja,
não há recursos nativos na plataforma para pular um job ou um passo de acordo
com os arquivos alterados, apenas a esteira inteira. Exemplo:
on:
pull_request:
paths:
- '**.rb'
jobs:
job1:
# (...)
job2:
# (...)
job3:
# (...)
No exemplo acima todos os jobs definidos são ignorados se não houver alterações
em algum arquivo de extensão .rb
.
Há basicamente 3 formas de lidar com essa limitação:
- Organize sua esteira em múltiplos arquivos de workflow com escopo pequeno
- Use um pouco de Shell script e git para construir a condição manualmente
- Use uma solução construída pela comunidade
Agendamento de execução
Além dos casos mais comuns em que uma esteira de CI roda quando um novo commit é empurrado para o repositório ou um Merge Request é aberto, também é possível agendar execuções periódicas. Isso pode ser útil para executar testes mais caros apenas 1 vez por semana ao invés de várias vezes por dia, por exemplo.
A maior diferença aqui é a forma de configurar esses agendamentos: enquanto no GitHub Actions isso é feito nos arquivos de configuração, no GitLab CI as execuções são agendadas através da interface.
Como o controle de acesso é mais rígido na interface do que nos arquivos que entram no projeto, a leitura que deve ser feita aqui é a seguinte: no GitHub Actions mais pessoas do time de desenvolvimento tem poder de criar agendamentos, no GitLab CI há um controle mais fino na gestão de agendamentos.
Ordem de execução dos jobs ou arquitetura da esteira
Os dois serviços permitem especificar qual a ordem que os jobs são executados numa esteira e quais as dependências de cada job. O GitLab CI oferece mais opcões de como fazer isso, enquanto o GitHub oferece apenas uma forma, que também está disponível no GitLab CI.
Então o GitLab CI é melhor aqui, certo? Não necessariamente. Apesar de ter mais opções, inclusive uma similar a do GitHub Actions, essa única forma de declarar dependências é a melhor para maximizar o paralelismo dos jobs executados.
Enquanto no GitLab CI você tem mais opções para construir sua esteira, no GitHub Actions é mais fácil você chegar numa esteira rápida.
Ordem dos jobs no GitHub Actions
Para controlar a ordem de execução dos jobs no GitHub Actions você usa a
chave needs
passando a lista de jobs que são dependências do job.
Exemplo:
jobs:
build_frontend:
runs-on: ubuntu-latest
steps:
- run: echo "Building frontend"
build_backend:
runs-on: ubuntu-latest
steps:
- run: echo "Building backend"
test_frontend:
runs-on: ubuntu-latest
needs: [build_frontend]
steps:
- run: echo "Test the frontend"
test_backend:
runs-on: ubuntu-latest
needs: [build_backend]
steps:
- run: echo "Test the backend"
test_e2e:
runs-on: ubuntu-latest
needs: [build_frontend, build_backend]
steps:
- run: echo "Test the complete app"
deploy:
runs-on: ubuntu-latest
needs: [test_frontend, test_backend, test_e2e]
steps:
- run: echo "Deploy the validated app"
No exemplo acima, há uma aplicação dividida entre frontend e backend, as builds dessas partes podem rodar em paralelo e os testes das partes individuais também (após cada build), mas o teste da aplicação completa depende da build das duas partes; e o deploy depende do sucesso de todos os testes.
Para um exemplo de esteira pequeno como esse, não é muito difícil descobrir quais jobs podem executar em paralelo e quais devem esperar os outros antes de começar, mas esse trabalho não é necessário. A partir dessas declarações de dependências o GitHub Actions constroi um grafo direcionado acíclico que descreve a esteira de CI e executa todos os jobs possíveis assim que possível.
O exemplo acima aparece assim na interface:
Ordem dos jobs no GitLab CI
O GitLab CI oferece 3 formas principais de organizar as esteiras de CI: básica com estágios (stages), com grafos direcionado acíclico (DAG) e esteiras Child/Parent.
O exemplo abaixo replica o exemplo usado no GitHub Actions usando os stages do GitLab CI.
stages:
- build
- test
- deploy
build_frontend:
stage: build
script:
- echo "Building frontend"
build_backend:
stage: build
script:
- echo "Building backend"
test_frontend:
stage: test
script:
- echo "Test the frontend"
test_backend:
stage: test
script:
- echo "Test the backend"
test_e2e:
stage: test
script:
- echo "Test the complete app"
deploy:
stage: deploy
script:
- echo "Deploy the validated app"
O exemplo acima aparece assim na interface:
Um problema dessa abordagem é que todos os jobs de cada estágio devem terminar antes de começar os jobs do próximo. Isso pode ter um grande impacto se o tempo dos jobs de um mesmo estágio forem muito diferentes, um job mais lento de um estágio atrasa toda a esteira.
Para resolver esse problema o GitLab CI oferece também a construção de esteiras
com DAGs, basta adicionar a chave needs
em alguns locais do exemplo acima:
test_frontend:
stage: test
+ needs: [build_frontend]
script:
- echo "Test the frontend"
test_backend:
stage: test
+ needs: [build_backend]
script:
- echo "Test the backend"
test_e2e:
stage: test
+ needs: [build_frontend, build_backend]
script:
- echo "Test the complete app"
deploy:
stage: deploy
+ needs: [test_frontend, test_backend, test_e2e]
script:
- echo "Deploy the validated app"
Com essa pequena alteração os jobs agora executam assim que possível. E na interface é possível ver as dependências entre os jobs:
O GitLab CI também oferece uma visualização alternativa, que é interativa e desenha a sua esteira como uma pipeline mesmo:
Uma terceira opção para arquitetar esteiras de CI no GitLab é usar as esteiras Child/Parent. Esse padrão é útil para repositórios que armazenam diversos componentes num mesmo lugar, mas que poderiam ter esteiras de CI distintas.
A esteira Parent do repositório (definida no arquivo .gitlab-ci.yml
principal do repositório) pode iniciar outras esteiras Child, definidas em outros arquivos, através da chave trigger
. Essa ativação através do trigger
pode usar as regras para execução condicional de jobs. Isso permite organizar esteiras de projetos complexos de maneira bem eficiente. Observe o exemplo abaixo:
stages:
- triggers
trigger_service1:
stage: triggers
trigger:
include: service1/.gitlab-ci.yml
rules:
- changes:
- service1/*
trigger_service2:
stage: triggers
trigger:
include: service2/.gitlab-ci.yml
rules:
- changes:
- service2/*
Neste exemplo, essa esteira Parent (.gitlab-ci.yml
na raiz do repositório) orquestra múltiplas esteiras Child de acordo com as alterações. Cada serviço alterado nesse exemplo tem sua própria esteira, que é ativada apenas quando há alterações em seus arquivos. A esteira do service1
, definida em service1/.gitlab-ci.yml
é executada apenas quando há alguma alteração nos arquivos da pasta service1
.
Serviços Extras
Para rodar os testes de integração automatizados de uma aplicação, pode ser necessário subir um banco de dados, um cache, um cofre de segredos ou algum outro serviço extra. As duas opções analisadas neste post permitem subir serviços para usar nas esteira, usando containers Docker.
Exemplo que sobe um PostgreSQL:
GitLab CI/CD | GitHub Actions |
---|---|
|
|
No exemplo acima o job também é executado dentro de um container PostgreSQL, que faz apenas um teste de conexão.
Esse provisionamento automático de serviços é feito apenas para containers Docker. Portanto, no GitLab CI está disponível apenas para jobs que executam em runners Docker e no GitHub Actions está disponível apenas para runners Linux com Docker instalado. Não é preciso se preocupar com isso para runners gerenciados.
Para usar um serviço extra que não esteja containerizado, é necessário deixar o serviço pré-configurado num runner self-hosted ou configurá-lo durante o tempo de execução da esteira de CI. No entanto, a natureza efêmera e isolamento dos containers Docker é ideal para subir esses serviços adicionais. Além do atalho na configuração, o gerenciamento dos serviços fica muito mais conveniente.
Cache e Artefatos
Tanto o GitHub Actions quanto o GitLab CI suportam armazenar arquivos como cache ou artefatos entre jobs. Configurar a esteira para cachear as dependências do projeto pode acelerar execuções subsequentes. Subir artefatos pode ser útil para analisar arquivos gerados durante a esteira de CI: screenshots de testes de interface, relatório de cobertura de testes ou arquivos de log, por exemplo.
Cache
O cache é usado dentro da esteira de CI, é muito útil para evitar baixar novamente as dependências da aplicação, por exemplo.
O GitHub Actions não suporta cache em runners self-hosted, apenas nos runners gerenciados pelo próprio GitHub. Arquivos que não foram acessados nos últimos 7 dias são removidos e caso o total de arquivos armazenados ultrapasse 5GB, os mais antigos são removidos.
No GitLab CI a documentação não cita por quanto tempo os runners gerenciados mantém o cache ou quanto pode ser armazenado. Nos runners self-hosted depende da configuração feita no runner.
Para cachear as dependências de um projeto que usa yarn:
GitLab CI/CD | GitHub Actions |
---|---|
|
|
Artefatos
Os artefatos podem ser baixados fora da esteira de CI.
No GitHub Actions eles são retidos por 90 dias por padrão, em repositórios
públicos isso pode ser alterado para algo entre 1 e 90 dias, para repositórios
privados pode variar entre 1 e 400 dias. Esse valor é configurado através da
chave retention-days
. No plano gratuito há uma cota de 500MB por projeto.
Essa cota é consumida apenas por artefatos e pacotes armazenados.
No GitLab CI os artefatos são retidos por 30 dias por padrão. Esse valor pode
ser alterado para todos os projetos para instâncias privadas do GitLab. Também
é possível alterar esse valor para cada job através da chave expire_in
. Não
há restrição ao tempo de retenção. No plano gratuito o GitLab oferece uma cota
de 10GB, porém qualquer informação persistida do projeto contabiliza para essa
cota: texto das issues, código fonte, anexos, artefatos, logs, etc.
Para disponibilizar o arquivo coverage/index.html
após a execução da esteira
de CI:
GitLab CI/CD | GitHub Actions |
---|---|
|
|
Diferenças
Conforme demonstrado acima, essas duas ferramentas são semelhantes em vários aspectos. Oferecem as mesmas capacidades com algumas variações pequenas nos arquivos de configuração.
Agora será discutido alguns pontos em que o GitHub Actions e o GitLab CI são muito diferentes.
Organização dos arquivos
Ambas as plataformas são configuradas usando arquivos YAML, no entanto o GitHub
Actions não suporta algumas funcionalidades da própria especificação desse
formato de arquivo, como o separador de documentos ---
e anchors &
:
Somado com a limitação de definir um workflow por arquivo, se não for tomado um cuidado para usar as Actions adequadamente o resultado pode ser múltiplos arquivos grandes e com bastante repetição.
No GitLab CI a chave include
permite importar definição de partes de uma
esteira de CI de outros arquivos, que podem estar em outra pasta, outro
repositório ou até mesmo numa URL qualquer. Isso dá liberdade para organizar os
arquivos da esteira da forma que se desejar.
Retry
No GitLab CI, através da interface, é possível rodar novamente um job clicando em um botão. Pedir para reexecutar um job é algo bem comum numa esteira de CI, uma falha pode ter ocorrido temporariamente e um simples retry resolve o problema.
No GitHub Actions não é possível dar retry em um job individual, você deve dar retry no workflow inteiro. Ao clicar em "Re-run jobs" a esteira inteira vai rodar novamente. Corrigir essa limitação é algo pedido desde o lançamento do GitHub Actions em 2019:
Para evitar problemas é melhor criar vários arquivos de workflows que executam rapidamente. Preferir várias esteiras rasas ao invés de uma esteira profunda.
Marketplace
A maior diferença entre o GitHub Actions e outros sistemas de CI é sem dúvidas
o marketplace de Actions. No meio da definição de suas esteiras com um
simples uses
você economiza um bom tempo: não precisa escrever várias linhas
de Shell script no meio de um YAML ou empacotar algo num container Docker.
O código das Actions fica no próprio GitHub, portanto vários vendors e organizações oferecem actions oficiais para incluir suas ferramentas numa esteira de CI. Hoje existe quase 10.000 Actions disponíveis para construir esteiras de CI mais rápido. Seu próprio time de desenvolvimento pode criar Actions para diversos fins.
Além disso, limitações que existem na plataforma podem ser superadas usando Actions desenvolvidas pela comunidade. É uma plataforma aberta para extensões.
Versões e Edições
O modelo de hospdeagem do GitLab é bem mais flexível do que o do GitHub.
Existe uma instância pública do SaaS em gitlab.com
e a sua organização pode
hospedar uma instância privada do GitLab, em diversas edições. Como o GitLab
é um software Open Source existe até uma edição que você pode hospedar sem
pagar nada para a GitLab. O GitLab CI faz parte do GitLab, portanto a versão e
edição usada no GitLab e GitLab CI são a mesma.
Essa flexibilidade de como gerenciar instâncias de GitLab reflete na documentação: você deve ficar atento com os avisos de qual versão do GitLab ganhou uma funcionalidade e se ela está disponível na licença que você usa.
Ao lado do nome de uma funcionalidade tem uma etiqueta identificando a licença necessária para usar: premium ou all tiers, por exemplo. E abaixo do nome da funcionalidade tem um histórico de mudanças significativas e em qual versão do GitLab essa mudança entrou: "Introduced in GitLab 11.4", por exemplo.
Se a sua organização usa a versão SaaS ou mantém o GitLab sempre atualizado, isso não é uma preocupação grande. Basta saber a edição usada (licença) e conferir a documentação.
Qual é o sistema ideal para o seu time?
O GitHub Actions e o GitLab CI são bons sistemas de integração contínua. Você provavelmente vai conseguir construir uma esteira adequada para as necessidades de seus projetos independente da escolha. Mas ter essas diferenças em mente pode ajudar a fazer uma escolha melhor para seu time e seus projetos.
A sintaxe do GitHub Actions lembra menos Shell script e mais JavaScript, incentiva o empacotamento de funcionalidades em repositórios git e tem opções mais limitadas para a organização das esteiras. Essas limitações no entanto, ajudam a evitar decisões ruins.
Para o GitLab CI é útil saber Shell script e se sentir confortável containerizando aplicações. Há muito mais opções de como organizar as esteiras e flexibilidade para definir permissionamento e controle de acesso. Com a opção de gerenciar tanto a instância de GitLab quanto os runners do CI, é uma opção excelente para o cenário enterprise.
Portanto, se a maioria do seu time for composta por devs, o GitHub Actions pode ser a melhor opção. Se algumas pessoas do seu time tem "DevOps" ou "SRE" no título do cargo, o pontencial do GitLab CI pode ser melhor explorado.
Por ser uma solução mais madura, o GitLab CI é ideal para projetos com muitas partes móveis e/ou processo de release mais exigentes: com etapas de aprovação manual, ajuste fino de permissões, múltiplos ambientes, etc.
Funcionalidade | GitLab CI | GitHub Actions |
---|---|---|
CI Nativo | ✔️ | ✔️ |
Jobs | ✔️ | ✔️ |
Minutos inclusos | 400 para repositórios privados, 50.000 para públicos | 2.000 para repositórios privados, ilimitado para públicos |
Runners self-hosted | ✔️ | ✔️ |
Runners Linux Gerenciados | 1 vCPU, 3,75GB de RAM e 25GB de HDD | 2 vCPU, 7GB de RAM e 14GB de SSD |
Runners Windows Gerenciados | Beta. 2 vCPU e 7,5GB de RAM | 2 vCPU, 7GB de RAM e 14GB de SSD |
Runners MacOS Gerenciados | Beta. 4 vCPUs e 10GB de RAM | 3 vCPU, 14GB de RAM e 14GB de SSD |
Docker | ✔️ | ✔️ |
Variáveis de Ambiente e Secrets | ✔️ | ⭐ Usa padrões mais seguros |
Condicionais | ⭐ Mais fácil de aprender e mais flexível | ✔️ |
Execução Agendada | Mais restrito, com maior controle | Menos restrito, mais disponível |
Arquitetura da esteira | Oferece mais opções, tem mais flexibilidade | Oferece apenas uma opção, que é performática |
Serviços Extras | Apenas para runners Docker | ✔️ |
Cache | ✔️ | Apenas para runners gerenciados |
Artefatos | 30 dias por padrão, cota compartilhada de 10GB | 90 dias por padrão, cota exclusiva de 500MB |
Organização dos arquivos | Muito flexível | Uma esteira por arquivo |
Retry | ✔️ | Apenas para a esteira inteira |
Marketplace | ❌ | ✔️ |
Construindo esteiras de CI em segundos
Este post não cobre todas as funcionalidades que existem nesses dois sistemas de CI, e ainda existem outros. Construir uma esteira de CI eficiente pode ser trabalhoso, exigir horas para ler documentação e testar a esteira. Como o feedback loop do desenvolvimento de uma esteira de CI é lento, bem mais devagar do que o loop do TDD, por exemplo, as primeiras tentativas podem ser bem frustrantes.
Além disso, vários projetos compartilham características similares, que poderiam ser descritas por estágios bem padronizados de esteiras de CI. Com a evolução dos projetos de software e dos próprios sistemas de CI, a esteiras de CI se tornam parte do projeto que também precisam de manutenção e atualização constante.
Pensando nesses problemas, a Instruct começou o desenvolvimento do Pipelinit, uma ferramenta gratuita e Open Source que gera esteiras de CI completas em segundos. Disponível para uso através de um CLI ou um Playground Online. Confira o projeto!
Para saber mais
- GitLab vs GitHub Decision Kit | GitLab
- Migrating from GitLab CI/CD to GitHub Actions | GitHub
Referências
- GitLab 8.0 released with new looks and integrated CI! | GitLab Releases
- GitHub Actions is generally available | GitHub Blog
- What are the best Continuous Integration Tools? | Stackshare
- Jenkins 1.396 released | Jenkins Nabble
- 2012 at Travis CI - what a blast! | Travis CI Blog
- Hudson | java.net
- The State of Developer Ecosystem 2021 | JetBrains
- GitLab Pricing | GitLab
- GitHub Pricing | GitHub
- GitLab Build Cloud runners | GitLab Docs
- Build Cloud runners for Linux | GitLab Docs
- VM instances and images for Build Cloud for macOS | GitLab Docs
- Build Cloud runners for Windows (beta) | GitLab Docs
- About GitHub-hosted runners | GitHub Docs
- Keyword reference for the .gitlab-ci.yml file | GitLab Docs
- Workflow syntax for GitHub Actions | GitHub Docs
- Encrypted secrets | GitHub Docs
- Environment variables | GitHub Docs
- GitLab CI/CD variables | GitLab Docs
- GitLab environment variables demystified | GitLab Blog
- Choose when to run jobs - CI/CD variable expressions | GitLab Docs
- Context and expression syntax for GitHub Actions | GitHub Docs
- Events that trigger workflows - Scheduled events | GitHub Docs
- Pipeline schedules | GitLab Docs
- Pipeline architecture | GitLab Docs
- About service containers | GitHub Docs
- Services | GitLab Docs
- Caching dependencies to speed up workflows | GitHub Docs
- Storing workflow data as artifacts | GitHub Docs
- Caching in GitLab CI/CD | GitLab Docs
- YAML Ain’t Markup Language (YAML™) Version 1.2 | YAML Spec
- GitHub Marketplace - Actions | GitHub Marketplace