Skip to content

Commit 5f2477a

Browse files
committed
Configurable approvals and labels when Codeball has approved a contribution
1 parent 0f6d3ac commit 5f2477a

5 files changed

Lines changed: 109 additions & 34 deletions

File tree

.github/workflows/codeball.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
on: [pull_request]
2-
2+
name: Codeball
33
jobs:
44
codeball_job:
55
runs-on: ubuntu-latest
66
name: Run Codeball
77
steps:
88
- name: Codeball AI Actions
99
uses: sturdy-dev/codeball-action@v1
10-

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
![github_bg](https://user-images.githubusercontent.com/47952/170700847-bb0cac65-f269-4758-955a-632c48f47290.png)
66

7-
87
**Save time and $$$, use Codeball to perform an early review of all your code.**
98

10-
* [Online Demo](https://codeball.ai/)
9+
- [Online Demo](https://codeball.ai/)
1110

1211
## Quick Start
1312

action.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# See https://github.com/sturdy-dev/codeball-action for more information of how to use this aciont
1+
# See https://github.com/sturdy-dev/codeball-action for more information of how to use this action
22
name: Codeball AI Actions
33
description: AI Code Review
44

@@ -7,8 +7,22 @@ branding:
77
icon: check
88
color: orange
99

10+
inputs:
11+
do-label:
12+
description: 'If "true", the action will label Pull Request if Codeball AI approves the contribution'
13+
default: "true"
14+
required: false
15+
label-name:
16+
description: 'Label value to be set.'
17+
default: 'codeball:approved'
18+
required: false
19+
do-approve:
20+
description: 'If "true", the action will submit an approving Pull Request review if Codeball AI approves the contribution'
21+
default: "true"
22+
required: false
23+
1024
runs:
11-
using: "composite"
25+
using: 'composite'
1226
steps:
1327
# Start a new Codeball review job
1428
# This step is asynchronous and will return a job id
@@ -23,3 +37,6 @@ runs:
2337
uses: sturdy-dev/codeball-action/approver@v1
2438
with:
2539
codeball-job-id: ${{ steps.codeball_baller.outputs.codeball-job-id }}
40+
do-label: ${{ inputs.do-label }}
41+
label-name: ${{ inputs.label-name }}
42+
do-approve: ${{ inputs.do-approve }}

approver/action.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
name: 'Codeball Approver'
22
description: 'Codeball Approver (beta)'
3+
34
branding:
45
icon: check
56
color: orange
7+
68
inputs:
79
GITHUB_TOKEN:
810
description: 'Default to {{ github.token }}. This is the default GitHub token available to actions and is used to run Codeball and to post the result. The default token permissions (https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#permissions) work fine.'
@@ -11,6 +13,19 @@ inputs:
1113
codeball-job-id:
1214
description: 'The ID of the Codeball Job created by the Baller Action'
1315
required: true
16+
do-label:
17+
description: 'If "true", the action will label Pull Request if Codeball AI approves the contribution'
18+
default: "true"
19+
required: false
20+
label-name:
21+
description: 'Label value to be set.'
22+
default: 'codeball:approved'
23+
required: false
24+
do-approve:
25+
description: 'If "true", the action will submit an approving Pull Request review if Codeball AI approves the contribution'
26+
default: "true"
27+
required: false
28+
1429
runs:
1530
using: 'node16'
16-
main: '../dist/approver/index.js'
31+
main: '../dist/approver/index.js'

src/approver/main.ts

Lines changed: 72 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fetch from 'node-fetch'
22
import {Job} from './types'
3-
import {isContributionJob, isFinalStatus} from './utils'
3+
import { isContributionJob, isFinalStatus } from "./utils";
44

55
async function getJob(id: string): Promise<Job> {
66
const res = await fetch(`https://api.codeball.ai/jobs/${id}`)
@@ -10,20 +10,33 @@ async function getJob(id: string): Promise<Job> {
1010

1111
async function run(): Promise<void> {
1212
const core = require('@actions/core')
13-
1413
try {
1514
const github = require('@actions/github')
1615
const {Octokit} = require('@octokit/action')
1716

1817
const pullRequestURL = github.context.payload?.pull_request?.html_url
19-
2018
if (!pullRequestURL) {
21-
core.setFailed('No pull request URL found')
22-
return
19+
throw new Error('No pull request URL found')
20+
}
21+
22+
const pullRequestNumber = github.context.payload?.pull_request?.number
23+
if (!pullRequestNumber) {
24+
throw new Error('No pull request number found')
25+
}
26+
27+
const commitId = github.context.payload.pull_request?.head.sha
28+
if (!commitId) {
29+
throw new Error('No commit ID found')
2330
}
2431

2532
const jobID = core.getInput('codeball-job-id')
33+
const doApprove = core.getInput('do-approve') === 'true'
34+
const doLabel = core.getInput('do-label') === 'true'
35+
const labelName = core.getInput('label-name')
36+
2637
core.info(`Job ID: ${jobID}`)
38+
core.info(`Do approve: ${doApprove}`)
39+
core.info(`Do label: ${doLabel} with value: ${labelName}`)
2740

2841
let job = await getJob(jobID)
2942
let attempts = 0
@@ -38,33 +51,71 @@ async function run(): Promise<void> {
3851
}
3952

4053
if (!isFinalStatus(job.status)) {
41-
core.setFailed(`Job ${jobID} is not finished`)
42-
return
54+
throw new Error(`Job ${jobID} is not finished`)
4355
}
4456

4557
if (!isContributionJob(job)) {
46-
core.setFailed(`Job ${jobID} is not a contribution job`)
47-
return
58+
throw new Error(`Job ${jobID} is not a contribution job`)
4859
}
4960

5061
const approved = job.contribution?.result === 'approved'
5162

63+
const octokit = new Octokit()
5264
if (approved) {
5365
core.info(`Job ${jobID} is approved, approving the PR now!`)
5466

55-
const octokit = new Octokit()
67+
if (doLabel) {
68+
core.debug(`Adding label "${labelName}" to PR ${pullRequestURL}`)
5669

57-
await octokit.request(
58-
'POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews',
59-
{
60-
owner: github.context.payload.organization.login,
61-
repo: github.context.payload.repository.name,
62-
pull_number: github.context.payload.pull_request.number,
63-
commit_id: github.context.payload.pull_request.head.sha,
70+
const existingLabels = await octokit.issues.listLabelsForRepo({
71+
owner: github.context.repo.owner,
72+
repo: github.context.repo.repo
73+
})
74+
75+
let haveLabel = false
76+
for (const label of existingLabels.data) {
77+
if (label.name === labelName) {
78+
haveLabel = true
79+
break
80+
}
81+
}
82+
83+
if (!haveLabel) {
84+
core.info(
85+
`Label "${labelName}" does not exist, creating it now`
86+
)
87+
await octokit.issues.createLabel({
88+
owner: github.context.repo.owner,
89+
repo: github.context.repo.repo,
90+
name: labelName,
91+
color: '008E43',
92+
description: 'Codeball approved this pull request'
93+
})
94+
} else {
95+
core.debug(
96+
`Label "${labelName}" already exists, will not create it`
97+
)
98+
}
99+
100+
await octokit.issues.addLabels({
101+
owner: github.context.repo.owner,
102+
repo: github.context.repo.repo,
103+
issue_number: pullRequestNumber,
104+
labels: [labelName]
105+
})
106+
}
107+
108+
if (doApprove) {
109+
core.debug(`Approving PR ${pullRequestURL}`)
110+
await octokit.pulls.createReview({
111+
owner: github.context.repo.owner,
112+
repo: github.context.repo.repo,
113+
pull_number: pullRequestNumber,
114+
commit_id: commitId,
64115
body: 'Codeball: LGTM! :+1:',
65116
event: 'APPROVE'
66-
}
67-
)
117+
})
118+
}
68119
} else {
69120
core.info(`Job ${jobID} is not approved, will not approve the PR`)
70121
}
@@ -76,15 +127,9 @@ async function run(): Promise<void> {
76127
{data: 'Pull Request', header: true},
77128
{data: 'Result', header: true}
78129
],
79-
[
80-
`#${github.context.payload.pull_request.number}`,
81-
approved ? 'Approved ✅' : 'Not approved'
82-
]
130+
[`#${pullRequestNumber}`, approved ? 'Approved ✅' : 'Not approved']
83131
])
84-
.addLink(
85-
'View on web',
86-
`https://codeball.ai/prediction/${jobID}`
87-
)
132+
.addLink('View on web', `https://codeball.ai/prediction/${jobID}`)
88133
.write()
89134
} catch (error) {
90135
if (error instanceof Error) core.setFailed(error.message)

0 commit comments

Comments
 (0)