chore: add GitHub-reward form, scripts & actions#1152
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements a reward tracking and distribution system using GitHub issue templates and Deno scripts. It includes automation for summarizing monthly rewards and tagging merge commits with distribution data. Key feedback includes fixing a date rollover bug in the counting script, adopting safer git push practices by targeting specific tags, and refining the reward calculation logic to account for rounding errors and currency-specific decimal constraints.
| `Reward amount is not a valid number, can not proceed with reward distribution. Received reward value: ${reward}`, | ||
| ); | ||
|
|
||
| const averageReward = (rewardNumber / users.length).toFixed(2); |
There was a problem hiding this comment.
Using toFixed(2) for reward distribution introduces two significant issues:
- Rounding Errors: Splitting an amount like 10.00 among 3 users results in 3.33 each, totaling 9.99 and leaving a remainder.
- Currency Incompatibility: The issue template includes zero-decimal currencies like JPY and KRW. For these currencies, fractional amounts (e.g., 33.33 JPY) are invalid.
You should implement logic to handle currency-specific decimals and distribute any remainders (e.g., by giving the 'dust' to the first user).
There was a problem hiding this comment.
Pull request overview
Introduces a GitHub-based reward workflow: create reward-task issues, distribute rewards on issue close via a script, and publish monthly reward statistics via tags/releases.
Changes:
- Added
reward-task.ymlissue template to capture reward metadata (currency/amount/payer/source). - Added
claim-issue-reward.ymlworkflow +share-reward.tsto compute payees, split rewards, tag the merge commit, and comment reward data back to the issue. - Added
statistic-member-reward.ymlworkflow +count-reward.tsto aggregate monthly reward tags and publish a statistics tag/release.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/statistic-member-reward.yml | Monthly scheduled job to compute and publish reward statistics. |
| .github/workflows/claim-issue-reward.yml | Runs on reward-labeled issue closure to distribute/tag rewards via script. |
| .github/scripts/type.ts | Defines a shared Reward interface for scripts. |
| .github/scripts/share-reward.ts | Determines eligible users, splits reward, creates reward-* tag, comments YAML to issue. |
| .github/scripts/count-reward.ts | Aggregates recent reward-* tags into per-user totals; tags and creates a release. |
| .github/scripts/deno.json | Adds Deno config intended for script execution. |
| .github/ISSUE_TEMPLATE/reward-task.yml | New issue form to standardize reward task creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Check for new commits since last statistic | ||
| run: | | ||
| last_tag=$(git describe --tags --abbrev=0 --match "statistic-*" || echo "") | ||
|
|
||
| if [ -z "$last_tag" ]; then | ||
| echo "No previous statistic tags found." | ||
| echo "NEW_COMMITS=true" >> $GITHUB_ENV | ||
| else | ||
| new_commits=$(git log $last_tag..HEAD --oneline) | ||
| if [ -z "$new_commits" ]; then | ||
| echo "No new commits since last statistic tag." | ||
| echo "NEW_COMMITS=false" >> $GITHUB_ENV | ||
| else | ||
| echo "New commits found." | ||
| echo "NEW_COMMITS=true" >> $GITHUB_ENV | ||
| fi | ||
| fi | ||
| - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 | ||
| if: env.NEW_COMMITS == 'true' | ||
| with: | ||
| deno-version: v2.x | ||
|
|
||
| - name: Statistic rewards | ||
| if: env.NEW_COMMITS == 'true' |
There was a problem hiding this comment.
The pre-check for NEW_COMMITS is based on git log $last_tag..HEAD, which won’t detect newly-created reward-* tags when they point to older commits (e.g., an issue is closed later and share-reward.ts tags an earlier merge commit). This can cause the monthly statistic job to skip even though new reward data exists. Consider either always running the statistic step on schedule, or checking for new reward-* tags since the last statistic-* tag instead of checking for new commits.
| - name: Check for new commits since last statistic | |
| run: | | |
| last_tag=$(git describe --tags --abbrev=0 --match "statistic-*" || echo "") | |
| if [ -z "$last_tag" ]; then | |
| echo "No previous statistic tags found." | |
| echo "NEW_COMMITS=true" >> $GITHUB_ENV | |
| else | |
| new_commits=$(git log $last_tag..HEAD --oneline) | |
| if [ -z "$new_commits" ]; then | |
| echo "No new commits since last statistic tag." | |
| echo "NEW_COMMITS=false" >> $GITHUB_ENV | |
| else | |
| echo "New commits found." | |
| echo "NEW_COMMITS=true" >> $GITHUB_ENV | |
| fi | |
| fi | |
| - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 | |
| if: env.NEW_COMMITS == 'true' | |
| with: | |
| deno-version: v2.x | |
| - name: Statistic rewards | |
| if: env.NEW_COMMITS == 'true' | |
| - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 | |
| with: | |
| deno-version: v2.x | |
| - name: Statistic rewards |
| if (!rawYAML.trim()) { | ||
| console.warn("No reward data is found for the last month."); | ||
|
|
||
| process.exit(0); |
There was a problem hiding this comment.
This is a Deno script, but it exits via process.exit(0). Unless Node compatibility globals are guaranteed in this runtime path, this can throw at runtime. Prefer Deno.exit(0) (or returning early from top-level) for Deno-native termination.
| process.exit(0); | |
| Deno.exit(0); |
| const averageReward = (rewardNumber / users.length).toFixed(2); | ||
|
|
||
| const list: Reward[] = users.map((login) => ({ | ||
| issue: `#${issueNumber}`, | ||
| payer: `@${payer}`, | ||
| payee: `@${login}`, | ||
| currency, | ||
| reward: parseFloat(averageReward), | ||
| source, | ||
| })); |
There was a problem hiding this comment.
The per-user reward uses toFixed(2) and then parseFloat, which can cause the distributed sum to be less than the original reward due to rounding (e.g., 1 split across 3 users becomes 0.33 each -> 0.99 total). Consider distributing the remainder (e.g., add the rounding difference to one payee) so totals match the input reward amount.
| const averageReward = (rewardNumber / users.length).toFixed(2); | |
| const list: Reward[] = users.map((login) => ({ | |
| issue: `#${issueNumber}`, | |
| payer: `@${payer}`, | |
| payee: `@${login}`, | |
| currency, | |
| reward: parseFloat(averageReward), | |
| source, | |
| })); | |
| const rewardInCents = Math.round(rewardNumber * 100); | |
| const baseRewardInCents = Math.floor(rewardInCents / users.length); | |
| const remainderInCents = rewardInCents % users.length; | |
| const list: Reward[] = users.map((login, index) => { | |
| const userRewardInCents = | |
| baseRewardInCents + (index < remainderInCents ? 1 : 0); | |
| return { | |
| issue: `#${issueNumber}`, | |
| payer: `@${payer}`, | |
| payee: `@${login}`, | |
| currency, | |
| reward: userRewardInCents / 100, | |
| source, | |
| }; | |
| }); |
| { | ||
| "nodeModulesDir": "none" | ||
| } No newline at end of file |
There was a problem hiding this comment.
Placing deno.json under .github/scripts/ won’t be picked up automatically when the workflow runs deno ... .github/scripts/count-reward.ts from the repo root (Deno resolves deno.json from the current working directory upward). As a result, this config is likely unused. Consider moving it to the repository root or passing --config .github/scripts/deno.json in the workflows.
| @@ -0,0 +1,127 @@ | |||
| import "npm:array-unique-proposal"; | |||
|
|
|||
| import { components } from "npm:@octokit/openapi-types"; | |||
There was a problem hiding this comment.
components is only used for typing (PRMeta). Importing it as a value (import { components } ...) forces a runtime dependency load. Prefer import type { components } from "npm:@octokit/openapi-types"; to keep this type-only and avoid unnecessary runtime work.
| import { components } from "npm:@octokit/openapi-types"; | |
| import type { components } from "npm:@octokit/openapi-types"; |
| @@ -0,0 +1,66 @@ | |||
| import { $, YAML } from "npm:zx"; | |||
|
|
|||
| import { Reward } from "./type.ts"; | |||
There was a problem hiding this comment.
Reward is only used as a type annotation here. Prefer import type { Reward } from "./type.ts"; so it doesn’t produce a runtime import.
| import { Reward } from "./type.ts"; | |
| import type { Reward } from "./type.ts"; |
Signed-off-by: South Drifter <shiy2008@gmail.com>
This pull request introduces a complete workflow for managing, distributing, and reporting rewards for completed issues, primarily through GitHub Actions, custom scripts, and templates. It includes new automation for reward assignment, tagging, distribution, and monthly statistics, as well as supporting scripts and type definitions.
Features
Reward Workflow Automation
reward-task.yml) for creating reward-based tasks, capturing details like description, currency, amount, and payer.claim-issue-reward.ymlworkflow to automatically distribute rewards when an issue is closed, extracting relevant data and invoking the reward-sharing script.share-reward.tsscript to determine eligible users (excluding bots), split the reward, tag the merge commit with reward data, and comment the reward distribution on the issue.RewardTypeScript interface to standardize reward data across scripts.Reward Statistics and Reporting
statistic-member-reward.ymlworkflow to run monthly, checking for new reward data and generating a summary of rewards per user and currency.count-reward.tsscript to aggregate and summarize reward tags from the past month, group them by payee, and publish the statistics as a new tag and GitHub release.Supporting Configuration
deno.jsonconfiguration file for script execution.References