Skip to content

Instantly share code, notes, and snippets.

@TranBaVinhSon
Last active September 3, 2024 06:23
Show Gist options
  • Save TranBaVinhSon/deb7ce52ec79e9666c67a8b12ed1985c to your computer and use it in GitHub Desktop.
Save TranBaVinhSon/deb7ce52ec79e9666c67a8b12ed1985c to your computer and use it in GitHub Desktop.
Getting all releases from the past 7 days from each repository within an organization, categorizing them, and creating a release note for business stakeholders.
import { Octokit } from "@octokit/rest";
import OpenAI from "openai";
import "dotenv/config";
const octokit = new Octokit({ auth: process.env.GH_TOKEN });
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
interface Release {
name: string;
body: string;
repositoryName: string;
}
export async function getAllReleasesLastWeek(
org: string,
days = 7
): Promise<Release[]> {
const repos = await octokit.paginate(octokit.repos.listForOrg, { org });
const releases: Release[] = [];
const now = new Date();
const cutoffDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
for (const repo of repos) {
console.log("repo", repo.name);
let page = 1;
let shouldContinue = true;
while (shouldContinue) {
const repoReleases = await octokit.rest.repos.listReleases({
owner: org,
repo: repo.name,
per_page: 100,
page: page,
});
if (repoReleases.data.length === 0) break;
for (const repoRelease of repoReleases.data) {
const releaseDate = new Date(repoRelease.created_at);
if (releaseDate >= cutoffDate) {
releases.push({
name: repoRelease.name as string,
body: repoRelease.body as string,
repositoryName: repo.name,
});
} else {
shouldContinue = false;
break;
}
console.log(`release ${repoRelease.name} at ${repoRelease.created_at}`);
// If we reach to the final page
if (repoReleases.data.length < 100) {
shouldContinue = false;
}
}
page += 1;
}
}
return releases;
}
export async function classifyReleaseNotes(releaseNotes: string) {
const response = await openai.chat.completions.create({
model: "gpt-4o-2024-08-06",
messages: [
{
role: "user",
content: `Classify the following release notes into the following categories and re-write them for non-technical readers such as business members in a concise manner:
## Product Improvements
New features and improvements which has impact directly to customers
### New Features
### Enhancements
### Bug Fixes
## Engineering Improvements
New release and improvements that can’t be seen from customers such as CI/CD improvement, cutting infra cost…etc
### Infrastructure Updates
### Performance Improvements
### Known Issues
\n\n
Release note: \n ${releaseNotes}`,
},
],
});
console.log(JSON.stringify(response, null, 2));
return response.choices[0].message.content;
}
async function generateReleaseNote() {
const org = "moneyforward-i";
const releases = await getAllReleasesLastWeek(org);
let releaseNotes = "";
for (const release of releases) {
releaseNotes += `## ${release.name}\n${release.body}\n\n`;
}
const classifiedNotes = await classifyReleaseNotes(releaseNotes);
const releaseNote = ` \n\n ${classifiedNotes}`;
console.log("releaseNote", releaseNote);
}
generateReleaseNote();
@TranBaVinhSon
Copy link
Author

To generate release note weekly, here is the GH action:
.github/workflows/release-note.yml

name: Generate Release Note

on:
  schedule:
    - cron: "0 1 * * 4" # Run at 10:00 AM JST (1:00 AM UTC) on Thursday every week

jobs:
  generate-release-note:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: "20"

      - name: Install dependencies
        run: yarn install

      - name: Run release note generator
        run: npx tsx src/index.ts
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment