Using GitHub CLI with GitHub Actions for GitHub Project automation

I’ve been working on GitHub Projects automation using the GitHub CLI and GitHub Actions. In this blog post, I will show how you can also do this.

What is GitHub CLI?

GitHub CLI is a command-line tool that brings GitHub to your “terminal”, allowing you to manage a number of GitHub related tasks

GitHub CLI, or gh, is a command-line interface to GitHub for use in your terminal or your scripts.

https://cli.github.com/manual/

I have found it super powerful. It is not just for project automation. For more information and documentation, visit the official GitHub CLI manual: https://cli.github.com/manual/

Why use GitHub CLI with GitHub Actions for project automation?

You may be wondering why this approach when there is buit-in workflows?

I prefer using GitHub CLI with GitHub Actions for several reasons:

  1. Full customisation of the overall project workflow
  2. Greater flexibility
  3. Ability to integrate with other workflows

Moving Issues to a Project Board with GitHub Actions

I will show one example to showcase a foundation of what can be done, its great. (In following blog posts, I will include others as I feel this will be a very lengthy blog post 🙂 )

  • Move issues to project and add to “ToDo” column / status field

I do recommend checking out this article on customising boards, ToDo is a status field within my project:

Here is a snippet from my GitHub project board, that both GitHub workflows above will reference:

Move issues to project and add to “ToDo” column / status field

Lets start by moving an issue to my project when a new issue has been opened.

Within my GitHub actions workflow, I make use of 4 environment variables:

      GITHUB_TOKEN: ${{ secrets.SECRET_PAT }}
      ISSUE_URL: ${{ github.event.issue.html_url }}
      PROJECT_OWNER: thomasthorntoncloud
      PROJECT_NUMBER: 1
  • GITHUB_TOKEN – PAT token with permissions project, read:discussion, read:org, repo
  • ISSUE_URL – This will retrieve the issue URL
  • PROJECT_OWNER & PROJECT_NUMBER are both taken from the project URL:
https://github.com/orgs/thomasthorntoncloud/projects/1

GitHub Actions breakdown

Lets go step-by-step through the workflow, it will have 1 step:

Add issue to project Todo column.

Breakdown of the step:

1. Get the Project ID

gh project view to get the Project ID of the associated project I want the issue to be added to:

  • Uses the GitHub CLI (gh) to get the project details in JSON format
  • Extracts the project ID using jq and stores it in the PROJECT_ID variable.
          # Get project ID
          PROJECT_ID=$(gh project view $PROJECT_NUMBER --owner $PROJECT_OWNER --format json | jq -r '.id')
          echo "Project ID: $PROJECT_ID"

2. Add issue to project:

  • Uses the GitHub CLI (gh) to add the issue to the project.
  • Retrieves the item ID of the added issue and stores it in the ITEM_ID variable.
  • Error handling: Checks if the ITEM_ID is empty. It prints an error message and exits if the issue was not successfully added to the project.
          # Add issue to project
          echo "Adding issue to project..."
          ITEM_ID=$(gh project item-add $PROJECT_NUMBER --owner "$PROJECT_OWNER" --url "$ISSUE_URL" --format json | jq -r '.id')
          echo "ITEM_ID: $ITEM_ID"

          if [ -z "$ITEM_ID" ]; then
            echo "Error: Failed to add issue to project."
            exit 1
          fi

3. Retrieves the IDs for the “Status” field and the “Todo” option of my GitHub project.

  • Fetch the project fields: GitHub CLI (gh) to get the project fields in JSON format.
  • Extracts IDs:Extracts the ID of the “Status” field and the ID of the “Todo” option within that field using jq.
  • Error handling: Checks if either ID is missing and prints an error message, then exits if they are not found.
      # Get the Status field ID and Todo option ID
      echo "Fetching project fields..."
      FIELDS=$(gh project field-list $PROJECT_NUMBER --owner "$PROJECT_OWNER" --format json)
      STATUS_FIELD=$(echo "$FIELDS" | jq -r '.fields[] | select(.name == "Status")')
      STATUS_FIELD_ID=$(echo "$STATUS_FIELD" | jq -r '.id')
      TODO_OPTION_ID=$(echo "$STATUS_FIELD" | jq -r '.options[] | select(.name == "Todo") | .id')

      echo "STATUS_FIELD_ID: $STATUS_FIELD_ID"
      echo "TODO_OPTION_ID: $TODO_OPTION_ID"

      if [ -z "$STATUS_FIELD_ID" ] || [ -z "$TODO_OPTION_ID" ]; then
        echo "Error: Could not find Status field or Todo option."
        exit 1
      fi

