Add Labels to PRs with a Typescript GitHub Action
Tags: github, githubactions, typescript, tutorial
Published: 2023-08-16 16:04:00

**GitHub Actions** are a powerful tool to automate your workflow. They can be used to **run tests**, **deploy** your code, **publish** a package, and much more.

The cool thing is, there's a GitHub Actions Marketplace where you can find a lot of actions created by... the community.

_But what if you can't find the action you need?_ You can create your own and publish it there!

## How to use this tutorial

{% details Read more... %} 


In this tutorial we're going to see in detail how to:

- Create a GitHub Action in Typescript
- Expand our Action to support custom inputs
- Integrate with GitHub's API to add labels to Pull Requests
- Unit testing our action
- Debugging in Visual Studio Code
- Publishing our action to the GitHub Marketplace
- Using our action in another repository
- Some final touches to make our project more robust

The articles will be split into separate bite-sized chapters as technically each one can be a little tutorial by itself.

If you're interested in the full text all at once, you can find it here: https://leonardomontini.dev/typescript-github-action/

One more great piece of advice is to create a new repository and follow along with the steps. This way you'll have a working action at the end of the post and you'll be able to play with it and experiment, rather than just reading a long tutorial and forgetting about 90% of it.

The full code of this tutorial is available on GitHub on [this repo](https://github.com/Balastrong/ga-label-issue), so you can always refer to it if you get stuck.

{% enddetails %}

The full tutorial (all chapters at once) is also available as a video, which you can find here:

{% youtube N26xgQ7kLKo %}

## Chapter 4: Use case - Adding a label to new Pull Requests

We learned the basics, we have our MVP ready to be expanded so let's do it! We'll create a new action that will automatically run every time a new Pull Request is created and it will add a `needs-review` label to it.

### Update the action code

In order to add a label to a Pull Request, we need to use the [GitHub REST API](https://docs.github.com/en/rest). We can use the Octokit client to make the API calls. Luckily for us there's an official package we can use.

```bash
npm install @actions/github
```

Then we can import the `Octokit` client and use it to add the label to the Pull Request. Here's the full code.

```ts
import { getInput, setFailed } from '@actions/core';
import { context, getOctokit } from '@actions/github';

async function run() {
  const token = getInput('gh-token');
  const label = getInput('label');

  const octokit = getOctokit(token);
  const pullRequest = context.payload.pull_request;

  try {
    if (!pullRequest) {
      throw new Error('This action can only be run on Pull Requests');
    }

    await octokit.rest.issues.addLabels({
      owner: context.repo.owner,
      repo: context.repo.repo,
      issue_number: pullRequest.number,
      labels: [label],
    });
  } catch (error) {
    setFailed((error as Error)?.message ?? 'Unknown error');
  }
}

run();
```

What happened here?

1. We read the `gh-token` and `label` inputs from the workflow. The token is a [personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) that we need to create and pass to the action. More on that later. The label is the name of the label we want to add to the Pull Request.
2. We create an instance of the `Octokit` client using the token.
3. We get the Pull Request number from the `context.payload.pull_request` object. This object is only available when the action is triggered by a Pull Request event.
4. We call the `octokit.rest.issues.addLabels` method to add the label to the Pull Request.

In case something fails, we catch the error and we set the workflow as failed with the `setFailed` function, also coming from the `@actions/core` package.

### Update the action definition

We also need to update the action definition to add the new inputs.

```yaml
inputs:
  gh-token:
    description: 'The GitHub token for authentication.'
    required: true
  label:
    description: 'The label to be applied to the pull request.'
    required: true
```

### Update the workflow

Finally, we need to update the workflow to run when PRs are opened or reopened:

```yaml
on:
  pull_request:
    types: [opened, reopened]
```

...and to pass the new inputs to the action.

```yaml
- uses: ./
  with:
    gh-token: ${{ secrets.GITHUB_TOKEN }}
    label: 'needs-review'
```

We're passing the `GITHUB_TOKEN` secret to the action. It usually does not require you any extra action, as GitHub automatically creates it for you.

In case you get an error that says "Resource not accessible by integration", you need to make sure your token on that repository has write access. You can do that by going to the Settings tab > Actions > General and scroll down to the "Workflow permissions" section.

Setting it to "Read and write permissions" will be enough.

![Workflow permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/of9yx3ldkmw9ds804y2g.png)

### Run the action

We can now run the action again and see the label is added to the Pull Request.

However, let's not forget to run `npm run build` first, then we can commit and push the changes. The reason is that we told GitHub that the action is located in the `dist` folder, so we need to make sure that folder is up to date.

```bash
npm run build
git add .
git commit -m "Add label to new Pull Requests"
git push
```

Cool! We can now create a new branch, change a file and open a Pull Request. We'll see the action running and adding the label to the Pull Request, aaaand...

![Build error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qg1x580o1v36btzykza7.png)

```
Error: Cannot find module '@actions/core'
```

What happened here? If you look at your file in `dist/index.js`, you'll see that the `@actions/core` package is not there. That's because we didn't tell TypeScript to include it in the build.

A good way to include packages and condense everything in one file is with the tool `@vercel/ncc`. Let's install it.

```bash
npm install @vercel/ncc
```

Then we can update our `build` script in `package.json` to use it.

```json
 "build": "tsc && ncc build lib/index.js"
```

We also need to change `outDir` in `tsconfig.json` to `lib`.

```json
"outDir": "lib"
```

You might also want to double-check that `lib` is in your `.gitignore` file but `dist` is not. This is our desired behavior as `lib` will only have our code in javascript, but `dist` will have the whole action.

### Run the action (2.0)

We can now run `npm run build` again, then commit and push the changes.

Create a new branch, change a file and open a Pull Request. This time, you'll see the action running and adding the label to the Pull Request.

![Label added to the Pull Request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/au4cvf3m88rfjeyrnnif.png)

## Closing

And that was it for today! if you have any question or suggestion, feel free to add a comment :)

See you in the next chapter!

---

Thanks for reading this article, I hope you found it interesting!

I recently launched my Discord server to talk about Open Source and Web Development, feel free to join: https://discord.gg/bqwyEa6We6

Do you like my content? You might consider subscribing to my YouTube channel! It means a lot to me ❤️
You can find it here:
[![YouTube](https://img.shields.io/badge/YouTube:%20Dev%20Leonardo-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/channel/UC-KqnO3ez7vF-kyIQ_22rdA?sub_confirmation=1)

Feel free to follow me to get notified when new articles are out ;)
{% embed https://dev.to/balastrong %}