How I Bulk Closed 1000+ GitHub Issues with GitHub Actions 🚀
Tags: githubactions, github, githubcopilot, productivity
Published: 2024-01-17 18:29:57

Now why would I want to do this huh? 🤔 

Well, we have a private repo where maintainers are able to make requests to join the [private maintainer community](https://maintainers.github.com/auth/signin).

Someone thought it'd be cute to submit 1000+ issues to the repo with the handle `@undefined` and the title `undefined`. This was a problem because it was making it difficult to find the actual issues that were being submitted by maintainers that needed to be approved.

![1600+ issues on repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/884nz0o2217qn0666znv.png)

We need to do some additional work to the repo before I'm able to add validation to the issue form but in the meantime, we needed to close these issues. So, I thought a temporary solution would be to create a GitHub action that closed all of the issues - with the title containing `Pending invitation request for: @undefined'`. And it worked!!

**We went from having over 1,600+ issues to 64 valid issues in a matter of minutes.** 

Here's how I did it.


## Creating the GitHub Action

The first thing I did was go to my bestie chatty (GitHub Copilot Chat) and asked it if I could close 1000+ issues with a GitHub action. It gave me a few options, but I decided to go with this one:


![gh copilot chat response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u6kuj92p00n668hx3i8o.png)

Then it generated the `closeIssue.js` file which looked like this:

![gh copilot chat closeIssues.js function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fa44dc07s1g6m67rszia.png)

Sweet, that was the bulk of the work. Now I just needed to add the action to my repo.

I created a file called `close_issues.yml` in the `.github/workflows` directory and added the following code:

```yml
name: Close Undefined Issues
on:
    workflow_dispatch:
    push:
      branches: [ "main" ]
    pull_request:
      # The branches below must be a subset of the branches above
      branches: [ "main" ]
    schedule:
      - cron: '43 0 * * 1'
  
jobs:
  close_issues:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 14

      - name: Install dependencies
        run: npm install

      - name: Run script
        run: node closeIssues.js
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

```

I made a few tweaks to the code, but all in all, this code was written largely in collaboration with GitHub Copilot.

The next file I created was the `closeIssues.js` file. I added the following code:

```js

import { Octokit } from "@octokit/rest";

const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN,
});

async function closeIssues() {
  const { data: issues } = await octokit.issues.listForRepo({
    owner: 'OWNER',
    repo: 'internal-repo',
  });

  const issuesToClose = issues.filter(issue => issue.title === 'Pending invitation request for: @undefined');

  for (const issue of issuesToClose) {
    await octokit.issues.update({
      owner: 'OWNER',
      repo: 'internal-repo',
      issue_number: issue.number,
      state: 'closed',
    });
  }
}

closeIssues().catch(console.error);
```

I added a `package.json` file to my repo, install the needed dependency, and then pushed the code to my repo.

## Testing the Action

After about 3 (ok 4!) tries, I finally got the action to run successfully.

Initially, the action closed 30 issues at a time, because that is the limit for the GitHub API. So, I had to read the docs 🤯 to understand how to grab ALL issues that matched the criteria and closed them.

## Updates

After reading the docs, I realized that I needed to use the `paginate()` method to get all of the issues for the specified repo. I also learned that with paginate, the issues are returned as an array, so I no longer needed `const { data: issues }`. I also needed to use `octokit.rest.issues` when making requests.

In the end, the code that ran successfully and closed all 1000+ issues AT ONCE looked like this:

```js
import { Octokit } from "@octokit/rest";

const octokit = new Octokit({
    auth: process.env.GITHUB_TOKEN,
});

async function closeIssues() {
    try {
        console.log('Fetching issues...');
        const issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
            owner: 'OWNER',
            repo: 'internal-repo',
            state: 'open',
        });

        console.log(`Fetched ${issues.length} issues.`);
        const issuesToClose = issues.filter(issue => issue.title === 'Pending invitation request for: @undefined');
        console.log(`Found ${issuesToClose.length} issues to close.`);

        for (const issue of issuesToClose) {
            console.log(`Closing issue #${issue.number}...`);
            await octokit.rest.issues.update({
                owner: 'OWNER',
                repo: 'internal-repo',
                issue_number: issue.number,
                state: 'closed',
            });
            console.log(`Closed issue #${issue.number}.`);
        }
    } catch (error) {
        console.error(error);
    }
}

closeIssues();

```

I added some logging to the code so I could see what was happening during the workflow run. I also added a try/catch block to catch any errors that may occur.

![action_autoclose](https://github.com/githubevents/open-source-friday/assets/47188731/75bd569f-b2f5-4b41-8915-2e6ca30a39ba)

## Conclusion
I was very happy that I could do this because it would've taken my teammate HOURS to close all these issues manually. I was able to do it in a matter of minutes! I'm super stoked that GitHub Copilot was able to teach me something new - how to interact with the GitHub API using the [Octokit](https://octokit.github.io/rest.js/v20) library. 🚀

![64 open issues on repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uo9jt1hqh0hxawgl9q27.png)


Have you ever used GitHub Copilot to help you write GitHub Actions? Let me know in the comments below! I hope you learned something new today. 

Until next time, happy coding! 😁