Skip to main content

Stages

Vamos evolumir um pouco mais e criar dois jobs. Vamos mudar um pouco o pipeline para que comece a entender os stages.

# Boa prática que aprendermos!
default:
image: debian:bullseye-slim
tags:
- general

build_project:
# Só para lembrar poderíamos definir imagem aqui sobreescrendo o valor default.
# image: debian:bullseye-slim
script:
- echo "Build project"
- mkdir build
- cd build
- echo "Our build" > build.txt
- cat build.txt
# Vamos testar se conseguimos pegar o valor de uma variável masked.
# Primeiro colocando em um arquivo para ver o que acontece.
- echo $VAR2 > var2.txt
- cat var2.txt
# Dois testes extras copiando a variável para outra e colocando no arquivo.
- NOVA_VAR2=$VAR2
- echo $NOVA_VAR2
- echo $NOVA_VAR2 > novavar2.txt
- cat novavar2.txt

# Neste job vamos testar o arquivo.
test_project:
script:
- echo "Testando o arquivo existe..."
- test -f build/build.txt
- echo "Testando se o conteúdo existe..."
- grep "Our build" build.txt
git add .gitlab-ci.yml  
git commit -m "test more jobs"
git push origin main

Veja o que aconteceu...

test é o que chamamos de stage. Esses dois jobs estão rodando no mesmo stage e em paralelo sem dependência entre eles.

Se não for especificado um stage por default será test. É boa prática e devemos sempre especificar os stages.

alt text

text_project executou primeiro do que build_project, ou seja, eles executaram juntos (em paralelo) sem nenhum tipo de dependência.

alt text

Por curiosidade veja o que aconteceu sobre as variáveis masked do job build_project...

...
Using docker image sha256:6ad184acc3babba4df7e3126b4a8a13bed727901c11fc121f141445f98e1ddba for debian:bullseye-slim with digest debian@sha256:7aafeb23eaef5d5b1de26e967b9a78f018baaac81dd75246b99781eaaa2d59ef ...
$ echo "Build project"
Build project
$ mkdir build
$ cd build
$ echo "Our build" > build.txt
$ cat build.txt
Our build
$ echo $VAR2 > var2.txt
$ cat var2.txt
[MASKED]
$ NOVA_VAR2=$VAR2
$ echo $NOVA_VAR2
[MASKED]
$ echo $NOVA_VAR2 > novavar2.txt
$ cat novavar2.txt
[MASKED]
Cleaning up project directory and file based variables
00:01
Job succeeded

O valor na verdade é lido normalmente, o que acontece é que o frontend do GitLab "mascara" o valor durante a exibição dos logs. Esse valor esta dentro do arquivo, foi amostrado no console, mas não foi exposto no GitLab.

O Gitlab só esconde na UI. Quer ver?

Já vamos resolver a situação de não deixar esses jobs rodar em paralelo. Vamos criar dois stages.

# Boa prática que aprendermos!
default:
image: debian:bullseye-slim
tags:
- general

stages: # A sequencia que é definido os stages garante uma ordem.
- build
- test

build_project:
stage: build
# Só para lembrar poderíamos definir imagem aqui sobreescrendo o valor default.
# image: debian:bullseye-slim
script:
- echo "Build project"
- mkdir build
- cd build
- echo "Our build" > build.txt
- cat build.txt
- echo "$VAR2" | base64 > segredo.b64
- echo "VAR2 encodado em base 64"
- cat segredo.b64
# Neste job vamos testar o arquivo.
test_project:
stage: test
script:
- echo "Testando o arquivo existe..."
- test -f build/build.txt
- echo "Testando se o conteúdo existe..."
- grep "Our build" build.txt
git push origin main  
git commit -m "build and test stage"
git push origin main

Conferindo podemos ver que a ordem foi feita, mas ainda sim o segundo job teve problemas.

alt text

O primeiro job tem o seguinte log.

...
$ echo "Build project"
Build project
$ mkdir build
$ cd build
$ echo "Our build" > build.txt
$ cat build.txt
Our build
$ echo "$VAR2" | base64 > segredo.b64
$ echo "VAR2 encodado em base 64"
VAR2 encodado em base 64
$ cat segredo.b64
dmFyaWFibGUyCg==
Cleaning up project directory and file based variables
00:01
Job succeeded

Se decodificarmos esse valor base64 o que temos?

echo "dmFyaWFibGUyCg==" | base64 --decode
variable2

Ou seja, só por que não mostrou na interface não quer dizer que você não consegue descobrir. Ao final do curso vamos falar de uma lista de métodos que podemos usar para revelar essas variáveis. A intenção não é te ensinar como burlar as coisas, mas como se defender. Quem sabe como fazer sabe como se proteger.

Por fim, o segundo job teve o seguinte problema.

...
$ echo "Testando o arquivo existe..."
Testando o arquivo existe...
$ test -f build/build.txt
Cleaning up project directory and file based variables
00:02
ERROR: Job failed: exit code 1

