How the GitHub Asana Request Review Action Tracks and Updates Review Status When PR Reviews Are Submitted
When GitHub sends a pull_request_review webhook, the action extracts the Asana task ID from the PR description, finds or creates a code-review subtask for the reviewer, then writes an HTML comment containing a status badge reflecting the review state (✅ Approved, 💬 Commented, or ❗️ Changes Requested) together with the comment count, and reassigns the subtask back to the PR requester.
The keitap/github-asana-request-review-action automates code review tracking by synchronizing GitHub pull request activity with Asana tasks. When reviewers submit feedback, the action processes the webhook payload to update the corresponding Asana subtask with real-time status indicators and comment counts. This guide walks through the exact implementation details of how the action tracks and updates review status when PR reviews are submitted, referencing the production source code.
Handling the Pull Request Review Webhook
The entry point for review tracking is the Handle method in handler.go, which routes incoming GitHub webhook events to specialized handlers.
When a reviewer submits a review, GitHub emits a pull_request_review event that the action captures and parses:
func (h *Handler) Handle(eventName string, eventPayload []byte) error {
event, err := github.ParseWebHook(eventName, eventPayload)
// ...
switch e := event.(type) {
case *github.PullRequestEvent:
return h.handlePullRequestEvent(e)
case *github.PullRequestReviewEvent:
return h.handlePullRequestReviewEvent(e) // review submitted handler
}
}
Source: handler.go lines 35-46
The handlePullRequestReviewEvent function orchestrates the entire update workflow, beginning with extracting the Asana task identifier from the pull request body.
Extracting the Asana Task ID
The action relies on a specific Asana URL embedded in the PR description to link GitHub activity to Asana tasks. The parseAsanaTaskLink function scans the PR body for Asana task URLs in either v0 or v1 format:
_, _, taskID := parseAsanaTaskLink(pr.PullRequest.GetBody())
if taskID == "" {
log.Println("asana task url not found in description.")
return nil
}
Source: asana.go lines 18-31
If no task ID is found, the action exits silently—review status updates only occur when an explicit Asana link is present in the PR description.
Resolving Requester and Reviewer Accounts
The action establishes two critical user mappings to ensure proper task assignment.
Requester Resolution: The getAssigneeOrRequester function determines who should own the Asana subtask after the review update. It examines the PR author and assignee:
requester, err := h.getAssigneeOrRequester(pr)
Source: handler.go lines 69-90
This function handles bot-authored PRs by returning the assignee's Asana account when the author is identified as a bot; otherwise, it returns the PR author's account.
Reviewer Resolution: The fetchAccount function maps the GitHub username from pr.Review.User.GetLogin() to a corresponding Asana account using the configuration's account mapping:
reviewer, err := h.fetchAccount(pr.Review.User.GetLogin())
If no mapping exists for the reviewer, the action uses a placeholder indicating no Asana account is available.
Locating the Reviewer's Code Review Subtask
Rather than creating duplicate tasks, the action searches for existing subtasks associated with the reviewer. The FindSubtaskByName function iterates through all subtasks of the parent Asana task to locate one matching the reviewer's name:
subtask, err := FindSubtaskByName(h.ac, taskID, reviewer.Name)
Source: asana.go lines 99-114
If the subtask cannot be found, the action logs the absence and terminates—ensuring updates only occur when a valid tracking subtask exists.
Counting Review Comments
To provide comprehensive status visibility, the action retrieves the total number of comments submitted as part of the review using getReviewCommentCount:
commentCount, err := getReviewCommentCount(
h.gh,
pr.GetRepo().GetOwner().GetLogin(),
pr.GetRepo().GetName(),
pr.PullRequest.GetNumber(),
pr.Review.GetID(),
)
Source: github.go
This count appears in the final status badge, enabling Asana users to see metrics like "Approved with 3 comments" directly in the task view.
Generating the HTML Status Badge
The action constructs a rich HTML representation of the review status using buildReviewCommentHTML. This function translates GitHub review states into visual indicators:
- ✅ Approved for
approvedstate - 💬 Commented for
commentedstate - ❗️ Changes Requested for
changes_requestedstate
htmlText := buildReviewCommentHTML(
pr.Review.GetHTMLURL(),
pr.Review.GetState(),
reviewer.GetUserPermalink(),
pr.Review.GetBody(),
commentCount,
)
Source: asana.go lines 18-42
The generated HTML includes a link to the GitHub review, the reviewer's Asana profile reference, the comment count, and the review body text, creating a comprehensive summary within Asana.
Posting Comments and Reassigning Ownership
The final step writes the status update to Asana and adjusts task ownership to ensure the requester receives the feedback.
Creating the Comment: The action posts the HTML badge to the reviewer's subtask using CreateComment:
story, err := subtask.CreateComment(client, &asana.StoryBase{HTMLText: htmlText})
Source: asana.go lines 55-66
Reassigning the Subtask: To ensure the PR requester sees the updated status, the action immediately reassigns the subtask from the reviewer back to the requester:
err = subtask.Update(client, &asana.UpdateTaskRequest{
Assignee: requester.AsanaUserGID,
})
Source: asana.go lines 71-77
The action logs the completion of the workflow with the review state and comment count:
log.Printf("review is submitted by reviewer: %s state: %s comments: %d", reviewer.Name, pr.Review.GetState(), commentCount)
Source: handler.go line 64
This reassignment ensures that while the reviewer's name remains associated with the subtask (providing context), the requester owns the actionable item in their Asana inbox.
Summary
- The
keitap/github-asana-request-review-actionprocessespull_request_reviewwebhooks to synchronize GitHub review activity with Asana subtasks. - The action extracts Asana task IDs from PR descriptions using
parseAsanaTaskLinkinasana.go. - Review status updates are stateless: each webhook triggers a new comment containing an HTML status badge generated by
buildReviewCommentHTML. - The workflow resolves GitHub usernames to Asana accounts via
fetchAccountandgetAssigneeOrRequesterinhandler.go. - After posting the review status with comment counts, the action reassigns the subtask to the PR requester to maintain accountability.
- The system handles three review states (Approved, Commented, Changes Requested) and includes the count of review comments in the status badge.
Frequently Asked Questions
What happens if the PR description doesn't contain an Asana task URL?
The action checks for a valid Asana task ID at the beginning of the workflow using parseAsanaTaskLink. If no URL is found in the PR body, the handler logs "asana task url not found in description" and returns early without making any Asana API calls. This silent exit ensures the action only processes linked tasks.
How does the action distinguish between human authors and bot accounts when determining the requester?
The getAssigneeOrRequester function in handler.go examines the PR author. If the author is identified as a bot and the PR has an assignee, it returns the assignee's Asana account instead of the bot's account. This logic ensures that automated PRs (from tools like Dependabot or semantic-release) route feedback to the human responsible for the changes rather than the bot user.
Where is the review status data stored long-term?
The action employs a stateless architecture with no external database. All review status information persists as comments on the reviewer's Asana subtask. Each time a review is submitted, the action generates a new HTML status badge and posts it as a story (comment) on the subtask. This design means the Asana task itself serves as the single source of truth for review history.
What GitHub review states does the action support?
The action handles three distinct GitHub review states: approved, commented, and changes_requested. The buildReviewCommentHTML function in asana.go maps each state to a specific visual indicator (emoji and text) in the HTML badge. The action also counts the number of review comments submitted and includes this metric in the status display, regardless of which state the review represents.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →