# How the GitHub Asana Request Review Action Tracks and Updates Review Status When PR Reviews Are Submitted

> Learn how the GitHub Asana Request Review Action tracks and updates Asana with PR review status. Automatically syncs reviews and comments to your Asana tasks.

- Repository: [Keita Kitamura/github-asana-request-review-action](https://github.com/keitap/github-asana-request-review-action)
- Tags: deep-dive
- Published: 2026-03-05

---

**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`](https://github.com/keitap/github-asana-request-review-action/blob/main/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:

```go
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`](https://github.com/keitap/github-asana-request-review-action/blob/main/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:

```go
_, _, taskID := parseAsanaTaskLink(pr.PullRequest.GetBody())
if taskID == "" {
    log.Println("asana task url not found in description.")
    return nil
}

```

*Source:* [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/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:

```go
requester, err := h.getAssigneeOrRequester(pr)

```

*Source:* [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/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:

```go
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:

```go
subtask, err := FindSubtaskByName(h.ac, taskID, reviewer.Name)

```

*Source:* [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/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`:

```go
commentCount, err := getReviewCommentCount(
    h.gh,
    pr.GetRepo().GetOwner().GetLogin(),
    pr.GetRepo().GetName(),
    pr.PullRequest.GetNumber(),
    pr.Review.GetID(),
)

```

*Source:* [`github.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/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 `approved` state
- **💬 Commented** for `commented` state  
- **❗️ Changes Requested** for `changes_requested` state

```go
htmlText := buildReviewCommentHTML(
    pr.Review.GetHTMLURL(),
    pr.Review.GetState(),
    reviewer.GetUserPermalink(),
    pr.Review.GetBody(),
    commentCount,
)

```

*Source:* [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/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`:

```go
story, err := subtask.CreateComment(client, &asana.StoryBase{HTMLText: htmlText})

```

*Source:* [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/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:

```go
err = subtask.Update(client, &asana.UpdateTaskRequest{
    Assignee: requester.AsanaUserGID,
})

```

*Source:* [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go) lines 71-77

The action logs the completion of the workflow with the review state and comment count:

```go
log.Printf("review is submitted by reviewer: %s state: %s comments: %d", reviewer.Name, pr.Review.GetState(), commentCount)

```

*Source:* [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/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-action` processes `pull_request_review` webhooks to synchronize GitHub review activity with Asana subtasks.
- The action extracts Asana task IDs from PR descriptions using `parseAsanaTaskLink` in [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.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 `fetchAccount` and `getAssigneeOrRequester` in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.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`](https://github.com/keitap/github-asana-request-review-action/blob/main/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`](https://github.com/keitap/github-asana-request-review-action/blob/main/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.