Skip to content

Commit 2d647b4

Browse files
committed
feat: support multi org #27
1 parent f1cad93 commit 2d647b4

File tree

16 files changed

+489
-366
lines changed

16 files changed

+489
-366
lines changed

.github/workflows/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- name: Test Action
3030
uses: ./
3131
with:
32-
github-token: ${{ secrets.GH_TOKEN_GH_NOTIFIER }}
32+
github-tokens: test, ${{ secrets.GH_TOKEN_GH_NOTIFIER }}
3333
channels: C07L8EWB389
3434
slack-token: ${{ secrets.SLACK_TOKEN_GH_NOTIFIER }}
3535
with-test-data: true

action.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ branding:
99
license: ISC
1010

1111
inputs:
12-
github-token:
12+
github-tokens:
1313
description: |
14-
Fine grained Github token with scopes,
14+
Comma-separated list of fine grained Github tokens (one per GitHub organization) with scopes,
1515
- Administration:read (to list all repos in org)
1616
- Pull Requests:read (to get PR details for repos)
1717
- (Organzation) Members:read on all repos (to get GitHub email for Slack user matching)
@@ -27,7 +27,7 @@ inputs:
2727
- chat:write.customize (to allow the bot to customize the name and avatar)
2828
channels:
2929
required: true
30-
description: Comma separated list of Slack channel_ids to post to.
30+
description: Comma-separated list of Slack channel IDs to post to.
3131
with-test-data:
3232
description: Append some test data to the Slack post.
3333
required: false
@@ -52,7 +52,7 @@ inputs:
5252
default: true
5353
repository-filter:
5454
required: false
55-
description: Comma separated list of repositories to scan.
55+
description: Comma-separated list of repositories to scan.
5656
base-url:
5757
description: Point to different github instance such as https://api.github.com
5858
required: false

dist/index.js

