Tabla de Contenidos
8. gh CLI
gh es la herramienta de línea de órdenes de GitHub: hace desde la terminal lo que se hace en la web de GitHub. No es para el repositorio: commits, ramas, push y pull siguen siendo cosa de git. gh trabaja con lo que GitHub pone alrededor del repositorio:
gh issue: crear, listar y ver los issues.gh project: el tablero y sus tarjetas.gh runygh workflow: los workflows de GitHub Actions.
Más información:
- GitHub CLI manual: todas las órdenes con sus opciones.
Instalación
Se instala con:
- Windows:
winget install GitHub.cli(y reabrir la terminal). - Linux:
sudo apt install gh. - Mac:
brew install gh.
gh auth login # GitHub.com, HTTPS, navegador. Guarda también la credencial de git gh auth status # «Logged in to github.com» gh auth refresh -s project # permiso para mover las tarjetas
Las órdenes de gh se lanzan dentro de la carpeta del repositorio (o de un worktree): de ahí saca de qué repositorio de GitHub se habla.
Issues
GitHub ya trae la etiqueta bug en cada repositorio; feature se crea una vez:
gh label create feature
- Crear un issue. Responde con su URL, que acaba en su número:
gh issue create --title "Alta de clientes" --body "Formulario de alta con nombre, NIF y email." --label feature
- Listar los issues abiertos:
gh issue list
- Ver el título, la descripción y el estado del issue 12:
gh issue view 12
- Ponerle una etiqueta:
gh issue edit 12 --add-label bug
- Cerrarlo:
gh issue close 12
gh issue create falla y no crea el issue.
Workflows
gh workflow run desplegar.yml
- Seguir en directo el workflow que se acaba de lanzar:
gh run watch
- Ver las últimas ejecuciones, con su id:
gh run list --limit 5
- Ver solo los pasos que han fallado de una ejecución:
gh run view 123456 --log-failed
Un workflow rojo tiene un log largo y la causa está en tres líneas: --log-failed las da sin tener que buscarlas en la web.
GitHub Projects
Un Project es un tablero de GitHub con los issues del repositorio como tarjetas, repartidas en columnas por su estado: Todo, In Progress y Done. Está en GitHub, en el menú Projects.
Crear el tablero
Uno por proyecto, desde el menú + de arriba a la derecha de GitHub → New project:
- En Start from scratch, la plantilla Board.
- El nombre del proyecto y, marcado, Import items from repository con el repositorio del proyecto. Así el Project queda vinculado al repositorio, que es de donde sacan su número los scripts, y los issues abiertos entran en el tablero. Create project.
- Sale con las tres columnas, Todo, In Progress y Done, las que usan los scripts.
Scripts para los issues
Mover una tarjeta con gh project necesita varios ids (el del Project, el del campo Status, el de la columna y el de la tarjeta), así que se han creado 3 scripts que lo hacen. Van en la carpeta scripts/ del proyecto. El número del Project no se configura en ningún sitio: los tres le preguntan a GitHub cuál está vinculado al repositorio. Por eso se vincula al crearlo (arriba). Por lo mismo, se lanzan desde la carpeta del proyecto o un worktree: fuera de él no saben de qué repositorio hablan.
- Crear un issue: lo crea, lo añade al Project (en Todo) y responde con su número. La etiqueta, si no se da, es
feature.
#!/bin/bash
# Uso: issue_create.sh "título" "descripción" [feature|bug]
PROJECT=$(gh api graphql -F owner='{owner}' -F repo='{repo}' -f query='query($owner:String!,$repo:String!){repository(owner:$owner,name:$repo){projectsV2(first:1){nodes{number}}}}' --jq '.data.repository.projectsV2.nodes[0].number')
URL=$(gh issue create --title "$1" --body "$2" --label "${3:-feature}")
gh project item-add $PROJECT --owner @me --url $URL > /dev/null 2>&1 # si ya lo ha añadido GitHub, da igual
echo $URL | grep -o '[0-9]*$'
scripts/issue_create.sh "Alta de clientes" "Formulario de alta con nombre, NIF y email." scripts/issue_create.sh "El total del carrito no suma los gastos de envío" "Con un pedido de 30 € sale 30 € en vez de 34,95 €." bug
- Empezar un issue: pasa su tarjeta a In Progress.
#!/bin/bash
# Uso: issue_in_progress.sh N
PROJECT=$(gh api graphql -F owner='{owner}' -F repo='{repo}' -f query='query($owner:String!,$repo:String!){repository(owner:$owner,name:$repo){projectsV2(first:1){nodes{number}}}}' --jq '.data.repository.projectsV2.nodes[0].number')
N=$1
PROJECT_ID=$(gh project view $PROJECT --owner @me --format json --jq .id)
CAMPO=$(gh project field-list $PROJECT --owner @me --format json --jq '.fields[] | select(.name=="Status") | .id')
OPCION=$(gh project field-list $PROJECT --owner @me --format json --jq '.fields[] | select(.name=="Status") | .options[] | select(.name=="In Progress") | .id')
TARJETA=$(gh project item-list $PROJECT --owner @me --limit 1000 --format json --jq ".items[] | select(.content.number==$N) | .id")
if [ -z "$TARJETA" ]; then echo "El issue #$N no está en el Project"; exit 1; fi
gh project item-edit --project-id $PROJECT_ID --field-id $CAMPO --single-select-option-id $OPCION --id $TARJETA > /dev/null || { echo "No se ha podido mover la tarjeta del issue #$N"; exit 1; }
echo "Issue #$N en In Progress"
scripts/issue_in_progress.sh 12
- Cerrar un issue: lo cierra y pasa su tarjeta a Done.
#!/bin/bash
# Uso: issue_close.sh N
PROJECT=$(gh api graphql -F owner='{owner}' -F repo='{repo}' -f query='query($owner:String!,$repo:String!){repository(owner:$owner,name:$repo){projectsV2(first:1){nodes{number}}}}' --jq '.data.repository.projectsV2.nodes[0].number')
N=$1
PROJECT_ID=$(gh project view $PROJECT --owner @me --format json --jq .id)
CAMPO=$(gh project field-list $PROJECT --owner @me --format json --jq '.fields[] | select(.name=="Status") | .id')
OPCION=$(gh project field-list $PROJECT --owner @me --format json --jq '.fields[] | select(.name=="Status") | .options[] | select(.name=="Done") | .id')
TARJETA=$(gh project item-list $PROJECT --owner @me --limit 1000 --format json --jq ".items[] | select(.content.number==$N) | .id")
if [ -z "$TARJETA" ]; then echo "El issue #$N no está en el Project"; exit 1; fi
gh issue close $N > /dev/null 2>&1
gh project item-edit --project-id $PROJECT_ID --field-id $CAMPO --single-select-option-id $OPCION --id $TARJETA > /dev/null || { echo "No se ha podido mover la tarjeta del issue #$N"; exit 1; }
echo "Issue #$N cerrado y en Done"
scripts/issue_close.sh 12
Ejercicios
Ejercicio 1
- Instala
ghy autentícate. Comprueba congh auth statusque está el permisoproject. - En tu repositorio de Java, crea la etiqueta
featurey un issue «Desplegar con GitHub Actions» con la etiquetafeature. Comprueba congh issue listque sale.
Ejercicio 2
- Crea el tablero del repositorio como en Crear el tablero y comprueba en la pestaña Projects que solo hay un Project vinculado.
- Copia los tres scripts en la carpeta
scripts/y dales permiso de ejecución. - Crea un issue con
issue_create.sh, pásalo a In Progress conissue_in_progress.shy ciérralo conissue_close.sh. Después de cada paso, mira en el tablero que la tarjeta está en su columna.




