# How the GitHub Asana Request Review Action Parses Asana Task URLs from Pull Request Descriptions

> Learn how the GitHub Asana Request Review Action parses Asana task URLs from PR descriptions using regular expressions. Supports both modern and legacy URL formats.

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

---

**The GitHub Asana Request Review Action extracts Asana task identifiers from pull request descriptions using the `parseAsanaTaskLink` function in [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go), which applies two distinct regular expression patterns to support both modern workspace-based URLs and legacy project-based URLs.**

This GitHub Action bridges GitHub pull requests with Asana by automatically scanning PR descriptions for valid Asana links. Written in Go, the implementation relies on specific regex matching logic defined in [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go) to identify supported URL formats and extract the necessary identifiers for API integration.

## The parseAsanaTaskLink Function Implementation

The core URL parsing mechanism is implemented in the `parseAsanaTaskLink` function, defined at lines 18-30 of [[`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go)](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go#L18-L30). This function attempts to match the PR description against two regular expression patterns sequentially, returning three strings: `workspaceID`, `projectID`, and `taskID`.

### Modern v1 Workspace-Based Format

The action first attempts to match URLs following Asana's modern v1 format using the following pattern:

```go
v1 := regexp.MustCompile(`https://app\.asana\.com/1/(\d+)/(?:task/|project/\d+/task/)(\d+)`)

```

This pattern captures two numeric groups:
- First `(\d+)` → **workspaceID**
- Second `(\d+)` → **taskID**

When this pattern matches, the function returns the workspace ID and task ID, leaving `projectID` empty. This format supports both direct task links and URLs containing intermediate project path segments.

### Legacy Project-Based Format

If the v1 pattern fails to match, the function falls back to the legacy "0" format:

```go
var taskURLMatcher = regexp.MustCompile(`https://app.asana.com/0/(\d+)/(\d+)`)

```

This pattern captures:
- First `(\d+)` → **projectID** (note that workspace ID is not present in legacy URLs)
- Second `(\d+)` → **taskID**

When this pattern matches, `projectID` is populated while `workspaceID` remains empty.

## Integration with Pull Request Handling

The URL parser is invoked in [[`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go)](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go#L53-L55) using the pull request body as input:

```go
workspaceID, projectID, taskID := parseAsanaTaskLink(pr.PullRequest.GetBody())

```

If the returned `taskID` is empty—indicating neither regex pattern matched—the action logs "asana task url not found in description" and skips Asana integration for that pull request. This check prevents the action from attempting to create review subtasks when no valid Asana link is present.

## Supported Asana URL Formats

The GitHub Asana Request Review Action recognizes three specific URL structures derived from the regex patterns:

- **Legacy format**: `https://app.asana.com/0/<project-id>/<task-id>` — Captures legacy project and task identifiers.
- **Modern direct task**: `https://app.asana.com/1/<workspace-id>/task/<task-id>` — Captures workspace and task identifiers.
- **Modern project task**: `https://app.asana.com/1/<workspace-id>/project/<project-id>/task/<task-id>` — Captures workspace and task identifiers while ignoring the intermediate project path segment.

Query strings such as `?focus=true` are automatically ignored by the regex patterns, allowing the action to correctly extract IDs regardless of additional URL parameters.

### Practical Parsing Examples

The following examples demonstrate how `parseAsanaTaskLink` handles different URL formats:

```go
// Example 1: Legacy format (project/task)
prBody := `Implemented feature X.

https://app.asana.com/0/364167036366785/1162650948650897
`
ws, proj, task := parseAsanaTaskLink(prBody)
// ws == "", proj == "364167036366785", task == "1162650948650897"

// Example 2: Modern workspace/task format with query parameter
prBody := `Please review.

https://app.asana.com/1/5590853215187/task/1209347772587937?focus=true
`
ws, proj, task = parseAsanaTaskLink(prBody)
// ws == "5590853215187", proj == "", task == "1209347772587937"

// Example 3: Modern workspace/project/task format
prBody := `Work in progress.

https://app.asana.com/1/5590853215187/project/364167036366785/task/1162650948650897
`
ws, proj, task = parseAsanaTaskLink(prBody)
// ws == "5590853215187", proj == "", task == "1162650948650897"

```

## Summary

- The **GitHub Asana Request Review Action** uses the `parseAsanaTaskLink` function in [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go) to extract task identifiers from pull request descriptions.
- Two regex patterns support both modern v1 workspace URLs (`https://app.asana.com/1/...`) and legacy project URLs (`https://app.asana.com/0/...`).
- The function returns `workspaceID`, `projectID`, and `taskID`, with only one of the first two populated depending on which URL format is detected.
- If no valid URL is found during execution in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go), the action logs a warning and skips Asana integration for that pull request.
- Query parameters and focus modes appended to Asana URLs are ignored during parsing.

## Frequently Asked Questions

### What regex patterns does the GitHub Asana Request Review Action use to find Asana task URLs?

The action employs two patterns defined in [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go): a modern v1 pattern `` `https://app\.asana\.com/1/(\d+)/(?:task/|project/\d+/task/)(\d+)` `` for workspace-based URLs, and a legacy pattern `` `https://app.asana.com/0/(\d+)/(\d+)` `` for project-based URLs. Both patterns are evaluated sequentially until a match is found.

### Which Asana URL formats are supported by this GitHub Action?

The action supports three specific formats: legacy URLs structured as `https://app.asana.com/0/123456789/987654321`, modern direct task URLs formatted as `https://app.asana.com/1/555/task/999`, and modern project-task URLs like `https://app.asana.com/1/555/project/123/task/999`. Any URL not conforming to these patterns is ignored by the parser.

### What happens if a pull request description contains no valid Asana task URL?

When `parseAsanaTaskLink` returns an empty `taskID`—indicating neither regex pattern matched—the handler in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go) logs "asana task url not found in description" and bypasses all Asana integration steps for that pull request.

### How does the action handle query parameters in Asana URLs?

The regex patterns match only up to the task ID portion of the URL, automatically ignoring query strings such as `?focus=true`. This ensures the action correctly extracts numeric identifiers regardless of additional tracking parameters or view options appended to the link.