Optimizing GitHub Actions with GitHub GraphQL API

gitprotectteam

GitProtect Team

Posted on September 13, 2024

Optimizing GitHub Actions with GitHub GraphQL API

GitHub Actions and the GitHub GraphQL API are powerful tools for automating and optimizing workflows. GitHub Actions, released in 2018 brings CI/CD directly into the GitHub ecosystem and automates general project management using YAML files. Whereas, a 2-year earlier-released GraphQL API provides a more efficient way to fetch and manipulate data.

In this article we will examine how combining these technologies may simplify development processes, making them more efficient as well as customized to specific requirements. While we explore how to optimize GitHub Actions with GraphQL, we will also focus on the importance of automation efficiency to guarantee every workflow is as effective as possible.

Let’s analyze GraphQL API

GraphQL, which was developed by Facebook and adopted by GitHub in 2016, represents a major change in how developers query and manipulate data. Moreover, GitHub was among the first major platforms to offer a public GraphQL API, which just shows its early support for the technology.

Unlike traditional REST APIs, which frequently return a comprehensive set of data, GraphQL allows developers to request only what they need. This targeted approach to data retrieval significantly reduces response payload, which improves web application and service performance.

To understand the efficiency of GraphQL better, consider a database containing customer records. A typical customer entry, in a RESTful service, might look like this:

"Customer": {

  "id": "101010",

  "first_name": "Jack",

  "last_name": "Kowalski",

  "email": "fake_jackkowalski@gmail.com",

  "City": "Chicago",

  "State": "Illinois"

  ...
}
Enter fullscreen mode Exit fullscreen mode

If the objective is to personalize a greeting with the customer’s first name, a REST get request would return the entire customer record, including unneeded data. This is where GraphQL shines. With GraphQL, you can construct a query to fetch only the desired information:

query FindCustomerFirstName {
  customer(id: "101010") {
    first_name
  }
}
Enter fullscreen mode Exit fullscreen mode

Applying this to the GitHub GraphQL API, you should also consider a scenario where you need the ID of a specific repository:

query FindRepoID {
  repository(owner:"YOUR_ORGANIZATION_NAME", name:"REPOSITORY_NAME") {
    id
  }
}
Enter fullscreen mode Exit fullscreen mode

This query efficiently retrieves just the repository’s ID, despite the repository object containing numerous other fields, potentially over a hundred. The response to such a query would be succinct and focused:

