Custom Action
A maioria das actions que já usamos são públicas criadas pelo time do github ou pela comunidade, ainda não criamos nenhuma action personalizada.
Podemos criar uma actions para simplificar vários steps no nosso workflow de uma única vez criando uma única action simplificando o workflow e melhorando a legibilidade.
Por exemplo temos essas 2 steps nos jobs lint, test e build que poderiam ser agrupadas.
- name: Cache dependencies
id: cache
uses: actions/cache@v3
with:
path: node_modules
key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: npm ci
Apesar de existirem muitas actions disponíveis no github marketplace em algum momento podemos precisar de algo que ainda não exista ou até existe, mas queremos personalizar o comportamento. É raro, mas acontece com frequência :D. É uma opotunidade de contribuir e publicar para outros usuários poderem ajudar a melhorar a action.
Existe 3 tipos de actions que são utilizadas da mesma forma sendo chamada pelo uses
mas são buildadas de forma diferente.
- JavaScript Actions
- O código esta em Javascript e sempre que a action for executada chamará um arquivo
.js
. O runtime de execução será o nodejs com seus recursos e pacotes. - Runtime Nativo do GitHub.
- Não irei abordar aqui, vale a pena esquiser caso não consigo resolver usando o docker.
- O código esta em Javascript e sempre que a action for executada chamará um arquivo
- Docker Actions
- Utilizada para desenvolver em outras linguagens. O que importa é a ação que a action irá executar e esta pode ser uma ação do container. Dentro do container podemos desenvolver em qualquer linguagem, pois estaremos em uma ambiente preparado para a execução.
- Maior flexibilidade e portabilidade.
- Não irei abordar aqui, mas vale a pena pesquiser caso actions existentes não resolvam o problema.
- Composite Actions
- Não desenvolvemos nada, apenas combinamos vários steps e actions para resolver o problema, ou resumí-lo.
- Se você não gosta de desenvolver é aqui mesmo que você vai preferir ficar.
Se quisermos criar uma action que esteja disponível para outros repositórios ou que seja pública esta deve estar em um repositório exclusivo.
Para criar uma action em um projeto específico e acessível somente para neste repositório podemos criá-la em qualquer lugar do projeto, pois vamos referênciá-la usando o path.
É boa prática que esteja dentro da pasta .github/actions. Toda action precisar ter a sua pasta e dentro desta pasta estar no arquivo
action.y ml
cd projeto
tree .github
.github
├── actions
└── workflows
└── deploy.yml
Vamos criar uma action chamada cache-deps com os dois steps que mensionamos anteriormente dentro da pasta actions.
Actions são definidas também em arquivo yml. Actions não sofrem eventos então não possuem o
on
.
Apesar da action checkout
também fazer parte de vários steps estamos criando uma action que somente irá fazer parte deste repositório sendo necessário antes poder usar a action definida localmente. Se fosse criado uma action em um repositório separado faria sentido incluir o checkout também sendo possível fazer referência à action por meio da url do repositório. Esse é um caso específico pois não podemos fazer refência ao próprio projeto que estamos fazendo o checkout.
name: 'Get And Cache Deps'
description: 'Get dependencies via npm and cache them.'
runs:
using: 'composite' # Ese é o tipo da action
steps:
## Nosso bloco de steps ##
- name: Cache dependencies
id: cache
uses: actions/cache@v3
with:
path: node_modules
key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true' # Também tem suporte as condições então podemos manter
run: npm ci
shell: bash # Quando vamos usar um comando run precisamos definir shell
###########################
Só para mostrar como ficou a estrutura de pasta.
tree .github
.github
├── actions # Pasta
│ └── cache-deps # Pasta com nome da action
│ └── action.yml # arquivo da que define a action
└── workflows
└── deploy.yml
Agora vamos fazer referência a esta action no worfflow abaixo, deixarei comentado o que for removido
name: Deployment
on:
push:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
# - name: Cache dependencies
# id: cache
# uses: actions/cache@v3
# with:
# path: node_modules
# key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
# - name: Install dependencies
# if: steps.cache.outputs.cache-hit != 'true'
# run: npm ci
- name: Cache and Install Deps # <<<<
uses: ./.github/actions/cache-deps # <<<< Apontamos a pasta que contém o action.yaml
- name: Lint code
run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
# - name: Cache dependencies
# id: cache
# uses: actions/cache@v3
# with:
# path: node_modules
# key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
# - name: Install dependencies
# if: steps.cache.outputs.cache-hit != 'true'
# run: npm ci
- name: Cache and Install Deps # <<<<
uses: ./.github/actions/cache-deps # <<<< Apontamos a pasta que contém o action.yaml
- name: Test code
id: run-tests
run: npm run test
- name: Upload test report
if: failure() && steps.run-tests.outcome == 'failure'
uses: actions/upload-artifact@v3
with:
name: test-report
path: test.json
build:
needs: test
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
# - name: Cache dependencies
# id: cache
# uses: actions/cache@v3
# with:
# path: node_modules
# key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
# - name: Install dependencies
# if: steps.cache.outputs.cache-hit != 'true'
# run: npm ci
- name: Cache and Install Deps # <<<<
uses: ./.github/actions/cache-deps # <<<< Apontamos a pasta que contém o action.yaml
- name: Build website
run: npm run build
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: dist-files
path: dist
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
- name: Get build artifacts
uses: actions/download-artifact@v3
with:
name: dist-files
path: ./dist
- name: Output contents
run: ls
- name: Deploy site
run: echo "Deploying..."
Enxugando o código.
name: Deployment
on:
push:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
- name: Cache and Install Deps
uses: ./.github/actions/cache-deps
- name: Lint code
run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
- name: Cache and Install Deps
uses: ./.github/actions/cache-deps
- name: Test code
id: run-tests
run: npm run test
- name: Upload test report
if: failure() && steps.run-tests.outcome == 'failure'
uses: actions/upload-artifact@v3
with:
name: test-report
path: test.json
build:
needs: test
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
- name: Cache and Install Deps
uses: ./.github/actions/cache-deps
- name: Build website
run: npm run build
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: dist-files
path: dist
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
- name: Get build artifacts
uses: actions/download-artifact@v3
with:
name: dist-files
path: ./dist
- name: Output contents
run: ls
- name: Deploy site
run: echo "Deploying..."
Inputs e Outputs
Anteriormente usamos a tag with para passar os inputs para outras actions. Então vamos fazer a mesma coisa.
Vamos colocar coloca um input se devemos ou não fazer o cache. Caso não for passado ele usará o valor default e fará o cache normalmente.
name: 'Get And Cache Deps'
description: 'Get dependencies via npm and cache them.'
# Definindo as variáveis
inputs:
caching:
description: 'Se vamos ou não fazer o cache'
required: false # Não é obrigatório passar o input
default: 'true' # valor padrão caso não for passado
# Exemplo de como seria outro input...
# other-var:
# description: 'other var'
# required: true
# default: 'idontknow'
runs:
using: 'composite'
steps:
- name: Cache dependencies
if: inputs.caching == 'true' # Só irá faze ro cache se for true
id: cache
uses: actions/cache@v3
with:
path: node_modules
key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true' || inputs.caching != 'true'
run: npm ci
shell: bash # Quando vamos usar um comando run precisamos definir shell
###########################
Mudamos a condição do step install dependencies pois ou ele vai fazer o cache quando o primeiro step executar e der um cache-hit ou caso ele receber o input diferente de true. Se não fará o cache, então precisa instalar forçadamente as dependencias.
vamos alterar somente o lint para usar não usar o cache.
...
lint:
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
- name: Cache and Install Deps
uses: ./.github/actions/cache-deps
with:
caching: 'false'
...
Vamos colocar um output aqui só para ilustrar como referenciar as coisas.
name: 'Get And Cache Deps'
description: 'Get dependencies via npm and cache them.'
inputs:
caching:
description: 'Se vamos ou não fazer o cache'
required: false
default: 'true'
runs:
using: 'composite'
steps:
- name: Cache dependencies
id: cache
if: ${{ inputs.caching == 'true' }}
uses: actions/cache@v3
with:
path: node_modules
key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
id: install
if: ${{ steps.cache.outputs.cache-hit != 'true' || inputs.caching != 'true' }}
shell: bash
run: npm ci
- name: Set output if cache was used
id: fill-outputs
shell: bash
run: |
if [[ "${{ steps.install.outcome }}" == "success" ]]; then
echo "used=false" >> $GITHUB_OUTPUT
else
echo "used=true" >> $GITHUB_OUTPUT
fi
# Acima conferimos se o step anterior teve ou não sucesso.
# Se teve sucesso criamos uma variável chamada used com o valor false pois ele não usou o cache
# Se não teve sucesso então o cache foi usado definimos false.
outputs:
used-cache:
description: "Se cache foi usado"
# Referenciamos o a variável used criada no step fill-outputs
value: ${{ steps.fill-outputs.outputs.used }}
Vamos colocar uma saída no lint para conferir.
...
lint:
runs-on: ubuntu-latest
steps:
- name: Get code
uses: actions/checkout@v3
- name: Cache and Install Deps
id: cache-deps # <<<added
uses: ./.github/actions/cache-deps
with:
caching: 'false'
- name: Output Info
run: echo "Cache used? ${{ steps.cache-deps.outputs.used-cache}}"
...
No lint que definomos para não
![]() | ![]() |