Introduction
GitHub provides some interesting tools such as the Project tab which allow creating cards based on PR or issues. It provides some automation out of the box, but as of now, it’s a bit below what you can have with Gitlab.
The project can be customized as a kanban board to track the work, like PRs in progress, in review, ready to merge… Since it’s tedious to create card manually, here’s how you can create you own GitHub action to do that automatically for you.
Process
In the context of multiple people/teams contributing to the same code base, we want to enhance the review process
Having members and owners the PR must first be reviewed and approved before being merged. The board will allow the PR to be visually separated between the ones that are:
- parked (draft, stale, …)
- in progress (actively being committed, solving review comments)
- ready for review
- approved and ready for merge
In a context where there’s a lot of teams, or a lot of PR visually separating the PR by states will facilitate the code review and merging process.
Tools
Let’s get set up with what GitHub offers out of the box.
- A repository that you own
- my-username/repo
- my-org/repo
- A project created in that repository
- you can use the kanban review template
Basics
Let’s go back to the basics so that we understand what we’re doing.
Create a personal access token
Let’s generate a token, with admin:org
rights for your personal token.
If it’s for your own usage only, the default secrets.GITHUB_TOKEN
accessible in the workflow might be just enough.
To automate the new beta project feature you might need more rights.
Go to Settings > Developer settings > Personal access tokens and select this option:
GitHub API
The GitHub rest API allows you to interact gain access to its resources. Those are the bare-bones commands that are usually wrapped in a library for ease of use in your language.
Here you need a token with at least read access to read the pull request in the repo.
curl \
-H "Accept: application/vnd.github.v3+json" \
-u user:token \
https://api.github.com/repos/UpstreamOrg/UpstreamRepo/pulls
This returns a lot of information! With GraphQL, we could just query for what we want.
What we want is the id
(797190601) of the pull request number 6 in that repo (pull/6),
because \(id \ne number\).
So once we have the id, we can move to the next step which is to create the corresponding card on our project board. The api reference is in my opinion a bit lacking, so here is a working example:
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-u user:token \
-d '{"content_id": 797190601, "content_type": "PullRequest", "column_id": 17134543 }' \
https://api.github.com/projects/columns/17134543/cards
The column_id
correspond to my in progress column and can be obtained on the URI when clicking on copy link on
the top right three dots button.
Note: You need to specify the column id twice… for fun 🤡
- once in the payload
- once in the URI.
With that the card is created for this PR. If you try it again, it will fail as there can only be one card per PR per board.
Automatically create the card
We’ll create a GitHub action that will do the card creation in the project for us. We’ll use the github-script action which uses Octokit to interact with GitHub’s API.
Action’s creation
There are two things we need for this action:
- The event that will trigger the action
- The pull request’s id
We’ll manage those thanks to the GitHub actions various features.
Use pull_request_target
We want to trigger the action on new Pull request, when they are opened.
The default pull_request
event reduces for security reasons the rights given when running the action,
to prevent malicious code to be injected or run and deploy via the CI/CD pipeline.
In order to bypass that for our use case, we’re going to use the pull_request_target
event which does not downgrade
the rights, meaning we’ll be able to access the repository secrets to make the call to the API to create the card based on the PR.
Use the context
The context from the github-script is an object containing the github
context which holds a lot of
contextual information. It’s wrapped up nicely, so it gets easier to use you can display it all by creating a
small action such as:
jobs:
view-context:
runs-on: ubuntu-latest
steps:
- name: View context attributes
continue-on-error: true
uses: actions/github-script@v5
with:
script: |
console.log('${{ github.actor }}');
console.log(context)
You see the context being used raw github.actor
and the context being called from the context
object.
In our case the pull request id on a pull_request event, we’ll be in context.payload.pull_request.id
,
where the payload is the event’s one.
Actual action
Now that everything is set, let’s dive in the actual action add-pr-card.yml
which will automate part of our process:
name: '[Project] Create card'
on:
pull_request_target:
types: [ opened ]
jobs:
create-card:
runs-on: ubuntu-latest
steps:
- name: Add PR to project board
uses: actions/github-script@v5
with:
github-token: ${{secrets.PROJECT_TOKEN}}
script: |
github.rest.projects.createCard({
column_id: 17101633,
content_id: context.payload.pull_request.id,
content_type: 'PullRequest'
});
Some caveat and notes in the use of the script:
- Make sure the notation match the version you are using (here it’s v5)
- The github-token is by default the
secrets.GITHUB_TOKEN
with the latest version - The
script
part is actual javascript and will fail if you put a yml comment in it (even though it would look valid at first glance)
That’s it, now you can add the action and enjoy it!
👉 The script is now ready to use, find a live example usage on UpstreamOrg/UpstreamRepo. 🍔
Troubleshoot common errors
Some errors are a bit obnoxious, so here is are some troubleshooting help. Those are the one I experienced using both the API and the github-script.
Token privilege issue
When using the API you may encounter:
{
"message": "You need at least public_repo scope to view public repository projects via OAuth"
}
This one is pretty clear, it’s a right issue related to your token. Apparently the basic right you need in you personal access token for any API call is repo > public_repo.
Data issue
For any types of data issue, using the API you will receive some message such as:
{
"message": "Validation Failed",
"errors": [
{
"resource": "ProjectCard",
"code": "unprocessable",
"field": "data",
"message": "Could not resolve to a node with the global id of 'MDExOlB1bGxSZXF1ZXN0OA=='."
}
],
"documentation_url": "https://docs.github.com/v3/projects/cards/#create-a-project-card"
}
This can happen on missing or incorrect payload for example. Or when using a PR number instead of the PR id.
Token privilege downgraded issue
When using the github-script action you may see this error:
Unhandled error: HttpError: Resource not accessible by integration
In that case I checked my personal access token, and it was never used meaning it didn’t even try to get the resource
using my token.
That’s when I discovered that this issue is often related to the downgrade of rights on none pull_request_target
event
which would make the secrets inaccessible, hence empty.
So this is also a privilege issue, make sure you’re using the right trigger if you need more than the GITHUB_TOKEN
.
Script error
When using the github-script action, I encountered this error:
Unhandled error: SyntaxError: Invalid or unexpected token
The wording through me off because the token I was using did work with API before. In the end this meant that the script that I was using within the action was wrong.
In my case I had used a comment in yaml which starts with #
in the script part.
Javascript comments are usually //
, so parsing the #
made it error out.