-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
1,053 additions
and
1 deletion.
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,17 @@ | ||
name: Add Label To Issue Action Test | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
- name: Run the custom action | ||
uses: ./ | ||
with: | ||
mode: ISSUE_LABEL |
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,24 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? | ||
|
||
*.env |
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 |
---|---|---|
@@ -1,2 +1,45 @@ | ||
# GitMate | ||
Conversational AI for GitHub Workflows This project integrates a conversational AI assistant to streamline software development workflows for the Knowledge-based and Software Systems Group (KBSS). The AI automates tasks like issue tracking, pull request management, and code reviews within the GitHub framework. | ||
|
||
GitMate is a conversational AI assistant designed to optimize and streamline GitHub workflows. This project integrates a sophisticated AI bot that automates common tasks within the GitHub ecosystem, enhancing the productivity of the Knowledge-based and Software Systems Group (KBSS). With GitMate, developers can save time and reduce manual effort by automating key GitHub tasks such as issue tracking, pull request management, and code reviews. | ||
|
||
## Core Features | ||
|
||
--- | ||
|
||
### 1. **Issue Management** | ||
|
||
- **Automated Task Management**: GitMate automates the labeling of new issues based on content analysis. It detects keywords such as "bug" or "enhancement" in the issue description and assigns the corresponding labels automatically, ensuring that issues are categorized properly from the outset. | ||
|
||
Example: If a user reports a problem with the system's functionality, GitMate could automatically label the issue as "bug". | ||
|
||
### 2. **Pull Request Management** | ||
|
||
- **Automated Review Assistance**: GitMate can assist in automating certain aspects of pull request (PR) management, including providing reviews, identifying merge conflicts, and suggesting changes to improve code quality. | ||
|
||
### 3. **Code Review Automation** | ||
|
||
- **AI-Driven Code Review**: The bot can provide initial feedback on pull requests by analyzing code changes, highlighting potential issues, and suggesting improvements, helping to expedite the review process. | ||
|
||
--- | ||
|
||
## Usage | ||
|
||
### Adding Labels to Issues Automatically | ||
|
||
To enable GitMate to automatically label issues when they are opened or edited, use the following GitHub Actions workflow configuration. | ||
|
||
```yaml | ||
on: | ||
issues: | ||
types: [opened, edited] | ||
|
||
jobs: | ||
add-label-to-issue: | ||
runs-on: ubuntu-latest | ||
name: Add label to created issue | ||
steps: | ||
- uses: palagdan/GitMate@main | ||
with: | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
openai-api-key: ${{ secrets.OPENAI_API_KEY }} | ||
mode: 'issue label' |
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,12 @@ | ||
name: 'GitMate' | ||
description: 'GitMate is your buddy in a development world' | ||
|
||
inputs: | ||
mode: | ||
description: 'Mode for the bot' | ||
required: true | ||
default: 'mirror' | ||
|
||
runs: | ||
using: 'node20' | ||
main: 'dist/index.ts' |
101 changes: 101 additions & 0 deletions
101
dist/actions/addLabelsToIssueAction/addLabelsToIssueAction.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,101 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || (function () { | ||
var ownKeys = function(o) { | ||
ownKeys = Object.getOwnPropertyNames || function (o) { | ||
var ar = []; | ||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
return ar; | ||
}; | ||
return ownKeys(o); | ||
}; | ||
return function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
})(); | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const core = __importStar(require("@actions/core")); | ||
const github = __importStar(require("@actions/github")); | ||
const openai_1 = require("openai"); | ||
const utils_1 = require("@/utils"); | ||
const addLabelToIssueAction = () => __awaiter(void 0, void 0, void 0, function* () { | ||
var _a, _b, _c; | ||
try { | ||
const apiKey = core.getInput("openai-api-key"); | ||
const githubToken = core.getInput("github-token"); | ||
const octokit = github.getOctokit(githubToken); | ||
const issue = yield octokit.rest.issues.get(Object.assign(Object.assign({}, github.context.issue), { issue_number: github.context.issue.number })); | ||
const availableLabels = yield octokit.rest.issues.listLabelsForRepo(Object.assign({}, github.context.repo)); | ||
const prompt = createAddLabelsToIssuePrompt(issue.data.title, issue.data.body || '', availableLabels.data.map(label => label.name), './promptTemplate.txt'); | ||
core.debug(`Prompt: ${prompt}`); | ||
const client = new openai_1.OpenAI({ apiKey }); | ||
const params = { | ||
messages: [{ role: 'user', content: prompt }], | ||
model: 'gpt-4', | ||
}; | ||
const chatCompletion = yield client.chat.completions.create(params); | ||
core.debug(`Response: ${JSON.stringify(chatCompletion)}`); | ||
const responseText = ((_c = (_b = (_a = chatCompletion.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content) === null || _c === void 0 ? void 0 : _c.trim()) || ''; | ||
const parsedResponse = JSON.parse(responseText); | ||
const labels = parsedResponse.labels; | ||
if (labels.length > 0) { | ||
yield octokit.rest.issues.setLabels({ | ||
owner: github.context.repo.owner, | ||
repo: github.context.repo.repo, | ||
issue_number: github.context.issue.number, | ||
labels, | ||
}); | ||
core.info(`Labels added: ${labels.join(", ")}`); | ||
} | ||
else { | ||
core.info('No labels suggested to add.'); | ||
} | ||
} | ||
catch (error) { | ||
core.setFailed(`Error occurred: ${error.message}`); | ||
} | ||
}); | ||
/** | ||
* Create a prompt for OpenAI based on the issue details and available labels. | ||
* @param issueTitle - Title of the GitHub issue. | ||
* @param issueBody - Body of the GitHub issue. | ||
* @param availableLabels - List of available labels in the repository. | ||
* @param templateFilePath - Path to the prompt template file. | ||
* @returns The generated prompt string. | ||
*/ | ||
const createAddLabelsToIssuePrompt = (issueTitle, issueBody, availableLabels, templateFilePath) => { | ||
const template = (0, utils_1.loadFile)(templateFilePath); | ||
return template | ||
.replace('{{issueTitle}}', issueTitle) | ||
.replace('{{issueBody}}', issueBody) | ||
.replace('{{availableLabels}}', availableLabels.join(", ")); | ||
}; | ||
exports.default = addLabelToIssueAction; |
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,7 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const addLabelsToIssueAction_1 = __importDefault(require("./addLabelsToIssueAction")); | ||
exports.default = addLabelsToIssueAction_1.default; |
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,8 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.addLabelsToIssueAction = void 0; | ||
const addLabelsToIssueAction_1 = __importDefault(require("./addLabelsToIssueAction")); | ||
exports.addLabelsToIssueAction = addLabelsToIssueAction_1.default; |
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,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ISSUE_LABEL = void 0; | ||
const modes_js_1 = require("./modes.js"); | ||
Object.defineProperty(exports, "ISSUE_LABEL", { enumerable: true, get: function () { return modes_js_1.ISSUE_LABEL; } }); |
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,4 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ISSUE_LABEL = void 0; | ||
exports.ISSUE_LABEL = "issue label"; |
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,71 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || (function () { | ||
var ownKeys = function(o) { | ||
ownKeys = Object.getOwnPropertyNames || function (o) { | ||
var ar = []; | ||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
return ar; | ||
}; | ||
return ownKeys(o); | ||
}; | ||
return function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
})(); | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const core = __importStar(require("@actions/core")); | ||
const constants_1 = require("./constants"); | ||
const actions_1 = require("./actions"); | ||
function run() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
try { | ||
const mode = core.getInput('mode'); | ||
switch (mode) { | ||
case constants_1.ISSUE_LABEL: | ||
yield (0, actions_1.addLabelsToIssueAction)(); | ||
break; | ||
default: | ||
core.setFailed('Mode is not supported'); | ||
break; | ||
} | ||
} | ||
catch (error) { | ||
if (error instanceof Error) { | ||
core.setFailed(error.message); | ||
} | ||
else { | ||
core.setFailed('An unknown error occurred'); | ||
} | ||
} | ||
}); | ||
} | ||
run(); |
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,46 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || (function () { | ||
var ownKeys = function(o) { | ||
ownKeys = Object.getOwnPropertyNames || function (o) { | ||
var ar = []; | ||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
return ar; | ||
}; | ||
return ownKeys(o); | ||
}; | ||
return function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.loadFile = void 0; | ||
const fs = __importStar(require("node:fs")); | ||
/** | ||
* Load a file. | ||
* @param filePath - Path to the file to be loaded. | ||
* @returns File content as a string. | ||
*/ | ||
const loadFile = (filePath) => { | ||
return fs.readFileSync(filePath, 'utf-8'); | ||
}; | ||
exports.loadFile = loadFile; |
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,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.loadFile = void 0; | ||
const fsUtils_1 = require("./fsUtils"); | ||
Object.defineProperty(exports, "loadFile", { enumerable: true, get: function () { return fsUtils_1.loadFile; } }); |
Oops, something went wrong.