Skip to main content

Usando concorrência, expressões e uma matriz de teste

Como usar funcionalidades avançadas de GitHub Actions para integração contínua (IC).

Visão geral do exemplo

Este artigo usa um exemplo de fluxo de trabalho para demonstrar algumas das principais funcionalidades da CI de GitHub Actions. Quando este fluxo de trabalho é acionado, ele testa seu código usando uma matriz de combinações de teste com teste de npm.

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

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

Características utilizadas neste exemplo

O exemplo de fluxo de trabalho demonstra os seguintes recursps de GitHub Actions:

FuncionalidadeImplementação
Executando manualmente um fluxo de trabalho a partir da interface do usuário:workflow_dispatch
Acionando um fluxo de trabalho para funcionar automaticamente:pull_request
Executando um fluxo de trabalho em intervalos regulares:cronograma
Definindo permissões para o token:permissões
Controlando quantas execuções de fluxo de trabalho ou trabalhos podem ser executados ao mesmo tempo:concorrência
Executando o trabalho em diferentes corredores, dependendo do repositório:runs-on
Impedindo que um trabalho seja executado a menos que condições específicas sejam atendidas:se
Usando uma matriz para criar diferentes configurações de teste:matriz
Clonando o repositório para o executor:actions/checkout
Instalando o no executor:actions/setup-node
Deoendências de cache:actions/cache

Exemplo de fluxo de trabalho

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

Observação: Cada linha deste fluxo de trabalho é explicada na próxima seção em "Compreendendo 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 }}/

Entendendo o exemplo

 A tabela a seguir explica como cada um desses recursos são usados ao criar um fluxo de trabalho de GitHub Actions.

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

O nome do fluxo de trabalho como ele aparecerá na aba "Ações" do repositório de GitHub.

YAML
on:

A palavra-chave 'on' permite definir os eventos que acionam quando o fluxo de trabalho é executado. Você pode definir vários eventos aqui. Para obter mais informações, consulte "Acionando um fluxo de trabalho."

YAML
  workflow_dispatch:

Adicione o evento "workflow_dispatch" se você quiser poder executar manualmente este fluxo de trabalho na interface do usuário. Para obter mais informações, consulte 'workflow_dispatch'.

YAML
  pull_request:

Adicione o evento "pull_request", para que o fluxo de trabalho seja executado automaticamente toda vez que um pull request for criado ou atualizado. Para obter mais informações, consulte 'pull_request'.

YAML
  push:
    branches:
      - main

Add the push event, so that the workflow runs automatically every time a commit is pushed to a branch matching the filter main. Para obter mais informações, consulte 'push'.

YAML
permissions:
  contents: read
  pull-requests: read

Modifica as permissões padrão concedidas a "GITHUB_TOKEN". Isso vai variar dependendo das necessidades do seu fluxo de trabalho. Para obter mais informações, consulte "Atribuindo permissões a trabalhos."

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

Cria um grupo de concorrência para eventos específicos e usa o operador "||" para definir valores de recuo. Para obter mais informações, consulte "Usando concorrência."

YAML
  cancel-in-progress: true

Cancela qualquer trabalho ou fluxo de trabalho em execução no mesmo grupo de concorrência.

YAML
jobs:

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

YAML
  test:

Define uma trabalho com o ID "test" que é armazenado dentro da 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 em GitHub ou em um executor auto-hospedado, dependendo do repositório que executa o fluxo de trabalho. Neste exemplo, o trabalho será executado em um runner auto-hospedado se o repositório for denominado "docs-internal" e estiver dentro da organização "github". Se o repositório não corresponder a este caminho, então ele será executado em um executor "ubuntu-latest" hospedado por GitHub. Para obter mais informações sobre essas opções consulte "Escolhendo o executor para um trabalho".

YAML
    timeout-minutes: 60

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

YAML
    strategy:
Esta seção define a matriz de compilações para seus trabalhos.
YAML
      fail-fast: false

Definir "fail-fast" como "false" impede GitHub de cancelar todos os trabalhos em andamento se qualquer trabalho da matriz falhar.

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

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

YAML
    steps:

Agrupa todos os passos que serão executados como parte do trabalho test. Cada trabalho em um fluxo de trabalho tem sua própria seção "etapas".

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

A palavra-chave "uses" diz para o trabalho recuperar a ação denominada "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 tecla "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 repositório github/docs-internal, essa etapa usará a ação actions/github-script para executar um script para verificar se existe um branch denominado 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 repositório github/docs-interno, esta etapa irá 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 repositório github/docs-internl, esta etapa usará a palavra-chave 'run' para executar comandos shell para mover pastas do repositório 'docs-early-access' para pastas do repositório principal.

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

Esta etapa executa um comando para conferir objetos do 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 no pull request, para que possam ser analisados na próxima etapa. Este exemplo está 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 de shell que usa uma saída da etapa anterior para criar um arquivo que contém a lista de arquivos alterados no pull request.

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

Esta etapa usa a ação actions/setup-node para instalar a versão especificada do pacote do "node" de software no executor, que lhe dá acesso ao 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 para o projeto.

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

Este passo usa a ação 'actions/cache' para armazenar em cache o Next.js build, para que o fluxo de trabalho tente recuperar um cache da compilação e não recriá-lo do zero todas as vezes. Para obter mais informações, consulte "Dependências de cache para acelerar fluxos de trabalho".

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

Esta etapa executa o script de compilação.

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 }}/

Essa etapa executa os testes usando npm test e a matriz de teste fornece um valor diferente para ${{ 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 registro de alterações.

Próximas etapas