Skip to content

Commit

Permalink
Merge pull request #19 from checkly/ghr
Browse files Browse the repository at this point in the history
Ghr
  • Loading branch information
aluedeke authored Nov 22, 2024
2 parents acc39c4 + e9ec5e7 commit ec5690c
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 8 deletions.
6 changes: 3 additions & 3 deletions src/github/agent.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const CHECKLY_GITHUB_TOKEN = process.env.CHECKLY_GITHUB_TOKEN!;

jest.setTimeout(120000); // Set timeout to 120 seconds

describe.skip('GithubAgent Tests', () => {
describe('GithubAgent Tests', () => {
let openai: OpenAIProvider;
let github: GitHubAPI;

Expand All @@ -19,13 +19,13 @@ describe.skip('GithubAgent Tests', () => {
github = new GitHubAPI(CHECKLY_GITHUB_TOKEN);
});

it('should summarize a single release', async () => {
it.skip('should summarize a single release', async () => {
let agent = new GithubAgent(openai('gpt-4o'), github);
let response = await agent.summarizeRelease('checkly', 'checkly-webapp', '2024-11-15-12.56.18', '2024-11-15-11.29.32');
console.log(response);
});

it('should summarize releases by prompt', async () => {
it.skip('should summarize releases by prompt', async () => {
let agent = new GithubAgent(openai('gpt-4o'), github);
let response = await agent.summarizeReleases('what changed in the ui since yesterday', 'checkly');
console.log(response);
Expand Down
5 changes: 3 additions & 2 deletions src/github/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class GithubAgent {
let diff = await this.github.getDiffBetweenTags(
org,
repo,
previousRelease,
release,
previousRelease
);

const { text } = await generateText({
Expand Down Expand Up @@ -97,8 +97,9 @@ export class GithubAgent {
id: release.tag,
release_date: release.date,
link: release.link,
diffLink: diff.html_url,
summary: summary,
authors: Array.from(new Set(diff.commits.map((c) => c.author))),
authors: Array.from(new Set(diff.commits.map(commit => commit.author))),
};
})
);
Expand Down
6 changes: 3 additions & 3 deletions src/github/github.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import GitHubAPI from './github';

const CHECKLY_GITHUB_TOKEN = process.env.CHECKLY_GITHUB_TOKEN!;

describe.skip('GitHub API Tests', () => {
describe('GitHub API Tests', () => {

it('should return the latest releases for checkly', async () => {
it.skip('should return the latest releases for checkly', async () => {
const githubAPI = new GitHubAPI(CHECKLY_GITHUB_TOKEN);
const org = 'checkly';
const repo = 'checkly-backend';
Expand All @@ -18,7 +18,7 @@ describe.skip('GitHub API Tests', () => {
expect(diff).toBeDefined();
});

it('should return the latest releases with diffs for checkly', async () => {
it.skip('should return the latest releases with diffs for checkly', async () => {
const githubAPI = new GitHubAPI(CHECKLY_GITHUB_TOKEN);
const org = 'checkly';
const repo = 'checkly-backend';
Expand Down
21 changes: 21 additions & 0 deletions src/github/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ class GitHubAPI {
}
}

async getPreviousReleaseTag(org: string, repoName: string, release: string) {
try {
const { data: releases } = await this.octokit.rest.repos.listReleases({
owner: org,
repo: repoName,
});

const releaseIndex = releases.findIndex(r => r.tag_name === release);
if (releaseIndex === -1) {
throw new Error(`Release ${release} not found`);
} else if (releaseIndex === releases.length - 1) {
return "";
} else {
return releases[releaseIndex + 1].tag_name;
}
} catch (error) {
console.error('Error querying GitHub releases:', error);
throw error;
}
}

async queryLatestReleases(org: string, repoName: string, since: Date) {
try {
let { data: releases } = await this.octokit.rest.repos.listReleases({
Expand Down
62 changes: 62 additions & 0 deletions src/github/slackBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
export const releaseHeader = {
type: "section",
text: {
type: "mrkdwn",
text: "*Release Overview*",
},
};

export const divider = { type: "divider" };

export const createReleaseBlock = function ({
release,
releaseUrl,
diffUrl,
date,
repo,
repoUrl,
authors,
summary,
}: {
release: string;
releaseUrl: string;
diffUrl: string;
date: string;
repo: string;
repoUrl: string;
authors: string[];
summary: string;
}) {
return {
blocks: [
{
type: "section",
fields: [
{
type: "mrkdwn",
text: `:rocket: *Release*\n<${releaseUrl}|${release}> - <${diffUrl}|Diff>`,
},
{
type: "mrkdwn",
text: `:calendar: *When*\n${date}`,
},
{
type: "mrkdwn",
text: `:package: *Repo*\n<${repoUrl}|${repo}>`,
},
{
type: "mrkdwn",
text: `:star: *Authors*\n${authors.join(", ")}`,
},
],
},
{
type: "section",
text: {
type: "mrkdwn",
text: `*Summary*\n${summary}`,
},
},
],
};
};
107 changes: 107 additions & 0 deletions src/routes/githubReleaseWebhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import crypto from "crypto";
import express, { Request, Response, NextFunction } from "express";
import {
ReleaseEvent,
WebhookEvent,
WebhookEventName,
} from "@octokit/webhooks-types";
import { App, LogLevel } from "@slack/bolt";
import { getOpenaiSDKClient } from "src/ai/openai";
import GitHubAPI from "src/github/github";
import { GithubAgent } from "src/github/agent";
import { createReleaseBlock, releaseHeader } from "../github/slackBlock";
import moment from "moment";

const GITHUB_SECRET = process.env.GITHUB_SECRET || "your_secret";

export const app = new App({
signingSecret: process.env.SLACK_SIGNING_SECRET,
token: process.env.SLACK_AUTH_TOKEN,
appToken: process.env.SLACK_APP_TOKEN,
socketMode: true,
logLevel:
process.env.NODE_ENV !== "production" ? LogLevel.DEBUG : LogLevel.INFO,
});

const CHECKLY_GITHUB_TOKEN = process.env.CHECKLY_GITHUB_TOKEN!;

const github = new GitHubAPI(CHECKLY_GITHUB_TOKEN);

let setupAgent = () => {
let openai = getOpenaiSDKClient();

return new GithubAgent(openai("gpt-4o"), github);
};

const githubAgent = setupAgent();

const router = express.Router();

async function verifySignature(req: Request, res: Response, buf: Buffer) {
const signature = req.headers["x-hub-signature-256"] as string;
const hmac = crypto.createHmac("sha256", GITHUB_SECRET);
const digest = `sha256=${hmac.update(buf).digest("hex")}`;

if (signature !== digest) {
throw new Error("Invalid signature");
}
}

router.post(
"/webhook",
express.json({ verify: verifySignature }),
async (req: Request, res: Response) => {
const event = req.headers["x-github-event"] as WebhookEventName;
const body = req.body as WebhookEvent;

switch (event) {
case "release":
let releaseEvent = body as ReleaseEvent;
if (releaseEvent.action !== "published") {
res.status(200).send("Webhook received");
return;
}

const previousRelease = await github.getPreviousReleaseTag('checkly', releaseEvent.repository.name, releaseEvent.release.tag_name);

const release = await githubAgent.summarizeRelease(
releaseEvent.repository.organization!,
releaseEvent.repository.name,
releaseEvent.release.tag_name,
previousRelease
);
const date = moment(releaseEvent.release.published_at).fromNow();
const authors = release.diff.commits
.map((c) => c.author)
.filter((author) => author !== null)
.map((author) => author.login);
let releaseBlocks = createReleaseBlock({
release: releaseEvent.release.name,
releaseUrl: releaseEvent.release.html_url,
diffUrl: release.diff.html_url,
date,
repo: releaseEvent.repository.name,
repoUrl: releaseEvent.repository.html_url,
authors,
summary: release.summary,
}).blocks;

await app.client.chat.postMessage({
channel: "C07V9GNU9L6",
metadata: {
event_type: "release-summary",
event_payload: {

},
},
blocks: releaseBlocks,
});

res.status(200).send("Webhook received");
break;
default:
console.log("Unhandled event received:", event);
res.status(404).send("Webhook received");
}
}
);
35 changes: 35 additions & 0 deletions src/slackbot/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { getRunMessages } from "../ai/utils";
import { SreAssistant } from "../sre-assistant/SreAssistant";
import GitHubAPI from "../github/github";
import { GithubAgent } from "../github/agent";
import moment from "moment";
import { createReleaseBlock, divider as releaseDivider, releaseHeader } from "../github/slackBlock";

export const app = new App({
signingSecret: process.env.SLACK_SIGNING_SECRET,
Expand Down Expand Up @@ -50,6 +52,39 @@ let setupAgent = () => {

const githubAgent = setupAgent();

app.command("/srebot-releases", async ({ command, ack, respond }) => {
await ack();
let summaries = await githubAgent.summarizeReleases(command.text, 'checkly');
if (summaries.releases.length === 0) {
await respond({ text: `No releases found in repo ${summaries.repo} since ${summaries.since}`});
}

let releases = summaries.releases.sort((a, b) => new Date(b.release_date).getTime() - new Date(a.release_date).getTime()); let response = [releaseHeader].concat(releases.map(summary => {
const date = moment(summary.release_date).fromNow();
const authors = summary.authors.filter(author => author !== null).map(author => author.login)
return createReleaseBlock({
release: summary.id,
releaseUrl: summary.link,
diffUrl: summary.diffLink,
date,
repo: summaries.repo.name,
repoUrl: summaries.repo.link,
authors,
summary: summary.summary
}).blocks as any;
}).reduce((prev, curr) => {
if (!prev) {
return curr;
}

return prev.concat([releaseDivider]).concat(curr);
}));

await respond({
blocks: response
});
})

app.event("app_mention", async ({ event, context }) => {
try {
let threadId;
Expand Down

0 comments on commit ec5690c

Please sign in to comment.