Update Hacktoberfest Leaderboard #5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Update Hacktoberfest Leaderboard | |
on: | |
schedule: | |
- cron: '0 * * * *' # Runs every hour at the start of the hour | |
workflow_dispatch: # Allows manual triggering | |
jobs: | |
update-leaderboard: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v2 | |
- name: Update Leaderboard | |
uses: actions/github-script@v6 | |
with: | |
github-token: ${{ secrets.HACKTOBERFEST_LEADERBOARD_TOKEN }} | |
script: | | |
const owner = context.repo.owner; | |
const repo = context.repo.repo; | |
const issueNumber = 1; // Replace with your actual issue number | |
const REPOS = [ | |
'galaxy-bytes/main-test-repo', | |
'GCodeHouse/Cohort4', | |
'TBD54566975/developer.tbd.website' | |
//'blackgirlbytes/LunaFocus' | |
]; | |
const POINT_VALUES = { | |
small: 5, | |
medium: 10, | |
large: 15 | |
}; | |
const calculatePoints = (labels) => { | |
const size = labels.find(label => POINT_VALUES[label.name.toLowerCase()]); | |
return size ? POINT_VALUES[size.name.toLowerCase()] : POINT_VALUES.small; | |
}; | |
const fetchRecentPRs = async (repo) => { | |
try { | |
console.log(`Fetching recent PRs for ${repo}`); | |
const [repoOwner, repoName] = repo.split('/'); | |
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); | |
const { data: prs } = await github.rest.pulls.list({ | |
owner: repoOwner, | |
repo: repoName, | |
state: 'closed', | |
sort: 'updated', | |
direction: 'desc', | |
per_page: 100 | |
}); | |
console.log(`Fetched ${prs.length} PRs for ${repo}`); | |
const hacktoberfestPRs = prs.filter(pr => { | |
const isMerged = !!pr.merged_at; | |
const isRecent = new Date(pr.merged_at) > new Date(thirtyDaysAgo); | |
const isHacktoberfest = pr.labels.some(label => label.name.toLowerCase() === 'hacktoberfest'); | |
console.log(`PR #${pr.number}: merged=${isMerged}, recent=${isRecent}, hacktoberfest=${isHacktoberfest}`); | |
return isMerged && isRecent && isHacktoberfest; | |
}).map(pr => ({ | |
user: pr.user.login, | |
points: calculatePoints(pr.labels), | |
repo: repo, | |
prNumber: pr.number, | |
prTitle: pr.title, | |
})); | |
console.log(`Found ${hacktoberfestPRs.length} qualifying PRs for ${repo}`); | |
return hacktoberfestPRs; | |
} catch (error) { | |
console.error(`Error fetching PRs for ${repo}: ${error.message}`); | |
return []; | |
} | |
}; | |
const generateLeaderboard = async () => { | |
try { | |
const allPRs = await Promise.all(REPOS.map(fetchRecentPRs)); | |
const flatPRs = allPRs.flat(); | |
console.log(`Total qualifying PRs found: ${flatPRs.length}`); | |
console.log('Qualifying PRs:', JSON.stringify(flatPRs, null, 2)); | |
const leaderboard = flatPRs.reduce((acc, pr) => { | |
if (!acc[pr.user]) acc[pr.user] = { points: 0, prs: 0 }; | |
acc[pr.user].points += pr.points; | |
acc[pr.user].prs += 1; | |
return acc; | |
}, {}); | |
console.log('Leaderboard object:', JSON.stringify(leaderboard, null, 2)); | |
const sortedLeaderboard = Object.entries(leaderboard) | |
.sort(([, a], [, b]) => b.points - a.points) | |
.map(([username, data], index) => ({ | |
rank: index + 1, | |
username, | |
points: data.points, | |
prs: data.prs | |
})); | |
console.log(`Generated leaderboard with ${sortedLeaderboard.length} entries`); | |
console.log('Sorted leaderboard:', JSON.stringify(sortedLeaderboard, null, 2)); | |
return sortedLeaderboard; | |
} catch (error) { | |
console.error(`Error generating leaderboard: ${error.message}`); | |
return []; | |
} | |
}; | |
const updateIssue = async (leaderboardData) => { | |
const issueBody = ` | |
# Hacktoberfest Leaderboard | |
| Rank | Contributor | Points | PRs | | |
|------|-------------|--------|-----| | |
${leaderboardData.map(entry => `| ${entry.rank} | @${entry.username} | ${entry.points} | ${entry.prs} |`).join('\n')} | |
Last updated: ${new Date().toUTCString()} | |
`; | |
try { | |
await github.rest.issues.update({ | |
owner, | |
repo, | |
issue_number: issueNumber, | |
body: issueBody | |
}); | |
console.log("Issue updated successfully!"); | |
console.log("Updated issue body:", issueBody); | |
} catch (error) { | |
throw new Error(`Failed to update issue: ${error.message}`); | |
} | |
}; | |
// Main execution | |
const leaderboardData = await generateLeaderboard(); | |
if (leaderboardData.length > 0) { | |
await updateIssue(leaderboardData); | |
} else { | |
console.log("No leaderboard data to update."); | |
const emptyIssueBody = ` | |
# Hacktoberfest Leaderboard | |
No qualifying PRs found at this time. | |
Last updated: ${new Date().toUTCString()} | |
`; | |
await github.rest.issues.update({ | |
owner, | |
repo, | |
issue_number: issueNumber, | |
body: emptyIssueBody | |
}); | |
console.log("Updated issue with empty leaderboard message."); | |
} |