Skip to main content

Utilizar concurrencia, expresiones y una matriz de pruebas

Cómo utilizar características avanzadas de GitHub Actions para la integración continua (IC).

Nota: Los ejecutores hospedados en GitHub no son compatibles con GitHub Enterprise Server actualmente. Puedes encontrar más información sobre el soporte que se tiene planeado en el futuro en el Itinerario público de GitHub.

Resumen de ejemplo

This article uses an example workflow to demonstrate some of the main CI features of GitHub Actions. When this workflow is triggered, it tests your code using a matrix of test combinations with npm test.

The following diagram shows a high level view of the workflow's steps and how they run within the job:

Diagrama de resumen de los pasos del flujo de trabajo

Características utilizadas en este ejemplo

The example workflow demonstrates the following capabilities of GitHub Actions:

CaracterísticaImplementación
Manually running a workflow from the UI:workflow_dispatch
Triggering a workflow to run automatically:pull_request
Running a workflow at regular intervals:schedule
Setting permissions for the token:permissions
Controlling how many workflow runs or jobs can run at the same time:concurrency
Ejecutar el job en ejecutores diferentes, dependiendo del repositorio:runs-on
Preventing a job from running unless specific conditions are met:if
Using a matrix to create different test configurations:matrix
Cloning your repository to the runner:actions/checkout
Installing node on the runner:actions/setup-node
Caching dependencies:actions/cache

Ejemplo de flujo de trabajo

The following workflow was created by the GitHub Docs Engineering team. To review the latest version of this file in the github/docs repository, see test.yml.

Note: Each line of this workflow is explained in the next section at "Understanding the example."

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

Cómo entender el ejemplo

 The following table explains how each of these features are used when creating a GitHub Actions workflow.

Código Explicación
YAML
name: Node.js Tests

The name of the workflow as it will appear in the "Actions" tab of the GitHub repository.

YAML
on:

La palabra clave on te permite definir los eventos que se activan cuando se ejecuta el flujo de trabajo. Puedes definir eventos múltiples aquí. Para obtener más información, consulta la sección "Activar un flujo de trabajo".

YAML
  workflow_dispatch:

Add the workflow_dispatch event if you want to be able to manually run this workflow in the UI. For more information, see workflow_dispatch.

YAML
  pull_request:

Agrega el evento pull_request para que el flujo de trabajo se ejecute automáticamente cada que se cree o actualice una solicitud de cambios. Para obtener más información, consulta 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 obtener más información, consulta push.

YAML
permissions:
  contents: read
  pull-requests: read

Modifica los permisos predeterminados que se otorgan al GITHUB_TOKEN. Esto variará dependiendo de las necesidades de tu flujo de trabajo. Para obtener más información, consulta la sección "Asignar permisos a los jobs".

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

Crea un grupo de concurrencia para los eventos específicos y utiliza el operador || para definir los valores de reserva. Para obtener más información, consulta la sección "Utilizar concurrencia".

YAML
  cancel-in-progress: true

Cancela cualquier job o flujo de trabajo concurrentes en el mismo grupo de concurrencia.

YAML
jobs:

Agrupa todos los jobs que se ejecutan en el archivo de flujo de trabajo.

YAML
  test:

Defines a job with the ID test that is stored within the jobs key.

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

Configura el job para ejecutarse en un ejecutor hospedado en GitHub o en un ejecutor auto-hospedado, dependiendo del repositorio que ejecuta el flujo de trabajo. En este ejemplo, el job se ejecutará en un ejecutor auto-hospedado si se nombra al repositorio docs-internal y está dentro de la organización github. Si el repositorio no empata con esta ruta, entonces se ejecutará en un ejecutor ubuntu-latest hospedado por GitHub. Para obtener más información sobre estas opciones, consulta la sección "Elegir el ejecutor para un job".

YAML
    timeout-minutes: 60

Sets the maximum number of minutes to let the job run before it is automatically canceled. For more information, see timeout-minutes.

YAML
    strategy:
This section defines the build matrix for your jobs.
YAML
      fail-fast: false

Setting fail-fast to false prevents GitHub from cancelling all in-progress jobs if any matrix job fails.

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

Creates a matrix named test-group, with an array of test groups. These values match the names of test groups that will be run by npm test.

YAML
    steps:

Groups together all the steps that will run as part of the test job. Cada job en un flujo de trabajo tiene su propia sección de steps.

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

La palabra clave uses le indica al job recuperar la acción llamada actions/checkout. Esta es una acción que revisa tu repositorio y lo descarga al ejecutor, lo que te permite ejecutar acciones contra tu código (tales como las herramientas de prueba). Debes utilizar la acción de verificación cada que tu flujo de trabajo se ejecute contra el código del repositorio o cada que estés utilizando una acción definida en el repositorio. Some extra options are provided to the action using the with key.

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
            }

If the current repository is the github/docs-internal repository, this step uses the actions/github-script action to run a script to check if there is a branch called 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 }}

If the current repository is the github/docs-internal repository, this step checks out the branch from the github/docs-early-access that was identified in the previous step.

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

If the current repository is the github/docs-internal repository, this step uses the run keyword to execute shell commands to move the docs-early-access repository's folders into the main repository's folders.

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

This step runs a command to check out LFS objects from the repository.

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: ' '

This step uses the trilom/file-changes-action action to gather the files changed in the pull request, so they can be analyzed in the next step. Este ejemplo se fija a una versión específica de la acción utilizando el SHA a6ca26c14274c33b15e6499323aac178af06ad4b.

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

This step runs a shell command that uses an output from the previous step to create a file containing the list of files changed in the pull request.

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

Este paso utiliza la acción actions/setup-node para instalar la versión especificada del paquete de software node en el ejecutor, lo cuál te da acceso al comando npm.

YAML
      - name: Install dependencies
        run: npm ci

This step runs the npm ci shell command to install the npm software packages for the project.

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

This step uses the actions/cache action to cache the Next.js build, so that the workflow will attempt to retrieve a cache of the build, and not rebuild it from scratch every time. For more information, see "Caching dependencies to speed up workflows."

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

This step runs the build script.

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

This step runs the tests using npm test, and the test matrix provides a different value for ${{ matrix.test-group }} for each job in the matrix. It uses the DIFF_FILE environment variable to know which files have changed, and uses the CHANGELOG_CACHE_FILE_PATH environment variable for the changelog cache file.

Pasos siguientes