How Merged Pull Requests Are Tracked and Displayed as Contributions in GitHub Resume
The GitHub Resume generator tracks merged pull requests by querying GitHub's search API for type:pr is:merged, aggregates the results by repository, and renders them as clickable contribution counts using client-side JavaScript and Mustache templates.
The resume.github.com repository generates dynamic résumé pages entirely in the browser, transforming a user's GitHub activity into a readable portfolio. Understanding how merged pull requests are tracked and displayed as contributions reveals the client-side architecture that powers this open-source project.
The Three-Step Process for Tracking Merged Pull Requests
The application follows a distinct pipeline to convert raw GitHub data into formatted contribution entries. This process occurs entirely within js/githubresume.js without server-side processing.
Fetching Merged Pull Request Data via GitHub API
The github_user_issues function initiates the data collection by calling GitHub's search issues endpoint using JSONP to bypass cross-origin restrictions. The function constructs a query string targeting specifically merged pull requests authored by the user:
var github_user_issues = function(username, callback, page_number, prev_data) {
var page = (page_number ? page_number : 1),
url = 'https://api.github.com/search/issues?q=type:pr+is:merged+author:' + username +
'&per_page=100&callback=?',
data = (prev_data ? prev_data : []);
if (page_number > 1) {
url += '&page=' + page_number;
}
$.getJSON(url, function(repos) {
data = data.concat(repos.data.items);
if (repos.data.total_count == 100) {
github_user_issues(username, callback, page + 1, data);
} else {
callback(data);
}
});
};
The query type:pr is:merged author:<username> ensures only successfully merged contributions are retrieved. The function paginates through results in increments of 100 items until exhausting the dataset.
Aggregating Contributions by Repository
Once the raw issue data returns, the callback function groups entries by their repository origin. The aggregation logic in js/githubresume.js counts occurrences of each repository_url to determine contribution volume per project:
github_user_issues(username, function(data) {
var sorted = [], repos = {};
$.each(data, function(i, issue) {
if (repos[issue.repository_url] === undefined) {
repos[issue.repository_url] = { popularity: 1 };
} else {
repos[issue.repository_url].popularity += 1;
}
});
$.each(repos, function(repo, obj) {
sorted.push({ repo: repo, popularity: obj.popularity });
});
sorted.sort(function(a, b) { return b.popularity - a.popularity; });
// Rendering logic follows...
});
This aggregation creates a popularity ranking, ensuring repositories with the most merged pull requests appear first in the final display.
Rendering the Contribution List with Mustache Templates
The final step transforms the aggregated data into HTML using the Mustache templating engine. The script asynchronously loads views/contrib.html and binds the sorted repository data to generate contribution entries:
$.ajax({
url: 'views/contrib.html',
dataType: 'html',
success: function(response) {
if (sorted.length > 0) {
$('#contrib-jobs').html('');
$.each(sorted, function(_, repo) {
var repoUrl = repo.repo.replace(/https:\/\/api\.github\.com\/repos/, 'https://github.com');
var repoName = repo.repo.replace(/https:\/\/api\.github\.com\/repos\//, '');
var commitsUrl = repoUrl + '/commits?author=' + username;
var view = {
count: repo.popularity,
username: username,
repoUrl: repoUrl,
repoName: repoName,
commitsUrl: commitsUrl
};
$('#contrib-jobs').append($(Mustache.to_html(response, view)));
});
} else {
$('#contributions').remove();
}
}
});
The template views/contrib.html contains the markup structure:
<div class="contributions">
<p>{{username}} has contributed for <a href="{{repoUrl}}">{{repoName}}</a>
with <a href="{{commitsUrl}}">{{count}} commit(s)</a></p>
</div>
Key Implementation Details in githubresume.js
The contribution tracking system relies on several critical implementation choices within js/githubresume.js:
- JSONP for Cross-Origin Requests: The GitHub API v3 requires authentication for CORS, so the application uses JSONP callbacks (
callback=?) to retrieve public search data without server proxies. - Repository URL Transformation: The code converts API URLs (
https://api.github.com/repos/owner/repo) to web URLs (https://github.com/owner/repo) using regex replacement before generating links. - Commit Link Generation: Each contribution entry links directly to the repository's commit history filtered by the author, constructed by appending
?author=<username>to the commits page URL.
Handling Empty Contribution States
When a user has no merged pull requests, the application gracefully removes the contributions section rather than displaying empty content. As implemented in js/githubresume.js, if the sorted array contains no repositories after aggregation, the script executes $('#contributions').remove(), eliminating the entire contributions panel from the DOM.
Summary
- Merged pull requests are tracked using the GitHub Search API with the query
type:pr is:merged author:<username>, fetched via JSONP injs/githubresume.js. - Contributions are aggregated by
repository_url, counted, and sorted by volume to prioritize repositories with the most activity. - The Mustache template
views/contrib.htmlrenders each repository as a clickable entry linking to the project and the author's specific commits. - Empty states are handled by removing the contributions section entirely when no merged PRs exist.
Frequently Asked Questions
How does the GitHub Resume generator authenticate with the GitHub API to fetch merged pull requests?
The application does not use authentication or API tokens. Instead, it relies on JSONP (JSON with Padding) to access the public GitHub Search API endpoint. By appending callback=? to the request URL, the script bypasses cross-origin restrictions without requiring CORS headers or authentication, though this limits the application to publicly available data.
Why are merged pull requests grouped by repository rather than displayed individually?
Grouping by repository creates a cleaner, more scannable résumé format. The aggregation logic in js/githubresume.js counts how many merged PRs exist per repository_url, then sorts repositories by this count in descending order. This approach highlights the user's most significant collaborations while preventing the page from becoming cluttered with individual pull request links.
What happens if a user has no merged pull requests on GitHub?
If the search returns no results, the sorted array remains empty after aggregation. The rendering logic detects this condition and executes $('#contributions').remove(), completely removing the contributions section from the DOM. This ensures the résumé page does not display empty headings or placeholder text when no contribution data exists.
How are the commit links in the contributions section generated?
For each repository, the script transforms the API URL into a web URL by replacing https://api.github.com/repos with https://github.com. It then constructs the commits URL by appending /commits?author=<username> to the repository path. This creates a direct link to the GitHub commits page filtered to show only the user's contributions to that specific repository.
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 →