{
  "data": {
    "repository": {
      "id": "R_ktHp3iGA"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In the context of GitHub Actions, this means that workflows can interact with GitHub data, requesting only what is necessary for each stage of the process. For example, an action may utilize a GraphQL query to determine the status of a pull request or issue, and then make decisions based on that information, such as launching a deployment or sending a message. This exact data fetching with GraphQL not only improves the efficiency of GitHub Actions, but also matches with the goal of developing efficient, responsive, and resource-conscious CI/CD pipelines.

Setting up GitHub GraphQL API with GitHub Actions

Integrating the GitHub GraphQL API with GitHub Actions enables developers to automate and optimize workflows with unmatched precision and efficiency. This configuration entails authenticating requests, constructing GraphQL queries, and managing replies using GitHub Actions.

Authenticating with a personal access token

Generate a Personal Access Token (PAT):

  • Navigate to your GitHub settings.
  • Go to the Developer settings → Personal access tokens → Generate new token.
  • Select the scopes or permissions you need for your GraphQL queries. For most applications involving repository data, repo scope suffices.
  • Generate the token and make sure to copy it; GitHub will not show it again.

Store your PAT in GitHub Actions:

  • In your repository, go to Settings → Secrets.
  • Click on “New repository secret.”
  • Name your secret (e.g., GH_GRAPHQL_TOKEN) and paste your Personal Access Token.
  • Your token is now securely stored and can be referenced in your GitHub Actions workflows.

Sample GraphQL Query

To use the GitHub GraphQL API within a GitHub Action, you’ll need to craft a GraphQL query. For example, to fetch the ID of a repository:

query {
  repository(owner:"OWNER_NAME", name:"REPO_NAME") {
    id
  }
}
Enter fullscreen mode Exit fullscreen mode

Incorporate the Query in GitHub Actions

Here’s how to use the above query within a GitHub Action workflow:

  1. Create a workflow file:
  • In your repository, navigate to the .github/workflows directory.
  • Create a new YAML file for your workflow (e.g., graphql_query.yml).
  1. Define your workflow:
  • Start with the basic structure of a workflow, specifying the name and the trigger event.
name: Fetch Repository ID
on: [push]

jobs:
  fetch-repo-id:
    runs-on: ubuntu-latest
    steps:
      - name: Query GitHub GraphQL API
        id: fetch_data
        uses: actions/github-script@v5
        with:
          github-token: ${{ secrets.GH_GRAPHQL_TOKEN }}
          script: |
            const query = `
              query {
                repository(owner:"OWNER_NAME", name:"REPO_NAME") {
                  id
                }
              }`;
            const response = await github.graphql(query);
            return response.repository.id;
        env:
          GH_GRAPHQL_TOKEN: ${{ secrets.GH_GRAPHQL_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Use the output of the GraphQL query in subsequent steps of your workflow. For example, you can echo the repository ID or use it in further automation logic.

 - name: Output Repository ID
        run: echo "The Repository ID is ${{ steps.fetch_data.outputs.result }}"
Enter fullscreen mode Exit fullscreen mode

Advanced GraphQL Queries for GitHub Actions

Optimizing GitHub Actions workflows involves more than automating tasks. One of the key strengths of the GitHub GraphQL API is its ability to fetch exactly what’s needed in a single request, as opposed to making multiple calls to a REST API. This capability can significantly reduce the computational load and speed up the execution of GitHub Actions.

Reduce API calls

Every API call in a GitHub Action can add delay and increase the likelihood of exceeding rate limits. By using advanced GraphQL queries, you may combine numerous REST API calls into a single GraphQL call. For example, rather than making multiple REST API calls to collect the data, comments, and status of a pull request, a single GraphQL query can do so all at once.

Example: Fetching Pull Request details

query {
  repository(owner: "OWNER_NAME", name: "REPO_NAME") {
    pullRequest(number: 123) {
      title
      body
      comments(first: 5) {
        nodes {
          body
        }
      }
      merged
      mergeable
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Precisely fetch data

The specificity of GraphQL is not just about efficiency; it’s also about getting precisely what your workflow needs to make decisions or perform actions. This precision prevents your GitHub Actions from downloading and processing unnecessary data, thus optimizing performance.

*Example: Conditional workflow execution
*

Imagine a workflow that should only proceed if certain conditions are met, such as a pull request being labeled with “review needed”. A GraphQL query can check for this condition by fetching only the labels of a pull request:

query {
  repository(owner: "OWNER_NAME", name: "REPO_NAME") {
    pullRequest(number: 123) {
      labels(first: 10) {
        nodes {
          name
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Best practices

When working with lists (e.g., comments, issues), use GraphQL’s pagination features to limit the amount of data returned in a single query.

Be mindful of GitHub’s rate limits. Advanced queries, while efficient, can quickly consume your rate limit if not optimized. Use the rateLimit field to monitor your usage.

It’s important to regularly review and optimize your queries.

Authenticating with the GitHub App

When automating and optimizing workflows with GitHub Actions and the GraphQL API, using a GitHub App for authentication offers several distinct advantages over personal access tokens (PATs). GitHub Apps provide a more secure and flexible way to interact with GitHub’s ecosystem, making them ideal for complex automation tasks.

*Benefits of GitHub App authentication:
*

  • Granular permissions: GitHub Apps allow for more precise control over permissions than PATs. You can specify exactly what actions your app can perform, reducing security risks by sticking to the principle of least privilege.
  • Better security: Authentication via a GitHub App uses a dynamic installation access token, which changes periodically, offering better security compared to a static PAT. This token reduces the risk if a token were ever to be compromised.
  • Per-repository installation: GitHub Apps can be installed on individual repositories or across an entire organization, providing flexibility in scope and application. This means actions can be tailored and restricted to specific repositories, further enhancing security and efficiency.

How to set up GitHub App

Go to your GitHub account or organization settings, find the GitHub Apps section under Developer settings, and click on “New GitHub App”. Then, configure your app’s name, description, and permissions based on the needs of your GitHub Actions. For GraphQL queries, set the necessary read and possibly write permissions.

Specify a Webhook URL if required (not necessary for GitHub Actions), and complete the app creation process. Once the app is created, install it on the repositories where your GitHub Actions will run. This step applies the permissions you’ve configured to the repositories selected

Use the App’s installation access token for authentication in your GitHub Actions workflows. GitHub provides an API endpoint to generate these tokens, which can be requested within a workflow run.

Example: Authenticating a GraphQL Query

name: Use GraphQL with GitHub App Auth
on: [push]

jobs:
  example-job:
    runs-on: ubuntu-latest
    steps:
    - name: Generate token
      id: generate_token
      run: |
        TOKEN=$(curl -X POST \
        -H "Authorization: Bearer YOUR_APP_JWT" \
        -H "Accept: application/vnd.github.v3+json" \
        https://api.github.com/app/installations/YOUR_INSTALLATION_ID/access_tokens | jq -r '.token')
        echo "::set-output name=token::$TOKEN"
    - name: Run GraphQL Query
      run: |
        curl -H "Authorization: Bearer ${{ steps.generate_token.outputs.token }}" \
        -X POST -d '{"query":"query { viewer { login }}"}' \
        https://api.github.com/graphql
Enter fullscreen mode Exit fullscreen mode

This example demonstrates generating an installation access token for the GitHub App and using it to authenticate a GraphQL query within a GitHub Action. Adjust the YOUR_APP_JWT and YOUR_INSTALLATION_ID placeholders to match your GitHub App’s credentials and installation ID.

Using GraphQL.js

Integrating GraphQL.js with GitHub Actions expands the possibilities for querying and manipulating data directly within automation scripts. GraphQL.js, the official JavaScript reference implementation for GraphQL, makes it easy to create complicated queries and mutations, allowing for dynamic and powerful interactions with the GitHub GraphQL API.

Set up GraphQL.js

To use GraphQL.js in a GitHub Action, you typically need a Node.js environment. While GitHub Actions run in a variety of environments, most custom scripts that leverage GraphQL.js will be executed in a Node.js setup. Here’s a streamlined approach to getting started:

  1. Make sure your GitHub Action workflow initiates a Node.js environment. This can be achieved by using a setup-node action at the beginning of your workflow.
  2. Use npm or yarn to install GraphQL.js. This can be done within your workflow file or as part of a project dependency if you’re running a custom script.
- name: Install Dependencies
  run: npm install graphql
Enter fullscreen mode Exit fullscreen mode

Example of a use case: Fetching repository information

Here’s how you might use GraphQL.js to fetch detailed information about issues within a repository:

const { graphql, buildSchema } = require('graphql');

// Define your schema and resolver
const schema = buildSchema(`
  type Query {
    repository(owner: String!, name: String!): Repository
  }
  type Repository {
    issues(labels: [String], first: Int): IssueConnection
  }
  type IssueConnection {
    edges: [IssueEdge]
  }
  type IssueEdge {
    node: Issue
  }
  type Issue {
    title: String
    body: String
    createdAt: String
  }
`);

// The root provides the top-level API endpoints
const root = {
  repository: () => {
    // Mocked data, replace with actual GraphQL query execution
    return { /* repository data */ };
  },
};

// Sample query
const query = `
  {
    repository(owner: "OWNER_NAME", name: "REPO_NAME") {
      issues(labels: ["bug"], first: 5) {
        edges {
          node {
            title
            body
            createdAt
          }
        }
      }
    }
  }
`;

// Execute the query
graphql(schema, query, root).then((response) => {
  console.log(response);
});
Enter fullscreen mode Exit fullscreen mode

Mutations in GraphQL

Mutations in GraphQL allow for the creation, modification, and deletion of data. In terms of GitHub Actions, mutations enable you to automate actions such as merging pull requests, creating issues, or updating repository content.

The GraphQL Endpoint

All interactions with the GitHub GraphQL API occur through a single HTTP endpoint. Understanding how to make requests to this endpoint is crucial for both queries and mutations.

Endpoint details:

  • URL: https://api.github.com/graphql
  • Authentication: Requires an OAuth token or GitHub App installation token for access.
  • Request structure: All GraphQL operations are sent as POST requests with the query included in the body.

Example Query: Fetching issue labels

query {
  repository(owner: "OWNER_NAME", name: "REPO_NAME") {
    issue(number: 1) {
      labels(first: 5) {
        nodes {
          name
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This query can be used within a GitHub Action to determine how to proceed based on the labels attached to an issue.

Example mutation: Closing an issue

mutation {
  closeIssue(input: {issueId: "ISSUE_ID"}) {
    issue {
      state
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Integrate this mutation into a GitHub Action to automatically close issues that meet certain criteria, such as all associated tasks being completed.

Implementing in GitHub Actions:

Make sure your GitHub Action has access to a token with the necessary permissions.

Then, process the data returned from queries and mutations to control the flow of your GitHub Actions, such as conditional logic based on query results or confirmation of mutation success.

Conclusion

The integration of GitHub Actions and the GraphQL API provides significant improvements to development workflows, including precise automation and optimization capabilities. As we use these tools to streamline processes, it’s critical to remember the value of protecting our workflows. Implementing GitHub backup best practices and using such backup and Disaster Recovery tools like GitProtect.io guarantees the resilience of our CI/CD pipelines.

✍️ Subscribe to GitProtect DevSecOps X-Ray Newsletter – your guide to the latest DevOps & security insights

🚀 Ensure compliant DevOps backup and recovery with a 14-day free trial

📅 Let’s discuss your needs and see a live product tour

💖 💪 🙅 🚩
gitprotectteam
GitProtect Team

Posted on September 13, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related