Skip to main content

Utilizar el CLI de GitHub en un ejecutor

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

Resumen de ejemplo

This article uses an example workflow to demonstrate some of the main CI features of GitHub Actions. Cuando se activa este flujo de trabajo, este ejecuta automáticamente un script que verifica si el sitio de GitHub Docs tiene enlaces rotos. Si se encuentra algún enlace roto, el flujo de trabajo utiliza el CLI de GitHub para crear una propuesta de GitHub con los detalles.

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
Running a workflow at regular intervals:schedule
Setting permissions for the token:permissions
Preventing a job from running unless specific conditions are met:if
Referencing secrets in a workflow:Secrets
Cloning your repository to the runner:actions/checkout
Installing node on the runner:actions/setup-node
Using a third-party action:peter-evans/create-issue-from-file

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 check-all-english-links.yml.

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

YAML
name: Check all English links

# **What it does**: This script once a day checks all English links and reports in issues.
# **Why we have it**: We want to know if any links break.
# **Who does it impact**: Docs content.

on:
  workflow_dispatch:
  schedule:
    - cron: '40 19 * * *' # once a day at 19:40 UTC / 11:40 PST

permissions:
  contents: read
  issues: write

jobs:
  check_all_english_links:
    name: Check all links
    if: github.repository == 'github/docs-internal'
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
      FIRST_RESPONDER_PROJECT: Docs content first responder
      REPORT_AUTHOR: docubot
      REPORT_LABEL: broken link report
      REPORT_REPOSITORY: github/docs-content
    steps:
      - name: Check out repo's default branch
        uses: actions/checkout@v2
      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: 16.13.x
          cache: npm
      - name: npm ci
        run: npm ci
      - name: npm run build
        run: npm run build
      - name: Run script
        run: |
          script/check-english-links.js > broken_links.md

      # check-english-links.js returns 0 if no links are broken, and 1 if any links
      # are broken. When an Actions step's exit code is 1, the action run's job status
      # is failure and the run ends. The following steps create an issue for the
      # broken link report only if any links are broken, so `if: ${{ failure() }}`
      # ensures the steps run despite the previous step's failure of the job.

      - if: ${{ failure() }}
        name: Get title for issue
        id: check
        run: echo "::set-output name=title::$(head -1 broken_links.md)"
      - if: ${{ failure() }}
        name: Create issue from file
        id: broken-link-report
        uses: peter-evans/create-issue-from-file@b4f9ee0a9d4abbfc6986601d9b1a4f8f8e74c77e
        with:
          token: ${{ env.GITHUB_TOKEN }}

          title: ${{ steps.check.outputs.title }}
          content-filepath: ./broken_links.md
          repository: ${{ env.REPORT_REPOSITORY }}
          labels: ${{ env.REPORT_LABEL }}
      - if: ${{ failure() }}
        name: Close and/or comment on old issues
        env:
          NEW_REPORT_URL: 'https://github.com/${{ env.REPORT_REPOSITORY }}/issues/${{ steps.broken-link-report.outputs.issue-number }}'
        run: |
          gh alias set list-reports "issue list \
                                       --repo ${{ env.REPORT_REPOSITORY }} \
                                       --author ${{ env.REPORT_AUTHOR }} \
                                       --label '${{ env.REPORT_LABEL }}'"

          # Link to the previous report from the new report that triggered this
          # workflow run.

          previous_report_url=$(gh list-reports \
                                  --state all \
                                  --limit 2 \
                                  --json url \
                                  --jq '.[].url' \
                                  | grep -v ${{ env.NEW_REPORT_URL }} | head -1)

          gh issue comment ${{ env.NEW_REPORT_URL }} --body "⬅️ [Previous report]($previous_report_url)"

          # If an old report is open and assigned to someone, link to the newer
          # report without closing the old report.

          for issue_url in $(gh list-reports \
                                  --json assignees,url \
                                  --jq '.[] | select (.assignees != []) | .url'); do
            if [ "$issue_url" != "${{ env.NEW_REPORT_URL }}" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report](${{ env.NEW_REPORT_URL }})"
            fi
          done

          # Link to the newer report from any older report that is still open,
          # then close the older report and remove it from the first responder's
          # project board.

          for issue_url in $(gh list-reports \
                                  --search 'no:assignee' \
                                  --json url \
                                  --jq '.[].url'); do
            if [ "$issue_url" != "${{ env.NEW_REPORT_URL }}" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report](${{ env.NEW_REPORT_URL }})"
              gh issue close $issue_url
              gh issue edit $issue_url --remove-project "${{ env.FIRST_RESPONDER_PROJECT }}"
            fi
          done

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: Check all English links

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

YAML
on:
  workflow_dispatch:
  schedule:
    - cron: '40 20 * * *' # once a day at 20:40 UTC / 12:40 PST

Define a workflow_dispatch y a scheduled como activadores para el flujo de trabajo:

  • El workflow_dispatch te permite ejecutar manualmente este flujo de trabajo desde la IU. Para obtener más información, consulta workflow_dispatch.
  • El evento schedule te permite utilizar la sintaxis de cron para definir un intervalo regular para activar el flujo de trabajo automáticamente. Para obtener más información, consulta schedule.
YAML
permissions:
  contents: read
  issues: write

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

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

YAML
  check_all_english_links:
    name: Check all links

Define un job con la ID check_all_english_links y con el nombre Check all links, lo cual se almacena dentro de la clave jobs.

YAML
if: github.repository == 'github/docs-internal'

