Skip to main content

Como usar simultaneidade, expressões e uma matriz de teste

Como usar recursos avançados do GitHub Actions para CI (integração contínua).

Observação: no momento, não há suporte para os executores hospedados no GitHub no GitHub Enterprise Server. Você pode ver mais informações sobre o suporte futuro planejado no GitHub public roadmap.

Visão geral de exemplo

Este artigo usa um fluxo de trabalho de exemplo para demonstrar alguns dos principais recursos de CI do GitHub Actions. Quando esse fluxo de trabalho é disparado, ele testa o código usando uma matriz de combinações de teste com npm test.

O diagrama a seguir mostra uma visão de alto nível das etapas do fluxo de trabalho e como elas são executadas no trabalho:

Diagrama de visão geral das etapas do fluxo de trabalho

Recursos usados neste exemplo

O fluxo de trabalho de exemplo demonstra os seguintes recursos do GitHub Actions:

RecursoImplementação
Como executar um fluxo de trabalho manualmente usando a interface do usuário:workflow_dispatch

Fluxo de trabalho de exemplo

O fluxo de trabalho a seguir foi criado pela equipe de engenharia de Docs do GitHub. Para revisar a versão mais recente deste arquivo no repositório github/docs, confira test.yml.

Observação: cada linha desse fluxo de trabalho é explicada na próxima seção em "Noções básicas sobre o exemplo".

YAML
name: Node.js Tests

# **What it does**: Runs our tests.
# **Why we have it**: We want our tests to pass before merging code.
# **Who does it impact**: Docs engineering, open-source engineering contributors.

on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      - main

permissions:
  contents: read
  # Needed for the 'trilom/file-changes-action' action
  pull-requests: read

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
  group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
  cancel-in-progress: true

jobs:
  test:
    # Run on self-hosted if the private repo or ubuntu-latest if the public repo
    # See pull # 17442 in the private repo for context
    runs-on: ${{ fromJSON('["ubuntu-latest", "self-hosted"]')[github.repository == 'github/docs-internal'] }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        # The same array lives in test-windows.yml, so make any updates there too.
        test-group:
          [
            content,
            graphql,
            meta,
            rendering,
            routing,
            unit,
            linting,
            translations,
          ]
    steps:
      # Each of these ifs needs to be repeated at each step to make sure the required check still runs
      # Even if if doesn't do anything
      - name: Check out repo
        uses: actions/checkout@v3
        with:
          # Not all test suites need the LFS files. So instead, we opt to
          # NOT clone them initially and instead, include them manually
          # only for the test groups that we know need the files.
          lfs: ${{ matrix.test-group == 'content' }}
          # Enables cloning the Early Access repo later with the relevant PAT
          persist-credentials: 'false'

      - name: Figure out which docs-early-access branch to checkout, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        id: check-early-access
        uses: actions/github-script@v6
        env:
          BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
        with:
          github-token: ${{ secrets.DOCUBOT_REPO_PAT }}
          result-encoding: string
          script: |
            // If being run from a PR, this becomes 'my-cool-branch'.
            // If run on main, with the `workflow_dispatch` action for
            // example, the value becomes 'main'.
            const { BRANCH_NAME } = process.env
            try {
              const response = await github.repos.getBranch({
                owner: 'github',
                repo: 'docs-early-access',
                BRANCH_NAME,
              })
              console.log(`Using docs-early-access branch called '${BRANCH_NAME}'.`)
              return BRANCH_NAME
            } catch (err) {
              if (err.status === 404) {
                console.log(`There is no docs-early-access branch called '${BRANCH_NAME}' so checking out 'main' instead.`)
                return 'main'
              }
              throw err
            }

      - name: Check out docs-early-access too, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        uses: actions/checkout@v3
        with:
          repository: github/docs-early-access
          token: ${{ secrets.DOCUBOT_REPO_PAT }}
          path: docs-early-access
          ref: ${{ steps.check-early-access.outputs.result }}

      - name: Merge docs-early-access repo's folders
        if: ${{ github.repository == 'github/docs-internal' }}
        run: |
          mv docs-early-access/assets assets/images/early-access
          mv docs-early-access/content content/early-access
          mv docs-early-access/data data/early-access
          rm -r docs-early-access

      # This is necessary when LFS files where cloned but does nothing
      # if actions/checkout was run with `lfs:false`.
      - name: Checkout LFS objects
        run: git lfs checkout

      - name: Gather files changed
        uses: trilom/file-changes-action@a6ca26c14274c33b15e6499323aac178af06ad4b
        id: get_diff_files
        with:
          # So that `steps.get_diff_files.outputs.files` becomes
          # a string like `foo.js path/bar.md`
          output: ' '

      - name: Insight into changed files
        run: |

          # Must to do this because the list of files can be HUGE. Especially
          # in a repo-sync when there are lots of translation files involved.
          echo "${{ steps.get_diff_files.outputs.files }}" > get_diff_files.txt

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16.14.x
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Cache nextjs build
        uses: actions/cache@v3
        with:
          path: .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('package*.json') }}

      - name: Run build script
        run: npm run build

      - name: Run tests
        env:
          DIFF_FILE: get_diff_files.txt
          CHANGELOG_CACHE_FILE_PATH: tests/fixtures/changelog-feed.json
        run: npm test -- tests/${{ matrix.test-group }}/

