-
Notifications
You must be signed in to change notification settings - Fork 7
[add] GitHub actions of GitHub-reward & Lark-GitHub-bot #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| name: 💰 Reward Task | ||
| description: Task issue with Reward | ||
| title: '[Reward] ' | ||
| labels: | ||
| - reward | ||
| body: | ||
| - type: textarea | ||
| id: description | ||
| attributes: | ||
| label: Task description | ||
| validations: | ||
| required: true | ||
|
|
||
| - type: dropdown | ||
| id: currency | ||
| attributes: | ||
| label: Reward currency | ||
| options: | ||
| - 'USD $' | ||
| - 'CAD C$' | ||
| - 'AUD A$' | ||
| - 'GBP £' | ||
| - 'EUR €' | ||
| - 'CNY ¥' | ||
| - 'HKD HK$' | ||
| - 'TWD NT$' | ||
| - 'SGD S$' | ||
| - 'KRW ₩' | ||
| - 'JPY ¥' | ||
| - 'INR ₹' | ||
| - 'UAH ₴' | ||
| validations: | ||
| required: true | ||
|
|
||
| - type: input | ||
| id: amount | ||
| attributes: | ||
| label: Reward amount | ||
| validations: | ||
| required: true | ||
|
|
||
| - type: input | ||
| id: payer | ||
| attributes: | ||
| label: Reward payer | ||
| description: GitHub username of the payer (optional, defaults to issue creator) | ||
| validations: | ||
| required: false |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { $, YAML } from 'npm:zx'; | ||
|
|
||
| import { Reward } from './type.ts'; | ||
|
|
||
| $.verbose = true; | ||
|
|
||
| const rawTags = await $`git tag --list "reward-*" --format="%(refname:short) %(creatordate:short)"`; | ||
|
|
||
| const lastMonth = new Date(); | ||
| lastMonth.setMonth(lastMonth.getMonth() - 1); | ||
| const lastMonthStr = lastMonth.toJSON().slice(0, 7); | ||
|
|
||
| const rewardTags = rawTags.stdout | ||
| .split('\n') | ||
| .filter(line => line.split(/\s+/)[1] >= lastMonthStr) | ||
| .map(line => line.split(/\s+/)[0]); | ||
|
|
||
| let rawYAML = ''; | ||
|
|
||
| for (const tag of rewardTags) rawYAML += (await $`git tag -l --format="%(contents)" ${tag}`) + '\n'; | ||
|
|
||
| if (!rawYAML.trim()) throw new ReferenceError('No reward data is found for the last month.'); | ||
|
|
||
| const rewards = YAML.parse(rawYAML) as Reward[]; | ||
|
|
||
| const groupedRewards = Object.groupBy(rewards, ({ payee }) => payee); | ||
|
|
||
| const summaryList = Object.entries(groupedRewards).map(([payee, rewards]) => { | ||
| const reward = rewards!.reduce( | ||
| (acc, { currency, reward }) => { | ||
| acc[currency] ??= 0; | ||
| acc[currency] += reward; | ||
| return acc; | ||
| }, | ||
| {} as Record<string, number> | ||
| ); | ||
|
|
||
| return { | ||
| payee, | ||
| reward, | ||
| accounts: rewards!.map(({ payee: _, ...account }) => account) | ||
| }; | ||
| }); | ||
|
|
||
| const summaryText = YAML.stringify(summaryList); | ||
|
|
||
| console.log(summaryText); | ||
|
|
||
| const tagName = `statistic-${new Date().toJSON().slice(0, 7)}`; | ||
|
|
||
| await $`git config user.name "github-actions[bot]"`; | ||
| await $`git config user.email "github-actions[bot]@users.noreply.github.com"`; | ||
|
|
||
| await $`git tag -a ${tagName} $(git rev-parse HEAD) -m ${summaryText}`; | ||
| await $`git push origin --tags --no-verify`; | ||
|
|
||
| await $`gh release create ${tagName} --notes ${summaryText}`; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "nodeModulesDir": "none" | ||
| } | ||
|
Comment on lines
+1
to
+3
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,113 @@ | ||||||||||||||
| import { components } from 'npm:@octokit/openapi-types'; | ||||||||||||||
| import { $, argv, YAML } from 'npm:zx'; | ||||||||||||||
|
|
||||||||||||||
| import { Reward } from './type.ts'; | ||||||||||||||
|
|
||||||||||||||
| $.verbose = true; | ||||||||||||||
|
|
||||||||||||||
| const [ | ||||||||||||||
| repositoryOwner, | ||||||||||||||
| repositoryName, | ||||||||||||||
| issueNumber, | ||||||||||||||
| payer, // GitHub username of the payer (provided by workflow, defaults to issue creator) | ||||||||||||||
| currency, | ||||||||||||||
| reward | ||||||||||||||
| ] = argv._; | ||||||||||||||
|
|
||||||||||||||
| interface PRMeta { | ||||||||||||||
| author: components['schemas']['simple-user']; | ||||||||||||||
| assignees: components['schemas']['simple-user'][]; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| const graphqlQuery = ` | ||||||||||||||
| query($owner: String!, $name: String!, $number: Int!) { | ||||||||||||||
| repository(owner: $owner, name: $name) { | ||||||||||||||
| issue(number: $number) { | ||||||||||||||
| closedByPullRequestsReferences(first: 10) { | ||||||||||||||
| nodes { | ||||||||||||||
| url | ||||||||||||||
| merged | ||||||||||||||
| mergeCommit { | ||||||||||||||
| oid | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| `; | ||||||||||||||
| const PR_DATA = await $`gh api graphql \ | ||||||||||||||
| -f query=${graphqlQuery} \ | ||||||||||||||
| -f owner=${repositoryOwner} \ | ||||||||||||||
| -f name=${repositoryName} \ | ||||||||||||||
| -F number=${issueNumber} \ | ||||||||||||||
| --jq '.data.repository.issue.closedByPullRequestsReferences.nodes[] | select(.merged == true) | {url: .url, mergeCommitSha: .mergeCommit.oid}' | head -n 1`; | ||||||||||||||
|
|
||||||||||||||
| const prData = PR_DATA.text().trim(); | ||||||||||||||
|
|
||||||||||||||
| if (!prData) throw new ReferenceError('No merged PR is found for the given issue number.'); | ||||||||||||||
|
|
||||||||||||||
| const { url: PR_URL, mergeCommitSha } = JSON.parse(prData); | ||||||||||||||
|
Comment on lines
+39
to
+50
|
||||||||||||||
|
|
||||||||||||||
| if (!PR_URL || !mergeCommitSha) throw new Error('Missing required fields in PR data'); | ||||||||||||||
|
|
||||||||||||||
| console.table({ PR_URL, mergeCommitSha }); | ||||||||||||||
|
|
||||||||||||||
| const { author, assignees }: PRMeta = await ( | ||||||||||||||
| await $`gh pr view ${PR_URL} --json author,assignees` | ||||||||||||||
| ).json(); | ||||||||||||||
|
|
||||||||||||||
| function isBotUser(login: string) { | ||||||||||||||
| const lowerLogin = login.toLowerCase(); | ||||||||||||||
| return ( | ||||||||||||||
| lowerLogin.includes('copilot') || | ||||||||||||||
| lowerLogin.includes('[bot]') || | ||||||||||||||
| lowerLogin === 'github-actions[bot]' || | ||||||||||||||
| lowerLogin.endsWith('[bot]') | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // Filter out Bot users from the list | ||||||||||||||
| const allUsers = [author.login, ...assignees.map(({ login }) => login)]; | ||||||||||||||
| const users = allUsers.filter(login => !isBotUser(login)); | ||||||||||||||
|
Comment on lines
+70
to
+72
|
||||||||||||||
| // Filter out Bot users from the list | |
| const allUsers = [author.login, ...assignees.map(({ login }) => login)]; | |
| const users = allUsers.filter(login => !isBotUser(login)); | |
| // Filter out Bot users from the list and deduplicate logins | |
| const allUsers = [author.login, ...assignees.map(({ login }) => login)]; | |
| const users = [...new Set(allUsers.filter(login => !isBotUser(login)))]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the loop building
rawYAML, you’re concatenating the$command result object directly into a string. In zx this will not reliably produce the command stdout (often becomes "[object Object]"), which will breakYAML.parse. Use the command output explicitly (e.g.,.stdoutor.text()) when appending tag contents.