Não encontrou o arquivo. Esses containers rodam em ambiente completamente isolados. O que aconteceu no container do build_project morreu com ele.

  1. Aprendemos que stages garantem ordem somente em stages, mas não entre jobs do mesmo stage.

  2. Os stages ajudam a dividir as etapas para ficar mais fácil ver o processo.

  3. Pipelines são acionadas todas as vezes que uma mudanças no repositório acontece. Até agora, sempre que fizemos qualquer modificação no repositório a pipeline foi executada. Será que foi só por que foi na branch main? Não e por último faremos uma amostragem disso.

  4. O console mascara a amostragem de uma secret, mas não o valor real dela durante o processo de execução. Com um acesso direto ao container poderíamos ver tudo. É necessário se proteger de alguns métodos que burlam esse cenário.

Artifact

O artifact é uma espécie de output que podemos salvar no contexto da pipeline. É como se fosse um file system que no primeiro job inicia vazio, mas nos demais pegam as coisas que são adicionadas dentro dele.

Quando salvamos algo podemos utilizar em outro job.

Então vamos modificar para que build_project salve a pasta output.

default:
image: debian:bullseye-slim
tags:
- general

stages:
- build
- test

build_project:
stage: build
script:
- echo "Build project"
- mkdir build
- cd build
- echo "Our build" > build.txt
- cat build.txt
artifacts: # Salvando essa pasta no contexto
paths:
- build/

test_project:
stage: test
script:
- echo "Testando o arquivo existe..."
# Obseve que estamos respeitando a hierarquia de diretório.
- test -f build/build.txt
- echo "Testando se o conteúdo existe..."
- grep "Our build" build/build.txt
git add .gitlab-ci.yml  
git commit -m "add artifact"
git push origin main

E temos sucesso!

alt text

Inclusive podemos ter acesso a esses artefatos.

alt text

Sabendo disso, esse é mais um teste a ser fazer em relação as variáveis.

Isso funcionaria perfeitamente se fizessemos download do artefato

build_project:
stage: build
script:
- echo "Build project"
- mkdir build
- cd build
- echo "Our build" > build.txt
- cat build.txt
- echo $VAR3 > var3.txt # <<<<
- cat build.txt
artifacts: # Salvando essa pasta no contexto
paths:
- build/

Ao fazer o download desse artefato pela interface gráfica para a pasta de download....

cd ~/Downloads
unzip artifacts.zip
Archive: artifacts.zip
replace build/build.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: n
inflating: build/var3.txt

cat build/var3.txt
variable3

Isso já vale para que você preste atenção ao fazer um review que envolve pipeline!

Vamos mudar a Branch para provar que a pipeline também será acionada.

git checkout -b develop  
Switched to a new branch 'develop'

git push origin develop
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: To create a merge request for develop, visit:
remote: https://gitlab.com/puziol/first-pipeline/-/merge_requests/new?merge_request%5Bsource_branch%5D=develop
remote:
To gitlab.com:puziol/first-pipeline.git
* [new branch] develop -> develop

alt text

E se fizermos um merge? Da develop para main também será executado.

Vale a pena observar alguns detalhes ainda...

alt text

Na barra lateral esquerda temos a duração, tempo em fila, qual o runner executou e a sua tag, qual o evento disparou (um push), os artefatos que podemos manter, fazer o download ou ver via browser, o número do commit, etc. Em browser temos...

alt text

Veja no log que ele fez o upload dos artifacts antes de terminar o job. Ao terminar o job esse container é destruido, mas o artefato foi mantido.

Na sequencia o job test_project fez o download do artefato antes de iniciar o job.

alt text

O GitLab atua como um servidor de arquivo entre jobs.

Por default não precisamos definir alguns stages, pois são pré configurados no GitLab. Ao marcar jobs para esses stages sem definí-los não teremos errors. Porém, é boa prática definir os stages para ficar claro, principalmente para quem não sabe disso.

Os stages default estão na seguinte ordem.

stages:
- .pre
- build
- test
- deploy
- .post

Alguns detalhes que vale a pena mensionar sobre definição de jobs.

# Dois jobs com nomes idênticos
build:
stage: build
image: node:22-slim
script:
- node --version
- npm --version
- npm ci
- npm run build

build: # Isso sobreescrete todo o job build acima. O arquivo é lido de cima para baixo, logo a última definição do job é que vai prevalecer
stage: teste
image: alpine
script:
- mkdir teste

E se um job começar com . então ele é ignorado, ou seja, nem precisamos comentar.

# Seria mais ou menos a mesma coisa
.build:
stage: teste
image: alpine
script:
- mkdir teste

# build:
# stage: teste
# image: alpine
# script:
# - mkdir teste

Porém o que eu escrevi acima não é verdade. Apesar de .build se ignorado ele pode funcionar como template, diferente de quando comentamos. Um template nunca é executado sozinho. Podemos utilizar o .build como um template e sobreescrever só a parte que queremos. Faremos isso mais pra frente, mas aqui é só uma pequena demonstração.

.build:
stage: teste
image: alpine
script:
- mkdir teste

build:
extends: .build
# Esse build tem tudo isso
# stage: teste
# image: alpine
# script:
# - mkdir teste

Ainda existe outras formas que aprenderemos no futuro.