Compreendendo o exemplo

A tabela a seguir explica como cada um desses recursos é usado ao criar um fluxo de trabalho GitHub Actions.

Código Explicação
YAML
name: Node.js Tests

O nome do fluxo de trabalho, como aparece na guia "Ações" do repositório do GitHub.

YAML
on:

A palavra-chave on permite definir os eventos que são disparados quando o fluxo de trabalho é executado. Você pode definir vários eventos aqui. Para obter mais informações, confira "Como disparar um fluxo de trabalho".

YAML
  workflow_dispatch:

Adicione o evento workflow_dispatch se você quiser a capacidade de executar manualmente esse fluxo de trabalho na interface do usuário. Para obter mais informações, confira workflow_dispatch.

YAML
  pull_request:

Adicione o evento pull_request para que o fluxo de trabalho seja executado automaticamente sempre que uma solicitação de pull for criada ou atualizada. Para obter mais informações, confira pull_request.

YAML
  push:
    branches:
      - main

Adicione o evento push para que o fluxo de trabalho seja executado automaticamente sempre que um commit for enviado por push a um branch que corresponda ao filtro main. Para obter mais informações, confira push.

YAML
permissions:
  contents: read
  pull-requests: read

Modifica as permissões padrão concedidas a GITHUB_TOKEN. Isso variará dependendo das necessidades do fluxo de trabalho. Para obter mais informações, confira "Como atribuir permissões a trabalhos".

YAML
concurrency:
  group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'

Cria um grupo de simultaneidade para eventos específicos e usa o operador || para definir valores de fallback. Para mais informações, confira "Como usar a simultaneidade".

YAML
  cancel-in-progress: true

Cancela todos os trabalhos ou fluxos de trabalho em execução no mesmo grupo de simultaneidade.

YAML
jobs:

Agrupa todos os trabalhos executados no arquivo de fluxo de trabalho.

YAML
  test:

Define um trabalho com a ID test que é armazenada na chave jobs.

YAML
    runs-on: ${{ fromJSON('["ubuntu-latest", "self-hosted"]')[github.repository == 'github/docs-internal'] }}

Configura o trabalho a ser executado em um executor hospedado no GitHub ou um executor auto-hospedado, dependendo do repositório que executa o fluxo de trabalho. Neste exemplo, o trabalho será executado em um executor auto-hospedado se o repositório for chamado docs-internal e estiver dentro da organização github. Se o repositório não corresponder a esse caminho, ele será executado em um executor ubuntu-latest hospedado no GitHub. Para obter mais informações sobre essas opções, confira "Como escolher o executor para um trabalho".

YAML
    timeout-minutes: 60

Define o número máximo de minutos para permitir que o trabalho seja executado antes de ser cancelado automaticamente. Para obter mais informações, confira timeout-minutes.

YAML
    strategy:
Esta seção define a matriz de build dos trabalhos.
YAML
      fail-fast: false

A configuração de fail-fast como false impede que o GitHub cancele todos os trabalhos em andamento quando algum trabalho da matriz falhar.

YAML
      matrix:
        test-group:
          [
            content,
            graphql,
            meta,
            rendering,
            routing,
            unit,
            linting,
            translations,
          ]

Cria uma matriz chamada test-group, com uma matriz de grupos de teste. Esses valores correspondem aos nomes dos grupos de teste que serão executados por npm test.

YAML
    steps:

Agrupa todas as etapas que serão executadas durante o trabalho test. Cada trabalho em um fluxo de trabalho tem a própria seção steps.

YAML
      - name: Check out repo
        uses: actions/checkout@v3
        with:
          lfs: ${{ matrix.test-group == 'content' }}
          persist-credentials: 'false'

A palavra-chave uses informa que o trabalho deve recuperar a ação chamada actions/checkout. Esta é uma ação que verifica seu repositório e o faz o download do runner, permitindo que você execute ações contra seu código (como, por exemplo, ferramentas de teste). Você deve usar a ação de checkout sempre que o fluxo de trabalho for executado no código do repositório ou você estiver usando uma ação definida no repositório. Algumas opções extras são fornecidas para a ação usando a chave with.

