-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: auto label discussions (#5259)
- Loading branch information
Showing
10 changed files
with
334 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.PHONEY: test | ||
test: helpers.js helpers.test.js | ||
node --test helpers.test.js |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
name: 'trivy-discussion-triage' | ||
description: 'automatic triage of Trivy discussions' | ||
inputs: | ||
discussion_num: | ||
description: 'Discussion number to triage' | ||
required: false | ||
runs: | ||
using: "composite" | ||
steps: | ||
- name: Conditionally label discussions based on category and content | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
uses: actions/github-script@v6 | ||
with: | ||
script: | | ||
const {detectDiscussionLabels, fetchDiscussion, labelDiscussion } = require('${{ github.action_path }}/helpers.js'); | ||
const config = require('${{ github.action_path }}/config.json'); | ||
discussionNum = parseInt(${{ inputs.discussion_num }}); | ||
let discussion; | ||
if (discussionNum > 0) { | ||
discussion = (await fetchDiscussion(github, context.repo.owner, context.repo.repo, discussionNum)).repository.discussion; | ||
} else { | ||
discussion = context.payload.discussion; | ||
} | ||
const labels = detectDiscussionLabels(discussion, config.discussionLabels); | ||
if (labels.length > 0) { | ||
console.log(`Adding labels ${labels} to discussion ${discussion.node_id}`); | ||
labelDiscussion(github, discussion.node_id, labels); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"discussionLabels": { | ||
"Container Image":"LA_kwDOCsUTCM75TTQU", | ||
"Filesystem":"LA_kwDOCsUTCM75TTQX", | ||
"Git Repository":"LA_kwDOCsUTCM75TTQk", | ||
"Virtual Machine Image":"LA_kwDOCsUTCM8AAAABMpz1bw", | ||
"Kubernetes":"LA_kwDOCsUTCM75TTQv", | ||
"AWS":"LA_kwDOCsUTCM8AAAABMpz1aA", | ||
"Vulnerability":"LA_kwDOCsUTCM75TTPa", | ||
"Misconfiguration":"LA_kwDOCsUTCM75TTP8", | ||
"License":"LA_kwDOCsUTCM77ztRR", | ||
"Secret":"LA_kwDOCsUTCM75TTQL" | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
module.exports = { | ||
detectDiscussionLabels: (discussion, configDiscussionLabels) => { | ||
res = []; | ||
const discussionId = discussion.id; | ||
const category = discussion.category.name; | ||
const body = discussion.body; | ||
if (category !== "Ideas") { | ||
consolt.log("skipping discussion with category ${category} and body ${body}"); | ||
} | ||
const scannerPattern = /### Scanner\n\n(.+)/; | ||
const scannerFound = body.match(scannerPattern); | ||
if (scannerFound && scannerFound.length > 1) { | ||
res.push(configDiscussionLabels[scannerFound[1]]); | ||
} | ||
const targetPattern = /### Target\n\n(.+)/; | ||
const targetFound = body.match(targetPattern); | ||
if (targetFound && targetFound.length > 1) { | ||
res.push(configDiscussionLabels[targetFound[1]]); | ||
} | ||
return res; | ||
}, | ||
fetchDiscussion: async (github, owner, repo, discussionNum) => { | ||
const query = `query Discussion ($owner: String!, $repo: String!, $discussion_num: Int!){ | ||
repository(name: $repo, owner: $owner) { | ||
discussion(number: $discussion_num) { | ||
number, | ||
id, | ||
body, | ||
category { | ||
id, | ||
name | ||
}, | ||
labels(first: 100) { | ||
edges { | ||
node { | ||
id, | ||
name | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}`; | ||
const vars = { | ||
owner: owner, | ||
repo: repo, | ||
discussion_num: discussionNum | ||
}; | ||
return github.graphql(query, vars); | ||
}, | ||
labelDiscussion: async (github, discussionId, labelIds) => { | ||
const query = `mutation AddLabels($labelId: ID!, $labelableId:ID!) { | ||
addLabelsToLabelable( | ||
input: {labelIds: [$labelId], labelableId: $labelableId} | ||
) { | ||
clientMutationId | ||
} | ||
}`; | ||
// TODO: add all labels in one call | ||
labelIds.forEach((labelId) => { | ||
const vars = { | ||
labelId: labelId, | ||
labelableId: discussionId | ||
}; | ||
github.graphql(query, vars); | ||
}); | ||
} | ||
}; | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
const assert = require('node:assert/strict'); | ||
const { describe, it } = require('node:test'); | ||
const {detectDiscussionLabels} = require('./helpers.js'); | ||
|
||
const configDiscussionLabels = { | ||
"Container Image":"ContainerImageLabel", | ||
"Filesystem":"FilesystemLabel", | ||
"Vulnerability":"VulnerabilityLabel", | ||
"Misconfiguration":"MisconfigurationLabel", | ||
}; | ||
|
||
describe('trivy-triage', async function() { | ||
describe('detectDiscussionLabels', async function() { | ||
it('detect scanner label', async function() { | ||
const discussion = { | ||
body: 'hello hello\nbla bla.\n### Scanner\n\nVulnerability\n### Target\n\nContainer Image\nbye bye.', | ||
category: { | ||
name: 'Ideas' | ||
} | ||
}; | ||
const labels = detectDiscussionLabels(discussion, configDiscussionLabels); | ||
assert(labels.includes('VulnerabilityLabel')); | ||
}); | ||
it('detect target label', async function() { | ||
const discussion = { | ||
body: 'hello hello\nbla bla.\n### Scanner\n\nVulnerability\n### Target\n\nContainer Image\nbye bye.', | ||
category: { | ||
name: 'Ideas' | ||
} | ||
}; | ||
const labels = detectDiscussionLabels(discussion, configDiscussionLabels); | ||
assert(labels.includes('ContainerImageLabel')); | ||
}); | ||
it('detect label when it is first', async function() { | ||
const discussion = { | ||
body: '### Scanner\n\nVulnerability\n### Target\n\nContainer Image\nbye bye.', | ||
category: { | ||
name: 'Ideas' | ||
} | ||
}; | ||
const labels = detectDiscussionLabels(discussion, configDiscussionLabels); | ||
assert(labels.includes('ContainerImageLabel')); | ||
}); | ||
it('detect label when it is last', async function() { | ||
const discussion = { | ||
body: '### Scanner\n\nVulnerability\n### Target\n\nContainer Image', | ||
category: { | ||
name: 'Ideas' | ||
} | ||
}; | ||
const labels = detectDiscussionLabels(discussion, configDiscussionLabels); | ||
assert(labels.includes('ContainerImageLabel')); | ||
}); | ||
it('detect scanner and target labels', async function() { | ||
const discussion = { | ||
body: 'hello hello\nbla bla.\n### Scanner\n\nVulnerability\n### Target\n\nContainer Image\nbye bye.', | ||
category: { | ||
name: 'Ideas' | ||
} | ||
}; | ||
const labels = detectDiscussionLabels(discussion, configDiscussionLabels); | ||
assert(labels.includes('ContainerImageLabel')); | ||
assert(labels.includes('VulnerabilityLabel')); | ||
}); | ||
it('not detect other labels', async function() { | ||
const discussion = { | ||
body: 'hello hello\nbla bla.\n### Scanner\n\nVulnerability\n### Target\n\nContainer Image\nbye bye.', | ||
category: { | ||
name: 'Ideas' | ||
} | ||
}; | ||
const labels = detectDiscussionLabels(discussion, configDiscussionLabels); | ||
assert(!labels.includes('FilesystemLabel')); | ||
assert(!labels.includes('MisconfigurationLabel')); | ||
}); | ||
}); | ||
}); |
65 changes: 65 additions & 0 deletions
65
.github/actions/trivy-triage/testutils/discussion-payload-sample.json
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
{ | ||
"active_lock_reason": null, | ||
"answer_chosen_at": null, | ||
"answer_chosen_by": null, | ||
"answer_html_url": null, | ||
"author_association": "OWNER", | ||
"body": "### Description\n\nlfdjs lfkdj dflsakjfd ';djk \r\nfadfd \r\nasdlkf \r\na;df \r\ndfsal;kfd ;akjl\n\n### Target\n\nContainer Image\n\n### Scanner\n\nMisconfiguration", | ||
"category": { | ||
"created_at": "2023-07-02T10:14:46.000+03:00", | ||
"description": "Share ideas for new features", | ||
"emoji": ":bulb:", | ||
"id": 39743708, | ||
"is_answerable": false, | ||
"name": "Ideas", | ||
"node_id": "DIC_kwDOE0GiPM4CXnDc", | ||
"repository_id": 323068476, | ||
"slug": "ideas", | ||
"updated_at": "2023-07-02T10:14:46.000+03:00" | ||
}, | ||
"comments": 0, | ||
"created_at": "2023-09-11T08:40:11Z", | ||
"html_url": "https://github.com/itaysk/testactions/discussions/9", | ||
"id": 5614504, | ||
"locked": false, | ||
"node_id": "D_kwDOE0GiPM4AVauo", | ||
"number": 9, | ||
"reactions": { | ||
"+1": 0, | ||
"-1": 0, | ||
"confused": 0, | ||
"eyes": 0, | ||
"heart": 0, | ||
"hooray": 0, | ||
"laugh": 0, | ||
"rocket": 0, | ||
"total_count": 0, | ||
"url": "https://api.github.com/repos/itaysk/testactions/discussions/9/reactions" | ||
}, | ||
"repository_url": "https://api.github.com/repos/itaysk/testactions", | ||
"state": "open", | ||
"state_reason": null, | ||
"timeline_url": "https://api.github.com/repos/itaysk/testactions/discussions/9/timeline", | ||
"title": "Title title", | ||
"updated_at": "2023-09-11T08:40:11Z", | ||
"user": { | ||
"avatar_url": "https://avatars.githubusercontent.com/u/1161307?v=4", | ||
"events_url": "https://api.github.com/users/itaysk/events{/privacy}", | ||
"followers_url": "https://api.github.com/users/itaysk/followers", | ||
"following_url": "https://api.github.com/users/itaysk/following{/other_user}", | ||
"gists_url": "https://api.github.com/users/itaysk/gists{/gist_id}", | ||
"gravatar_id": "", | ||
"html_url": "https://github.com/itaysk", | ||
"id": 1161307, | ||
"login": "itaysk", | ||
"node_id": "MDQ6VXNlcjExNjEzMDc=", | ||
"organizations_url": "https://api.github.com/users/itaysk/orgs", | ||
"received_events_url": "https://api.github.com/users/itaysk/received_events", | ||
"repos_url": "https://api.github.com/users/itaysk/repos", | ||
"site_admin": false, | ||
"starred_url": "https://api.github.com/users/itaysk/starred{/owner}{/repo}", | ||
"subscriptions_url": "https://api.github.com/users/itaysk/subscriptions", | ||
"type": "User", | ||
"url": "https://api.github.com/users/itaysk" | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#! /bin/bash | ||
# fetch discussion by discussion number | ||
# requires authenticated gh cli, assumes repo but current git repository | ||
# args: | ||
# $1: discussion number, e.g 123, required | ||
|
||
discussion_num="$1" | ||
gh api graphql -F discussion_num="$discussion_num" -F repo="{repo}" -F owner="{owner}" -f query=' | ||
query Discussion ($owner: String!, $repo: String!, $discussion_num: Int!){ | ||
repository(name: $repo, owner: $owner) { | ||
discussion(number: $discussion_num) { | ||
number, | ||
id, | ||
body, | ||
category { | ||
id, | ||
name | ||
}, | ||
labels(first: 100) { | ||
edges { | ||
node { | ||
id, | ||
name | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}' |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#! /bin/bash | ||
# fetch labels and their IDs | ||
# requires authenticated gh cli, assumes repo but current git repository | ||
|
||
gh api graphql -F repo="{repo}" -F owner="{owner}" -f query=' | ||
query GetLabelIds($owner: String!, $repo: String!) { | ||
repository(name: $repo, owner: $owner) { | ||
id | ||
labels(first: 100) { | ||
nodes { | ||
id | ||
name | ||
} | ||
} | ||
} | ||
}' |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#! /bin/bash | ||
# add a label to a discussion | ||
# requires authenticated gh cli, assumes repo but current git repository | ||
# args: | ||
# $1: discussion ID (not number!), e.g DIC_kwDOE0GiPM4CXnDc, required | ||
# $2: label ID, e.g. MDU6TGFiZWwzNjIzNjY0MjQ=, required | ||
discussion_id="$1" | ||
label_id="$2" | ||
gh api graphql -F labelableId="$discussion_id" -F labelId="$label_id" -F repo="{repo}" -F owner="{owner}" -f query=' | ||
mutation AddLabels($labelId: ID!, $labelableId:ID!) { | ||
addLabelsToLabelable( | ||
input: {labelIds: [$labelId], labelableId: $labelableId} | ||
) { | ||
clientMutationId | ||
} | ||
}' |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
name: Triage Discussion | ||
on: | ||
discussion: | ||
types: [created] | ||
workflow_dispatch: | ||
inputs: | ||
discussion_num: | ||
required: true | ||
jobs: | ||
label: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: ./.github/actions/trivy-triage | ||
with: | ||
discussion_num: ${{ github.event.inputs.discussion_num }} |