Skip to content

Commit d25c163

Browse files
authored
Initial commit
0 parents  commit d25c163

25 files changed

+3282
-0
lines changed

.editorconfig

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# EditorConfig is awesome: https://editorconfig.org/
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Use unix-style newlines with a newline ending every file
7+
[*]
8+
charset = utf-8
9+
end_of_line = lf
10+
indent_size = 2
11+
indent_style = space
12+
trim_trailing_whitespace = true
13+
insert_final_newline = true
14+
15+
[*.md]
16+
trim_trailing_whitespace = false
17+
18+
[*.{md,json}]
19+
insert_final_newline = false
20+
21+
[.*]
22+
insert_final_newline = false

.eslintrc.cjs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = {
2+
root: true,
3+
env: { browser: true, es2020: true },
4+
extends: [
5+
'eslint:recommended',
6+
'plugin:react/recommended',
7+
'plugin:react/jsx-runtime',
8+
'plugin:react-hooks/recommended',
9+
],
10+
ignorePatterns: ['dist', '.eslintrc.cjs'],
11+
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12+
settings: { react: { version: '18.2' } },
13+
plugins: ['react-refresh'],
14+
rules: {
15+
'react-refresh/only-export-components': [
16+
'warn',
17+
{ allowConstantExport: true },
18+
],
19+
},
20+
}

.eslintrc.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es2021": true
5+
},
6+
"extends": [
7+
"airbnb",
8+
"plugin:react/recommended",
9+
"plugin:prettier/recommended"
10+
],
11+
"parserOptions": {
12+
"ecmaFeatures": {
13+
"jsx": true
14+
},
15+
"ecmaVersion": 8,
16+
"sourceType": "module"
17+
},
18+
"plugins": [
19+
"react"
20+
],
21+
"rules": {
22+
}
23+
}

.github/workflows/CI.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI
2+
3+
on:
4+
[pull_request]
5+
6+
permissions: read-all
7+
8+
jobs:
9+
eslint:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v2
13+
- uses: actions/setup-node@v2
14+
with:
15+
node-version: '16'
16+
- run: yarn install
17+
- name: Run eslint with reviewdog
18+
uses: reviewdog/[email protected]
19+
with:
20+
github_token: ${{ secrets.CTC_DEVOPS_ORG_PAT }}
21+
reporter: github-pr-review
22+
fail_on_error: false
23+
eslint_flags: '**/*.{js,jsx}'
24+
25+
detect-secrets:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v2
29+
- name: Run detect-secrets with reviewdog
30+
uses: reviewdog/action-detect-secrets@master
31+
with:
32+
github_token: ${{ secrets.CTC_DEVOPS_ORG_PAT }}
33+
reporter: github-pr-review
34+
35+
# Prevents false positives from failing job
36+
fail_on_error: false
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: Heroku Build Logs
2+
on: deployment_status
3+
4+
jobs:
5+
heroku-build-logs:
6+
name: Heroku Build Logs
7+
runs-on: ubuntu-latest
8+
steps:
9+
- name: Heroku Build Logs
10+
uses: ctc-uci/github-action-heroku-logs@master
11+
env:
12+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
13+
HEROKU_AUTH_TOKEN: ${{ secrets.CTC_HEROKU_API_KEY }}

.github/workflows/issue-assigned.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Issue Assigned
2+
3+
on:
4+
issues:
5+
types: [assigned]
6+
jobs:
7+
slack_message_assignee:
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v3
12+
- uses: borales/[email protected]
13+
with:
14+
cmd: add @slack/bolt mongoose
15+
- uses: actions/github-script@v6
16+
env:
17+
SLACK_TOKEN: ${{ secrets.CTC_SLACKBOT_OAUTH_TOKEN }}
18+
SIGNING_SECRET: ${{ secrets.CTC_SLACKBOT_SIGNING_SECRET }}
19+
APP_LEVEL_TOKEN: ${{ secrets.CTC_SLACKBOT_APP_LEVEL_TOKEN }}
20+
MONGO_URI: ${{ secrets.CTC_SLACKBOT_MONGO_URI }}
21+
with:
22+
script: |
23+
const messageAssignee = require('./.github/workflows/scripts/issueAssigned');
24+
await messageAssignee({context});
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: PR Reviewer Requested
2+
3+
on:
4+
pull_request:
5+
types: [review_requested]
6+
jobs:
7+
slack_message_reviewer:
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v3
12+
- uses: borales/[email protected]
13+
with:
14+
cmd: add @slack/bolt mongoose
15+
- uses: actions/github-script@v6
16+
env:
17+
SLACK_TOKEN: ${{ secrets.CTC_SLACKBOT_OAUTH_TOKEN }}
18+
SIGNING_SECRET: ${{ secrets.CTC_SLACKBOT_SIGNING_SECRET }}
19+
APP_LEVEL_TOKEN: ${{ secrets.CTC_SLACKBOT_APP_LEVEL_TOKEN }}
20+
MONGO_URI: ${{ secrets.CTC_SLACKBOT_MONGO_URI }}
21+
with:
22+
script: |
23+
const messageReviewer = require('./.github/workflows/scripts/reviewRequested');
24+
await messageReviewer({context});