Solo ejecuta el job check_all_english_links si el repositorio se llama docs-internal y se encuentra dentro de la organización github. De otra forma, el job se marca como skipped.

YAML
runs-on: ubuntu-latest

Configura el job para ejecutarse en un ejecutor Ubuntu Linux. Esto significa que el job se ejecutará en una máquina virtual nueva hospedada en GitHub. Para obtener ejemplos de sintaxis utilizando otros ejecutores, consulta la sección "Sintaxis de flujo de trabajo para las GitHub Actions".

YAML
    env:
      GITHUB_TOKEN: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
      REPORT_AUTHOR: docubot
      REPORT_LABEL: broken link report
      REPORT_REPOSITORY: github/docs-content

Crea variables de ambiente personalizadas y redefine la variable integrada GITHUB_TOKEN para utilizar un [secret] personalizado(/actions/security-guides/encrypted-secrets). Estas variables se referenciarán más adelante en el flujo de trabajo.

YAML
    steps:

Agrupa todos los pasos que se ejecutarán como parte del job check_all_english_links. Cada job en el flujo de trabajo tiene su propia sección de steps.

YAML
      - name: Check out repo's default branch
        uses: actions/checkout@v2

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.

YAML
      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: 16.8.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: Run the "npm ci" command
        run: npm ci
      - name: Run the "npm run build" command
        run: npm run build

La palabra clave run le indica al job ejecutar un comando en el ejecutor. En este caso, los comandos npm ci y npm run build se ejecutan como pasos separados para instalar y compilar la aplicación de Node.js en el repositorio.

YAML
      - name: Run script
        run: |
          script/check-english-links.js > broken_links.md

Este comando de run ejecuta un script que se almacena en el repositorio en script/check-english-links.js y enlaza la salida con un archivo llamado broken_links.md.

YAML
      - if: ${{ failure() }}
        name: Get title for issue
        id: check
        run: echo "::set-output name=title::$(head -1 broken_links.md)"

Si el script check-english-links.js detecta enlaces rotos y se devuelve a un estado de salida no-cero (falla), entonces utiliza un comando de flujo de trabajo para configurar una salida que tiene el valor de la primera línea del archivo broken_links.md (este se utiliza en el siguiente paso).

YAML
      - if: ${{ failure() }}
        name: Create issue from file
        id: broken-link-report
        uses: peter-evans/create-issue-from-file@b4f9ee0a9d4abbfc6986601d9b1a4f8f8e74c77e
        with:
          token: ${{ env.GITHUB_TOKEN }}

          title: ${{ steps.check.outputs.title }}
          content-filepath: ./broken_links.md
          repository: ${{ env.REPORT_REPOSITORY }}
          labels: ${{ env.REPORT_LABEL }}

Utiliza la acción peter-evans/create-issue-from-file para crear una propuesta nueva de GitHub. Este ejemplo está fijado a una versión específica de la acción, utilizando el SHA b4f9ee0a9d4abbfc6986601d9b1a4f8f8e74c77e.

YAML
      - if: ${{ failure() }}
        name: Close and/or comment on old issues
        env:
          NEW_REPORT_URL: 'https://github.com/${{ env.REPORT_REPOSITORY }}/issues/${{ steps.broken-link-report.outputs.issue-number }}'
        run: |
          gh alias set list-reports "issue list \
                                       --repo ${{ env.REPORT_REPOSITORY }} \
                                       --author ${{ env.REPORT_AUTHOR }} \
                                       --label '${{ env.REPORT_LABEL }}'"
          previous_report_url=$(gh list-reports \
                                  --state all \
                                  --limit 2 \
                                  --json url \
                                  --jq '.[].url' \
                                  | grep -v ${{ env.NEW_REPORT_URL }} | head -1)

          gh issue comment ${{ env.NEW_REPORT_URL }} --body "⬅️ [Previous report]($previous_report_url)"

Utiliza gh issue list para ubicar la propuesta que se creó anteriormente a partir de ejecuciones anteriores. Esto se pone en aliased como gh list-reports para procesarse de forma más simple en pasos subsecuentes. Para obtener la URL de la propuesta, la expresión jq procesa la salida JSON resultante.

Entonces se utiliza gh issue comment para agregar un comentario a la propuesta nueva que enlaza a la anterior.

YAML
          for issue_url in $(gh list-reports \
                                  --json assignees,url \
                                  --jq '.[] | select (.assignees != []) | .url'); do
            if [ "$issue_url" != "$" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report]($)"
            fi
          done

Si se abre una propuesta de una ejecución anterior y se le asigna a alguien, utiliza entonces [gh issue comment] (https://cli.github.com/manual/gh_issue_comment) para agregar un comentario con un enlace a la propuesta nueva.

YAML
          for issue_url in $(gh list-reports \
                                  --search 'no:assignee' \
                                  --json url \
                                  --jq '.[].url'); do
            if [ "$issue_url" != "${{ env.NEW_REPORT_URL }}" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report](${{ env.NEW_REPORT_URL }})"
              gh issue close $issue_url
              gh issue edit $issue_url --remove-project "${{ env.FIRST_RESPONDER_PROJECT }}"
            fi
          done

Si la propuesta de una ejecución previa está abierta y no está asignada a nadie, entonces:

  • Utiliza gh issue comment para agregar un comentario con un enlace a la propuesta nueva.
  • Utiliza gh issue close para cerrar la propuesta antigua.
  • Utiliza gh issue edit para editar la propuesta antigua para eliminarla de un tablero de proyecto de GitHub específico.

Pasos siguientes