Update Loose Goose Leaderboard #204
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 Loose Goose Leaderboard | |
on: | |
schedule: | |
- cron: '0 * * * *' # Runs every hour at the start of the hour | |
workflow_dispatch: # Allows manual triggering | |
permissions: | |
issues: write | |
pull-requests: read | |
jobs: | |
update-leaderboard: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Update Leaderboard | |
uses: actions/github-script@v6 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const owner = context.repo.owner; | |
const repo = context.repo.repo; | |
const issueNumber = 46; // Change this to match your issue number | |
const REPOS = [ | |
'block-open-source/goose-plugins', // Update to your repo(s) | |
]; | |
const calculatePoints = (labels) => { | |
const labelNames = labels.map(label => label.name.toLowerCase()); | |
const hasLooseGoose = labelNames.includes('loosegoose'); | |
const hasCode = labelNames.includes('code'); | |
return { | |
points: hasLooseGoose && hasCode ? 15 : (hasLooseGoose ? 10 : 0), | |
isCodePR: hasCode, | |
isContentPR: hasLooseGoose && !hasCode | |
}; | |
}; | |
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 loosegoosePRs = prs.filter(pr => { | |
const isMerged = !!pr.merged_at; | |
const isRecent = new Date(pr.merged_at) > new Date(thirtyDaysAgo); | |
const isLoosegoose = pr.labels.some(label => label.name.toLowerCase() === 'loosegoose'); | |
return isMerged && isRecent && isLoosegoose; | |
}).map(pr => { | |
const pointsData = calculatePoints(pr.labels); | |
return { | |
user: pr.user.login, | |
points: pointsData.points, | |
isCodePR: pointsData.isCodePR, | |
isContentPR: pointsData.isContentPR, | |
repo: repo, | |
prNumber: pr.number, | |
prTitle: pr.title, | |
}; | |
}); | |
return loosegoosePRs; | |
} 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(); | |
const leaderboard = flatPRs.reduce((acc, pr) => { | |
if (!acc[pr.user]) { | |
acc[pr.user] = { | |
points: 0, | |
prs: 0, | |
codePRs: 0, | |
contentPRs: 0 | |
}; | |
} | |
acc[pr.user].points += pr.points; | |
acc[pr.user].prs += 1; | |
if (pr.isCodePR) { | |
acc[pr.user].codePRs += 1; | |
} | |
if (pr.isContentPR) { | |
acc[pr.user].contentPRs += 1; | |
} | |
return acc; | |
}, {}); | |
return Object.entries(leaderboard) | |
.sort(([, a], [, b]) => b.points - a.points) | |
.map(([username, data], index) => ({ | |
rank: index + 1, | |
username, | |
points: data.points, | |
prs: data.prs, | |
codePRs: data.codePRs, | |
contentPRs: data.contentPRs | |
})); | |
} catch (error) { | |
console.error(`Error generating leaderboard: ${error.message}`); | |
return []; | |
} | |
}; | |
const updateIssue = async (leaderboardData) => { | |
try { | |
const rows = leaderboardData.map(entry => | |
`| ${entry.rank} | @${entry.username} | ${entry.points} | ${entry.prs} | ${entry.codePRs} | ${entry.contentPRs} |` | |
).join('\n'); | |
const issueBody = [ | |
'# π Loose Goose November 2024 Leaderboard π', | |
'You thought Hacktoberfest 2024 was over?! The open source party is just getting started..! Wrap up 2024 with a bang (or honk) while enjoying super-fun contributions with Loose Goose November. From Nov 1st to Nov 20th, check our live leaderboard below to see who our top contributors are in real-time. Not only does this recognize everyone\'s efforts, it also brings an amplified competitive vibe with each contribution. π', | |
'', | |
'## This event is open to both employees and external contributors! π¦’', | |
'### π **Current Rankings:**', | |
'| Rank | Contributor | Points | Total PRs | Code PRs | Content PRs |', | |
'|------|-------------|--------|-----------|-----------|-------------|', | |
rows, | |
'', | |
'### π How It Works:', | |
'The top 3 contributors with the most points will earn $$$ gift cards (more on rewards below). Points are awarded based on the following criteria:', | |
'- 15 points for each merged PR with both `loosegoose` and `code` labels', | |
'- 10 points for each merged PR with just the `loosegoose` label (Content PR)', | |
'', | |
'### π Rewards', | |
'- Among our **top 3**? Our Top 3 Superstars earn $150 gift cards on Amazon. Stay tuned for the winners!', | |
'', | |
'### FAQ', | |
'- **Frequency of Updates:** The leaderboard will be updated every 1 hour.', | |
'- **Points System:**', | |
' - 15 points: Code PRs (PRs with both `loosegoose` and `code` labels)', | |
' - 10 points: Content PRs (PRs with just the `loosegoose` label)', | |
'- **Criteria:** Rankings are based on how many points you earn and PRs you close in the goose-plugins repo. To ensure your PRs are successfully merged:', | |
' - Ensure your contributions are aligned with our [project\'s CoC](https://github.com/block-open-source/goose-plugins/blob/main/CODE_OF_CONDUCT.md).', | |
' - Refer to our [Contributing Guide](https://github.com/block-open-source/goose-plugins/blob/main/CONTRIBUTING.md).', | |
'- **Tie-Breaker:** In the event of a tie-breaker, the tie-breaking PR that was submitted soonest will break the tie.', | |
'', | |
'### π Get Featured:', | |
'Want to see your name climbing our ranks? Explore our content issues with the labels `loosegoose` in our Loose Goose Project Hub:', | |
'- [ ] #32', | |
'', | |
'Excited to see everyone\'s hard work. Thank you so much for your invaluable contributions, and let the fun competition begin!', | |
`Last updated: ${new Date().toUTCString()}` | |
].join('\n'); | |
await github.rest.issues.update({ | |
owner, | |
repo, | |
issue_number: issueNumber, | |
body: issueBody | |
}); | |
console.log("Issue updated successfully!"); | |
} 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 = [ | |
'# π Loose Goose November 2024 Leaderboard π', | |
'You thought Hacktoberfest 2024 was over?! The open source party is just getting started..! Wrap up 2024 with a bang (or honk) while enjoying super-fun contributions with Loose Goose November. From Nov 1st to Nov 20th, check our live leaderboard below to see who our top contributors are in real-time. Not only does this recognize everyone\'s efforts, it also brings an amplified competitive vibe with each contribution. π', | |
'', | |
'## This event is open to both employees and external contributors! π¦’', | |
'### π **Current Rankings:**', | |
'| Rank | Contributor | Points | Total PRs | Code PRs | Content PRs |', | |
'|------|-------------|--------|-----------|-----------|-------------|', | |
'No qualifying PRs found at this time. Check back soon!', | |
'', | |
'### π How It Works:', | |
'The top 3 contributors with the most points will earn $$$ gift cards (more on rewards below). Points are awarded based on the following criteria:', | |
'- 15 points for each merged PR with both `loosegoose` and `code` labels (Code PR)', | |
'- 10 points for each merged PR with just the `loosegoose` label (Content PR)', | |
'', | |
'### π Rewards', | |
'- Among our **top 3**? Our Top 3 Superstars earn $150 gift cards on Amazon. Stay tuned for the winners!', | |
'', | |
'### FAQ', | |
'- **Frequency of Updates:** The leaderboard will be updated every 1 hour.', | |
'- **Points System:**', | |
' - 15 points: Code PRs (PRs with both `loosegoose` and `code` labels)', | |
' - 10 points: Content PRs (PRs with just the `loosegoose` label)', | |
'- **Criteria:** Rankings are based on how many points you earn and PRs you close in the goose-plugins repo. To ensure your PRs are successfully merged:', | |
' - Ensure your contributions are aligned with our [project\'s CoC](https://github.com/block-open-source/goose-plugins/blob/main/CODE_OF_CONDUCT.md).', | |
' - Refer to our [Contributing Guide](https://github.com/block-open-source/goose-plugins/blob/main/CONTRIBUTING.md).', | |
'- **Tie-Breaker:** In the event of a tie-breaker, the tie-breaking PR that was submitted soonest will break the tie.', | |
'', | |
'### π Get Featured:', | |
'Want to see your name climbing our ranks? Explore our content issues with the labels `loosegoose` in our Loose Goose Project Hub:', | |
'- [ ] #32', | |
'', | |
'Excited to see everyone\'s hard work. Thank you so much for your invaluable contributions, and let the fun competition begin!', | |
`Last updated: ${new Date().toUTCString()}` | |
].join('\n'); | |
await github.rest.issues.update({ | |
owner, | |
repo, | |
issue_number: issueNumber, | |
body: emptyIssueBody | |
}); | |
console.log("Updated issue with empty leaderboard message."); | |
} |