.github/workflows/review-reviewed.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: PR Reviewed
2+
3+
on:
4+
pull_request_review:
5+
types: [submitted]
6+
jobs:
7+
slack_message_assignee:
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v3
12+
- uses: borales/[email protected]
13+
with:
14+
cmd: add @slack/bolt mongoose
15+
- uses: actions/github-script@v6
16+
env:
17+
SLACK_TOKEN: ${{ secrets.CTC_SLACKBOT_OAUTH_TOKEN }}
18+
SIGNING_SECRET: ${{ secrets.CTC_SLACKBOT_SIGNING_SECRET }}
19+
APP_LEVEL_TOKEN: ${{ secrets.CTC_SLACKBOT_APP_LEVEL_TOKEN }}
20+
MONGO_URI: ${{ secrets.CTC_SLACKBOT_MONGO_URI }}
21+
with:
22+
script: |
23+
const messageAssignee = require('./.github/workflows/scripts/reviewReviewed');
24+
await messageAssignee({context});
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const { App } = require('@slack/bolt');
2+
const mongoose = require('mongoose');
3+
4+
const getUserModel = () => {
5+
const userSchema = new mongoose.Schema({
6+
slackId: { type: String, required: true }, // Slack user ID
7+
slackName: { type: String, required: true }, // Slack user name
8+
role: { type: String, required: true }, // "ADMIN" | "TECHLEAD" | "MEMBER"
9+
repos: { type: Array, required: true }, // List of assigned projects
10+
github: { type: String, required: true },
11+
rep: { type: Number, required: true }, // Reputation count
12+
matchyEnabled: { type: Boolean, required: true }, // Opted into Matchy or not
13+
});
14+
return mongoose.model('User', userSchema);
15+
};
16+
17+
const messageAssignee = async ({ context }) => {
18+
// Initializes your app with your bot token and signing secret
19+
const Bot = new App({
20+
token: process.env.SLACK_TOKEN,
21+
signingSecret: process.env.SIGNING_SECRET,
22+
socketMode: true,
23+
appToken: process.env.APP_LEVEL_TOKEN,
24+
});
25+
26+
const assignee = context.payload.assignee.login;
27+
const url = context.payload.issue.html_url;
28+
29+
// Connect to mongo
30+
mongoose.connect(process.env.MONGO_URI, {
31+
useNewUrlParser: true,
32+
useUnifiedTopology: true,
33+
});
34+
const mongoConnection = mongoose.connection;
35+
// After connection is established, DM assignee on Slack
36+
mongoConnection.once('open', async () => {
37+
try {
38+
const UserModel = getUserModel();
39+
const user = await UserModel.findOne({ github: assignee });
40+
await Bot.client.chat.postMessage({
41+
channel: user.slackId,
42+
text: `You have been assigned to an <${url}|issue> for \`${context.payload.repository.name}\`! :worry-frog-cheer:`,
43+
});
44+
} catch (e) {
45+
console.log(e);
46+
}
47+
mongoConnection.close();
48+
});
49+
};
50+
51+
module.exports = messageAssignee;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const { App } = require('@slack/bolt');
2+
const mongoose = require('mongoose');
3+
4+
const getUserModel = () => {
5+
const userSchema = new mongoose.Schema({
6+
slackId: { type: String, required: true }, // Slack user ID
7+
slackName: { type: String, required: true }, // Slack user name; do we want to keep track of this
8+
role: { type: String, required: true }, // "ADMIN" | "TECHLEAD" | "MEMBER" - defined in config/perms.js
9+
repos: { type: Array, required: true }, // List of assigned projects
10+
github: { type: String, required: true },
11+
rep: { type: Number, required: true }, // Reputation count
12+
matchyEnabled: { type: Boolean, required: true }, // Opted into Matchy or not
13+
});
14+
return mongoose.model('User', userSchema);
15+
};
16+
17+
const messageReviewer = async ({ context }) => {
18+
// Initializes your app with your bot token and signing secret
19+
const Bot = new App({
20+
token: process.env.SLACK_TOKEN,
21+
signingSecret: process.env.SIGNING_SECRET,
22+
socketMode: true,
23+
appToken: process.env.APP_LEVEL_TOKEN,
24+
});
25+
26+
const sender = context.payload.sender.login;
27+
const reviewer = context.payload.requested_reviewer.login;
28+
const url = context.payload.pull_request.html_url;
29+
30+
// Connect to mongo
31+
mongoose.connect(process.env.MONGO_URI, {
32+
useNewUrlParser: true,
33+
useUnifiedTopology: true,
34+
});
35+
const mongoConnection = mongoose.connection;
36+
// After connection is established, DM reviewer on Slack
37+
mongoConnection.once('open', async () => {
38+
try {
39+
const UserModel = getUserModel();
40+
const user = await UserModel.findOne({ github: reviewer });
41+
await Bot.client.chat.postMessage({
42+
channel: user.slackId,
43+
text: `A review for a <${url}|PR> has been requested by ${sender}! :catjam:`,
44+
});
45+
} catch (e) {
46+
console.log(e);
47+
}
48+
mongoConnection.close();
49+
});
50+
};
51+
52+
module.exports = messageReviewer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const { App } = require('@slack/bolt');
2+
const mongoose = require('mongoose');
3+
4+
// commented, approved, changes_requested
5+
6+
const getUserModel = () => {
7+
const userSchema = new mongoose.Schema({
8+
slackId: { type: String, required: true }, // Slack user ID
9+
slackName: { type: String, required: true }, // Slack user name; do we want to keep track of this
10+
role: { type: String, required: true }, // "ADMIN" | "TECHLEAD" | "MEMBER" - defined in config/perms.js
11+
repos: { type: Array, required: true }, // List of assigned projects
12+
github: { type: String, required: true },
13+
rep: { type: Number, required: true }, // Reputation count
14+
matchyEnabled: { type: Boolean, required: true }, // Opted into Matchy or not
15+
});
16+
return mongoose.model('User', userSchema);
17+
};
18+
19+
const messageAssignee = async ({ context }) => {
20+
// Initializes your app with your bot token and signing secret
21+
const Bot = new App({
22+
token: process.env.SLACK_TOKEN,
23+
signingSecret: process.env.SIGNING_SECRET,
24+
socketMode: true,
25+
appToken: process.env.APP_LEVEL_TOKEN,
26+
});
27+
28+
const reviewer = context.payload.sender.login;
29+
const githubAssignees = context.payload.pull_request.assignees;
30+
const url = context.payload.review.html_url;
31+
32+
// Connect to mongo
33+
mongoose.connect(process.env.MONGO_URI, {
34+
useNewUrlParser: true,
35+
useUnifiedTopology: true,
36+
});
37+
const mongoConnection = mongoose.connection;
38+
// After connection is established, do the things
39+
mongoConnection.once('open', async () => {
40+
try {
41+
const UserModel = getUserModel();
42+
const slackAssignees = await Promise.allSettled(
43+
githubAssignees.map(assignee => UserModel.findOne({ github: assignee.login })),
44+
);
45+
if (context.payload.review.state === 'approved') {
46+
await Promise.all(
47+
slackAssignees.map(assignee =>
48+
Bot.client.chat.postMessage({
49+
channel: assignee.value?.slackId,
50+
text: `One of your pull requests has been APPROVED by ${reviewer}! <${url}|View Review> :shrek::thumbsup:`,
51+
}),
52+
),
53+
);
54+
} else {
55+
await Promise.all(
56+
slackAssignees.map(assignee =>
57+
Bot.client.chat.postMessage({
58+
channel: assignee.value?.slackId,
59+
text: `One of your pull requests has been REVIEWED by ${reviewer}! <${url}|View Review> :shrek:`,
60+
}),
61+
),
62+
);
63+
}
64+
} catch (e) {
65+
console.log(e);
66+
}
67+
mongoConnection.close();
68+
});
69+
};
70+
71+
module.exports = messageAssignee;

0 commit comments

Comments
 (0)