Lines changed: 360 additions & 246 deletions
Large diffs are not rendered by default.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@krauters/github-notifier",
33
"description": "GitHub Notifier by Krauters – Post Open Pull Requests to Slack",
4-
"version": "0.14.1",
4+
"version": "0.15.0",
55
"author": "Colten Krauter <coltenkrauter>",
66
"type": "module",
77
"homepage": "https://buymeacoffee.com/coltenkrauter",
@@ -17,15 +17,15 @@
1717
"typescript"
1818
],
1919
"exports": {
20-
".": "./dist/index.js"
20+
".": "./dist/app.js"
2121
},
2222
"engines": {
2323
"node": ">=20"
2424
},
2525
"scripts": {
2626
"all": "npm run test && npm run package",
2727
"bundle:watch": "npm run bundle -- --watch",
28-
"bundle": "npx ncc build src/index.ts -o dist --source-map",
28+
"bundle": "npx ncc build src/app.ts -o dist --source-map",
2929
"fix": "npm run lint -- --fix",
3030
"lint": "npx eslint src/**",
3131
"prepare": "husky || true",

src/app.ts

Lines changed: 89 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,102 +3,111 @@
33
import type { KnownBlock } from '@slack/web-api'
44

55
import { context } from '@actions/github'
6-
import { formatStringList, plural, snapDate, SnapType } from '@krauters/utils'
7-
8-
import type { RunProps } from './structures.js'
6+
import { debug } from '@actions/core'
7+
import { formatStringList, plural } from '@krauters/utils'
98

109
import pkg from '../package.json' with { type: 'json' }
11-
import { workflowLogsUrl, workflowUrl } from './defaults.js'
10+
import { workflowLogsUrl, workflowUrl } from './constants.js'
1211
import { GitHubClient } from './utils/github/github-client.js'
12+
import type { Pull } from './utils/github/structures.js'
1313
import { PullState, RepositoryType } from './utils/github/structures.js'
1414
import { getFirstBlocks, getLastBlocks, getPullBlocks } from './utils/slack/blocks.js'
1515
import { SlackClient } from './utils/slack/slack-client.js'
1616
import { getApprovedPullRequest } from './utils/test-data.js'
17+
import { parseInputs as getInputs } from './input-parser.js'
1718

1819
const { homepage, name, version } = pkg
1920

2021
/**
21-
* Runs the GitHub Notifier to query GitHub for open pull requests and then post messages to Slack channels.
22-
*
23-
* @param props Configurable properties of the GitHub Notifier.
22+
* The main function that gets executed when the action is run.
2423
*/
25-
export async function run({
26-
githubProps,
27-
repositoryFilter,
28-
slackProps,
29-
withArchived,
30-
withDrafts,
31-
withPublic,
32-
withPullReport,
33-
withTestData,
34-
withUserMentions,
35-
}: RunProps): Promise<void> {
36-
const slack = new SlackClient(slackProps)
37-
const gh = new GitHubClient(githubProps)
38-
39-
await slack.enforceAppNamePattern(/.*github[\s-_]?notifier$/i)
40-
41-
const repositories = await gh.getRepositories({
42-
repositoryFilter,
43-
type: RepositoryType.All,
44-
withArchived,
45-
withPublic,
46-
})
47-
const openPulls = await gh.getPulls({ repositories, state: PullState.Open, withDrafts })
48-
let blocks: KnownBlock[] = []
49-
for (const openPull of openPulls) {
50-
console.log(`Building Slack blocks from pull request [${openPull.number}]`)
51-
52-
blocks = [...blocks, ...(await getPullBlocks(openPull, slack, withUserMentions))]
53-
}
24+
async function main(): Promise<void> {
25+
try {
26+
debug('Starting main...')
27+
const {
28+
githubConfig,
29+
repositoryFilter,
30+
slackConfig,
31+
withArchived,
32+
withDrafts,
33+
withPublic,
34+
withTestData,
35+
withUserMentions,
36+
} = getInputs()
37+
38+
const slack = new SlackClient(slackConfig)
39+
const results = await Promise.all(
40+
githubConfig.tokens.map(async (token) => {
41+
const client = new GitHubClient({
42+
options: githubConfig.options,
43+
token,
44+
})
45+
46+
const repositories = await client.getRepositories({
47+
repositoryFilter,
48+
type: RepositoryType.All,
49+
withArchived,
50+
withPublic,
51+
})
52+
53+
const openPulls = await client.getPulls({ repositories, state: PullState.Open, withDrafts })
54+
55+
return { client, openPulls, org: client.getOrg() }
56+
}),
57+
)
58+
59+
console.log(results, 'results')
60+
61+
await slack.enforceAppNamePattern(/.*github[\s-_]?notifier$/i)
62+
63+
const openPulls: Pull[] = []
64+
let blocks: KnownBlock[] = []
65+
for (const openPull of openPulls) {
66+
console.log(`Building Slack blocks from pull request [${openPull.number}]`)
67+
68+
blocks = [...blocks, ...(await getPullBlocks(openPull, slack, withUserMentions))]
69+
}
5470

55-
if (withTestData) {
56-
console.log(`With test data: [${withTestData}]`)
57-
const testDataPullRequest = getApprovedPullRequest()
58-
for (let i = 1; i <= 2; i++) {
59-
blocks = [
60-
...blocks,
61-
...(await getPullBlocks({ ...testDataPullRequest, number: i }, slack, withUserMentions)),
62-
]
71+
if (withTestData) {
72+
console.log(`With test data: [${withTestData}]`)
73+
const testDataPullRequest = getApprovedPullRequest()
74+
for (let i = 1; i <= 2; i++) {
75+
blocks = [
76+
...blocks,
77+
...(await getPullBlocks({ ...testDataPullRequest, number: i }, slack, withUserMentions)),
78+
]
79+
}
6380
}
64-
}
6581

66-
const total = openPulls.length
67-
let header = `You've got ${total} open pull ${plural('request', total)}.`
68-
if (total === 0) {
69-
header = 'There are no open pull requests! :tada:'
70-
}
82+
const total = openPulls.length
83+
let header = `You've got ${total} open pull ${plural('request', total)}.`
84+
if (total === 0) {
85+
header = 'There are no open pull requests! :tada:'
86+
}
7187

72-
let text
73-
if (repositoryFilter.length > 0) {
74-
text = `_<${workflowUrl}|Repository filter>: ${formatStringList(repositoryFilter)}_`
75-
}
88+
let text
89+
if (repositoryFilter.length > 0) {
90+
text = `_<${workflowUrl}|Repository filter>: ${formatStringList(repositoryFilter)}_`
91+
}
7692

77-
blocks = [...getFirstBlocks(gh.cacheOrganization.name, header, text), ...blocks]
78-
79-
blocks = [
80-
...blocks,
81-
...getLastBlocks(
82-
[
83-
`Run from <${workflowUrl}|${context.repo.owner}`,
84-
`/${context.payload.repository?.name}> (<${workflowLogsUrl}|logs>) using <${homepage}|${name}>@<${homepage}/releases/tag/${version}|${version}>`,
85-
].join(''),
86-
),
87-
]
88-
89-
await slack.postMessage(header, blocks)
90-
91-
if (withPullReport) {
92-
const pulls = await gh.getPulls({
93-
oldest: snapDate(new Date(), { months: -12, snap: SnapType.Month }),
94-
onlyGhReviews: true,
95-
repositories,
96-
state: PullState.All,
97-
withCommits: false,
98-
withDrafts: false,
99-
withFilesAndChanges: false,
100-
withUser: false,
101-
})
102-
console.log(gh.getPullReport(pulls).reportString)
93+
blocks = [...getFirstBlocks('gh.cacheOrganization.name', header, text), ...blocks]
94+
95+
blocks = [
96+
...blocks,
97+
...getLastBlocks(
98+
[
99+
`Run from <${workflowUrl}|${context.repo.owner}`,
100+
`/${context.payload.repository?.name}> (<${workflowLogsUrl}|logs>) using <${homepage}|${name}>@<${homepage}/releases/tag/${version}|${version}>`,
101+
].join(''),
102+
),
103+
]
104+
105+
await slack.postMessage(header, blocks)
106+
} catch (err) {
107+
console.error('Fatal error:', err)
108+
process.exit(1)
103109
}
104110
}
111+
112+
// This is required for the GitHub Action to execute main() when it invokes app.ts as specified in action.yaml
113+
await main()
File renamed without changes.

src/index.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import { debug, getBooleanInput, getInput } from '@actions/core'
22
import { stringToArray } from '@krauters/utils'
33

4-
import { run } from './app.js'
4+
import type { InputProps } from './structures.js'
55

66
/**
7-
* The main function that gets executed when the action is run.
7+
* Parses and validates all inputs required for the GitHub Notifier.
8+
*
9+
* @returns The parsed and validated inputs for the GitHub Notifier.
810
*/
9-
export async function main(): Promise<void> {
10-
debug('Starting main...')
11+
export function parseInputs(): InputProps {
1112
debug('Parsing inputs...')
12-
const ghToken = getInput('github-token', { required: true })
13+
const githubTokens = stringToArray(getInput('github-tokens', { required: true }))
1314
const channels = stringToArray(getInput('channels', { required: true }))
1415
const slackToken = getInput('slack-token', { required: true })
1516
const withTestData = getBooleanInput('with-test-data')
@@ -23,15 +24,15 @@ export async function main(): Promise<void> {
2324
// https://github.com/actions/github-script/issues/436
2425
const baseUrl = getInput('base-url') || process.env.GITHUB_API_URL
2526

26-
await run({
27-
githubProps: {
27+
return {
28+
githubConfig: {
2829
options: {
2930
baseUrl,
3031
},
31-
token: ghToken,
32+
tokens: githubTokens,
3233
},
3334
repositoryFilter,
34-
slackProps: {
35+
slackConfig: {
3536
channels,
3637
token: slackToken,
3738
},
@@ -41,5 +42,5 @@ export async function main(): Promise<void> {
4142
withPullReport,
4243
withTestData,
4344
withUserMentions,
44-
})
45+
}
4546
}

0 commit comments

Comments
 (0)