YAML
      - name: Figure out which docs-early-access branch to checkout, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        id: check-early-access
        uses: actions/github-script@v6
        env:
          BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
        with:
          github-token: ${{ secrets.DOCUBOT_REPO_PAT }}
          result-encoding: string
          script: |
            // If being run from a PR, this becomes 'my-cool-branch'.
            // If run on main, with the `workflow_dispatch` action for
            // example, the value becomes 'main'.
            const { BRANCH_NAME } = process.env
            try {
              const response = await github.repos.getBranch({
                owner: 'github',
                repo: 'docs-early-access',
                BRANCH_NAME,
              })
              console.log(`Using docs-early-access branch called '${BRANCH_NAME}'.`)
              return BRANCH_NAME
            } catch (err) {
              if (err.status === 404) {
                console.log(`There is no docs-early-access branch called '${BRANCH_NAME}' so checking out 'main' instead.`)
                return 'main'
              }
              throw err
            }

Se o repositório atual for o github/docs-internal, esta etapa usará a ação actions/github-script para executar um script e verificar se há um branch chamado docs-early-access.

YAML
      - name: Check out docs-early-access too, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        uses: actions/checkout@v3
        with:
          repository: github/docs-early-access
          token: ${{ secrets.DOCUBOT_REPO_PAT }}
          path: docs-early-access
          ref: ${{ steps.check-early-access.outputs.result }}

Se o repositório atual for o github/docs-internal, esta etapa verificará o branch do github/docs-early-access que foi identificado na etapa anterior.

YAML
      - name: Merge docs-early-access repo's folders
        if: ${{ github.repository == 'github/docs-internal' }}
        run: |
          mv docs-early-access/assets assets/images/early-access
          mv docs-early-access/content content/early-access
          mv docs-early-access/data data/early-access
          rm -r docs-early-access

Se o repositório atual for o github/docs-internal, esta etapa usará a palavra-chave run para executar comandos do shell para mover as pastas do repositório docs-early-access para as pastas do repositório principal.

YAML
      - name: Checkout LFS objects
        run: git lfs checkout

Esta etapa executa um comando para fazer check-out de objetos LFS do repositório.

YAML
      - name: Gather files changed
        uses: trilom/file-changes-action@a6ca26c14274c33b15e6499323aac178af06ad4b
        id: get_diff_files
        with:
          # So that `steps.get_diff_files.outputs.files` becomes
          # a string like `foo.js path/bar.md`
          output: ' '

Esta etapa usa a ação trilom/file-changes-action para reunir os arquivos alterados na solicitação de pull, para que eles possam ser analisados na próxima etapa. Este exemplo é fixado a uma versão específica da ação, usando o SHA a6ca26c14274c33b15e6499323aac178af06ad4b.

YAML
      - name: Insight into changed files
        run: |
          echo "${{ steps.get_diff_files.outputs.files }}" > get_diff_files.txt

Esta etapa executa um comando do shell que usa uma saída da etapa anterior para criar um arquivo contendo a lista de arquivos alterados na solicitação de pull.

YAML
      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16.14.x
          cache: npm

Essa etapa usa a ação actions/setup-node para instalar a versão especificada do pacote de software node no executor, o que permite que você acesse o comando npm.

YAML
      - name: Install dependencies
        run: npm ci

Esta etapa executa o comando do shell npm ci para instalar os pacotes de software npm do projeto.

YAML
      - name: Cache nextjs build
        uses: actions/cache@v3
        with:
          path: .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('package*.json') }}

Esta etapa usa a ação actions/cache para armazenar em cache o build Next.js, para que o fluxo de trabalho tente recuperar um cache do build e não recriá-lo do zero sempre. Para obter mais informações, confira "Como armazenar dependências em cache para acelerar os fluxos de trabalho".

YAML
      - name: Run build script
        run: npm run build

Esta etapa executa o script de build.

YAML
      - name: Run tests
        env:
          DIFF_FILE: get_diff_files.txt
          CHANGELOG_CACHE_FILE_PATH: tests/fixtures/changelog-feed.json
        run: npm test -- tests/${{ matrix.test-group }}/

Esta etapa executa os testes usando npm teste a matriz de teste fornece um valor diferente de ${{ matrix.test-group }} para cada trabalho na matriz. Ela usa a variável de ambiente DIFF_FILE para saber quais arquivos foram alterados e usa a variável de ambiente CHANGELOG_CACHE_FILE_PATH para o arquivo de cache do changelog.

Próximas etapas