kb.erickguedes.com
GitHub Actions: CI/CD Moderno

Actions Customizadas

Aula 2 de 5

Tipos de Actions

Docker container: ambiente completo, lento mas consistente
JavaScript/TypeScript: rápido (node), sem dependências
Composite: combinação de steps (DRY)

Action JavaScript

# action.yml
name: 'Slack Notification'
description: 'Envia notificação para Slack'
inputs:
  message:
    description: 'Mensagem a enviar'
    required: true
  webhook-url:
    description: 'Slack webhook URL'
    required: true
outputs:
  timestamp:
    description: 'Timestamp do envio'
    value: ${{ steps.send.outputs.time }}
runs:
  using: 'node20'
  main: 'dist/index.js'
// index.js
const core = require('@actions/core');
const fetch = require('node-fetch');

async function run() {
  try {
    const message = core.getInput('message');
    const webhook = core.getInput('webhook-url');

    const resp = await fetch(webhook, {
      method: 'POST',
      body: JSON.stringify({ text: message }),
    });

    core.setOutput('timestamp', new Date().toISOString());
    core.info(`Notificação enviada: ${message}`);
  } catch (error) {
    core.setFailed(error.message);
  }
}

run();
# Build para distribuição
npm install @actions/core node-fetch
npx ncc build index.js --license licenses.txt

Action Docker

# action.yml
name: 'Lint with ShellCheck'
description: 'Check shell scripts'
inputs:
  path:
    description: 'Path to scan'
    default: '.'
runs:
  using: 'docker'
  image: 'Dockerfile'
  args:
    - ${{ inputs.path }}
FROM koalaman/shellcheck-alpine:stable

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

Composite Action

# actions/setup-node-cache/action.yml
name: 'Setup Node with Cache'
description: 'Node + pnpm cache'
inputs:
  node-version:
    default: '20'
runs:
  using: 'composite'
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}

    - uses: pnpm/action-setup@v3
      with:
        version: latest

    - uses: actions/cache@v4
      id: pnpm-cache
      with:
        path: ~/.pnpm-store
        key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
        restore-keys: |
          ${{ runner.os }}-pnpm-

    - if: steps.pnpm-cache.outputs.cache-hit != 'true'
      run: pnpm install
      shell: bash

Reusable Workflows

# .github/workflows/deploy-template.yml (chamado)
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      cloud-token:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4
      - run: echo "Deploying to ${{ inputs.environment }}"
# .github/workflows/deploy-prod.yml (chamador)
on:
  push:
    branches: [main]

jobs:
  call-deploy:
    uses: ./.github/workflows/deploy-template.yml
    with:
      environment: production
    secrets:
      cloud-token: ${{ secrets.CLOUD_TOKEN }}

Publicar no Marketplace

# Estrutura recomendada
your-action/
├── action.yml
├── dist/
│   └── index.js
├── src/
│   └── index.ts
├── package.json
├── tsconfig.json
├── LICENSE
└── README.md
# action.yml (com branding)
branding:
  icon: 'bell'
  color: 'yellow'

Actions são blocos reutilizáveis. Composite actions são ideais para combinar steps complexos. Reusable workflows evitam duplicação entre repos. Use @actions/core para boilerplate.