# How Hiring Agent Extracts GitHub Usernames from Resumes: Pipeline Deep Dive

> Learn how Hiring Agent extracts GitHub usernames from resumes. Discover the process involving parsing and validating URLs in the candidate data pipeline.

- Repository: [HackerRank/hiring-agent](https://github.com/interviewstreet/hiring-agent)
- Tags: deep-dive
- Published: 2026-07-21

---

**Hiring Agent extracts GitHub usernames by scanning the `profile.links` array in candidate resume JSON, parsing github.com URLs through `extract_username_from_url` in [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py), and validating them via `extract_github_username` in [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/github.py) before storage.**

The `interviewstreet/hiring-agent` repository automates technical candidate screening by transforming unstructured resume data into structured profiles. The extraction pipeline specifically targets the `profile.links` field to identify GitHub URLs, employing domain-specific logic to isolate and verify usernames for downstream API integration.

## The Resume Data Structure

Candidate resumes are stored as structured JSON objects containing a `profile.links` array. This field aggregates professional URLs including LinkedIn, Twitter, and GitHub profiles. The extraction pipeline iterates over this collection to identify domains matching **github.com**.

## Step-by-Step Extraction Process

The extraction follows a deterministic flow from URL detection to validated storage.

### Detecting GitHub URLs in transform.py

At line 154 of [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py), the pipeline iterates over `profile.links` and calls `extract_username_from_url(url, domain)`. The function receives the raw URL and a domain identifier derived from a mapping that recognizes `"github.com": "GitHub"`. This domain argument routes the URL to the appropriate extraction logic.

### Parsing Usernames with extract_username_from_url

The `extract_username_from_url` function constructs a regular expression based on the domain pattern to extract the path component representing the username. It returns the raw username string or `None` if the URL structure does not match the expected pattern.

### Normalization and Validation in github.py

Once a candidate username is identified, the pipeline passes the original URL to `extract_github_username(github_url)` at line 116 of [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/github.py). This helper performs three critical operations:

- **Sanitization**: Strips trailing slashes, query strings (`?...`), and URL fragments (`#...`)
- **Pattern validation**: Applies the regex `^[A-z0-9_-]+$` to confirm the username contains only alphanumeric characters, hyphens, and underscores
- **Error handling**: Logs a warning and returns `None` if validation fails, preventing invalid data from entering the candidate record

## Implementation Example

The following Python demonstrates the extraction flow using the actual pipeline logic:

```python

# Simplified resume structure

resume = {
    "profile": {
        "links": [
            "https://github.com/jane-doe",
            "https://linkedin.com/in/jane-doe"
        ]
    }
}

# Extraction logic from transform.py (line 154)

for url in resume["profile"]["links"]:
    domain = url.split("://")[1].split("/")[0]   # e.g. "github.com"

    if domain == "github.com":
        username = extract_username_from_url(url, domain)
        if username:
            # Validation via github.py (line 116)

            username = extract_github_username(url)
            resume["github_username"] = username

```

Executing this snippet against the sample data yields:

```python
>>> resume["github_username"]
'jane-doe'

```

## Downstream Integration

After successful extraction and validation, the username is stored in the transformed candidate record under the key `github_username`. This field is consumed by [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) to fetch public profile data via the GitHub API (`https://api.github.com/users/<username>`) and retrieve public repositories for technical evaluation. The [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) file defines the `GitHubProfile` Pydantic model that enforces type safety for this data throughout the system.

## Summary

- **Source Location**: GitHub URLs are detected in [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py) at line 154 within the `profile.links` iteration loop.
- **Two-Stage Processing**: Raw extraction occurs via `extract_username_from_url`, followed by normalization and regex validation in `extract_github_username` at line 116 of [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/github.py).
- **Validation Rules**: Usernames must match `^[A-z0-9_-]+$` after stripping query parameters and fragments.
- **Storage**: Valid usernames populate the `github_username` field used by [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) for API-based repository analysis.

## Frequently Asked Questions

### What happens if a resume contains an invalid GitHub URL?

If `extract_github_username` cannot parse a valid username or if the regex validation fails, the function logs a warning and returns `None`. The candidate record proceeds without a `github_username` field, and no API calls are attempted for that profile.

### Does Hiring Agent support GitHub Enterprise or custom domains?

According to the source code, the domain detection relies on a hardcoded mapping that specifically checks for `"github.com"`. The `extract_username_from_url` function uses this domain parameter to route processing, indicating the current implementation targets public GitHub profiles only.

### Where is the extracted username stored in the data model?

The validated username is written to the `github_username` key in the transformed candidate record. This field is referenced by [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) and conforms to the `GitHubProfile` Pydantic model defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py).

### How does the pipeline handle multiple GitHub links in one resume?

The [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py) module iterates over all entries in `profile.links`, processing each github.com URL independently. Subsequent valid usernames overwrite previous values in the `github_username` field, meaning the last valid GitHub link in the array determines the final stored value.