4. Setting the issue to “ToDo” in my GitHub project:

  • Set the Status to ToDo: Uses the GitHub CLI (gh) to edit the project item, setting the status field to the “Todo” option.
  • Verification: Checks if the result contains a valid ID & prints an error message and exits if the update failed
          # Set the Status to Todo
          echo "Setting issue status to Todo..."
          RESULT=$(gh project item-edit $PROJECT_NUMBER \
            --id "$ITEM_ID" \
            --project-id "$PROJECT_ID" \
            --field-id "$STATUS_FIELD_ID" \
            --single-select-option-id "$TODO_OPTION_ID" \
            --format json)

          echo "RESULT: $RESULT"

          if echo "$RESULT" | jq -e '.id' > /dev/null; then
            echo "Issue successfully added to project and set to Todo status."
          else
            echo "Error updating issue status: $RESULT"
            exit 1
          fi

The full workflow:

name: Add Issue to Project Todo Column

on:
  issues:
    types: [opened]

jobs:
  add-to-project:
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.SECRET_PAT }}
      ISSUE_URL: ${{ github.event.issue.html_url }}
      PROJECT_OWNER: thomasthorntoncloud
      PROJECT_NUMBER: 1
    steps:
      - name: Add issue to project Todo column
        run: |
          # Get project ID
          PROJECT_ID=$(gh project view $PROJECT_NUMBER --owner $PROJECT_OWNER --format json | jq -r '.id')
          echo "Project ID: $PROJECT_ID"

          # Add issue to project
          echo "Adding issue to project..."
          ITEM_ID=$(gh project item-add $PROJECT_NUMBER --owner "$PROJECT_OWNER" --url "$ISSUE_URL" --format json | jq -r '.id')
          echo "ITEM_ID: $ITEM_ID"

          if [ -z "$ITEM_ID" ]; then
            echo "Error: Failed to add issue to project."
            exit 1
          fi

          # Get the Status field ID and Todo option ID
          echo "Fetching project fields..."
          FIELDS=$(gh project field-list $PROJECT_NUMBER --owner "$PROJECT_OWNER" --format json)
          STATUS_FIELD=$(echo "$FIELDS" | jq -r '.fields[] | select(.name == "Status")')
          STATUS_FIELD_ID=$(echo "$STATUS_FIELD" | jq -r '.id')
          TODO_OPTION_ID=$(echo "$STATUS_FIELD" | jq -r '.options[] | select(.name == "Todo") | .id')

          echo "STATUS_FIELD_ID: $STATUS_FIELD_ID"
          echo "TODO_OPTION_ID: $TODO_OPTION_ID"

          if [ -z "$STATUS_FIELD_ID" ] || [ -z "$TODO_OPTION_ID" ]; then
            echo "Error: Could not find Status field or Todo option."
            exit 1
          fi

          # Set the Status to Todo
          echo "Setting issue status to Todo..."
          RESULT=$(gh project item-edit $PROJECT_NUMBER \
            --id "$ITEM_ID" \
            --project-id "$PROJECT_ID" \
            --field-id "$STATUS_FIELD_ID" \
            --single-select-option-id "$TODO_OPTION_ID" \
            --format json)

          echo "RESULT: $RESULT"

          if echo "$RESULT" | jq -e '.id' > /dev/null; then
            echo "Issue successfully added to project and set to Todo status."
          else
            echo "Error updating issue status: $RESULT"
            exit 1
          fi

Now when I create a new issue the workflow kicks off:

1. New issue is opened:

Screenshot of GitHub Issue opened

2. Within GitHub Actions, we can see a workflow has been triggered:

3. Successfully the issue has now been added to the project board and moved to ToDo:

Notice the movement below, adding and moving: (I am using my own PAT for this example)

Screenshot to show

Checking the project board we can see it has been successfully added

Wrapping up

By combining GitHub CLI with GitHub Actions, you can create powerful automation workflows for your projects. This example demonstrates how to automate issue management. The possibilities are many. In future blog posts, I will include other examples.

You can extend this approach to automate various parts of your project. These parts include pull request management, code reviews, and more.

Remember to always review and test your workflows thoroughly before implementing them in your projects.

GitHub repository containing example above

1 thought on “Using GitHub CLI with GitHub Actions for GitHub Project automation”

Leave a Reply

Discover more from Thomas Thornton Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading

Discover more from Thomas Thornton Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading