generated from ztimson/template
This commit is contained in:
44
.github/workflows/build.yml
vendored
Normal file
44
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Build and publish
|
||||
run-name: Build and publish
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
container:
|
||||
name: Build Container
|
||||
runs-on: ubuntu-latest
|
||||
container: docker
|
||||
steps:
|
||||
- name: Build Container
|
||||
run: |
|
||||
git clone -b "${{github.ref_name}}" "$(echo ${{github.server_url}}/${{github.repository}}.git | sed s%://%://${{github.token}}@% )" .
|
||||
DOCKER_HUB=$([ -n "${{secrets.DOCKER_HUB_USER}}" ] && [ -n "${{secrets.DOCKER_HUB_TOKEN}}" ] && [ -n "${{secrets.DOCKER_HUB_IMAGE}}" ] && echo "true" || echo "false")
|
||||
REGISTRY="$(echo "${{github.server_url}}" | sed -E 's|https?://||')"
|
||||
VERSION="$(cat VERSION)"
|
||||
|
||||
docker login -u "${{github.repository_owner}}" -p "${{secrets.DEPLOY_TOKEN}}" "$REGISTRY"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then docker login -u "${{secrets.DOCKER_HUB_USER}}" -p "${{secrets.DOCKER_HUB_TOKEN}}" docker.io; fi
|
||||
|
||||
docker build -t "$REGISTRY/${{github.repository}}:${{github.ref_name}}" .
|
||||
docker push "$REGISTRY/${{github.repository}}:${{github.ref_name}}"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:${{github.ref_name}}"
|
||||
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:${{github.ref_name}}"
|
||||
fi
|
||||
|
||||
if [ "${{github.ref_name}}" = "master" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "$REGISTRY/${{github.repository}}:$VERSION"
|
||||
docker push "$REGISTRY/${{github.repository}}:$VERSION"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:$VERSION"
|
||||
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:$VERSION"
|
||||
fi
|
||||
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "$REGISTRY/${{github.repository}}:latest"
|
||||
docker push "$REGISTRY/${{github.repository}}:latest"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:latest"
|
||||
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:latest"
|
||||
fi
|
||||
fi
|
||||
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM node:22
|
||||
|
||||
COPY . /ai
|
||||
RUN cd /ai && npm ci
|
||||
|
||||
WORKDIR /app
|
||||
ENTRYPOINT ["npx", "--yes", "/ai", "review", "/app"]
|
||||
104
README.md
104
README.md
@@ -1,96 +1,48 @@
|
||||
<!-- Header -->
|
||||
<div id="top" align="center">
|
||||
<br />
|
||||
|
||||
|
||||
<!-- Logo -->
|
||||
<img src="https://git.zakscode.com/repo-avatars/2b4ee6ba1f2e2618bf7694e4a52fb56d1d0ea6abafa2dcbe496ab786b86d5a76" alt="Logo" width="200" height="200">
|
||||
|
||||
<!-- Title -->
|
||||
### Template
|
||||
|
||||
### AI Reviewer
|
||||
|
||||
<!-- Description -->
|
||||
Simple repository template
|
||||
Automated AI-powered code review for pull requests 🤖
|
||||
|
||||
<!-- Repo badges -->
|
||||
[](https://git.zakscode.com/ztimson/template/tags)
|
||||
[](https://git.zakscode.com/ztimson/template/pulls)
|
||||
[](https://git.zakscode.com/ztimson/template/issues)
|
||||
[](https://git.zakscode.com/ztimson/ai-reviewer/tags)
|
||||
[](https://git.zakscode.com/ztimson/ai-reviewer/pulls)
|
||||
[](https://git.zakscode.com/ztimson/ai-reviewer/issues)
|
||||
|
||||
<!-- Links -->
|
||||
|
||||
---
|
||||
<!-- Links -->
|
||||
---
|
||||
<div>
|
||||
<a href="https://git.zakscode.com/ztimson/template/wiki" target="_blank">Documentation</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/template/releases" target="_blank">Release Notes</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
|
||||
<a href="https://git.zakscode.com/ztimson/ai-reviewer/releases" target="_blank">Release Notes</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/ai-reviewer/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/ai-reviewer/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
|
||||
</div>
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
- [Template](#top)
|
||||
- [About](#about)
|
||||
- [Demo](#demo)
|
||||
- [Built With](#built-with)
|
||||
- [Setup](#setup)
|
||||
- [Production](#production)
|
||||
- [Development](#development)
|
||||
- [License](#license)
|
||||
- [AI Reviewer](#top)
|
||||
- [About](#about)
|
||||
- [Built With](#built-with)
|
||||
- [Setup](#setup)
|
||||
- [Production](#production)
|
||||
- [License](#license)
|
||||
|
||||
## About
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
|
||||
### Demo
|
||||
|
||||
Website: https://git.zakscode.com
|
||||
Automated code reviewer that uses AI to analyze git diffs and provide inline comments on pull requests. Supports Anthropic, OpenAI, and Ollama models with tool-based reviewing for precise feedback.
|
||||
|
||||
### Built With
|
||||
[](https://angular.io/)
|
||||
[](https://www.android.com/)
|
||||
[](https://www.arduino.cc/)
|
||||
[](https://getbootstrap.com)
|
||||
[](https://en.cppreference.com/w/c/language)
|
||||
[](https://cplusplus.com/)
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](https://www.w3.org/Style/CSS/Overview.en.html)
|
||||
[](https://www.djangoproject.com/)
|
||||
[](https://docker.com/)
|
||||
[](https://www.electronjs.org/)
|
||||
[](https://firebase.google.com/)
|
||||
[](https://go.dev/)
|
||||
[](https://graphql.org/)
|
||||
[](https://developer.mozilla.org/en-US/docs/Glossary/HTML)
|
||||
[](https://java.com/)
|
||||
[](https://javascript.com/)
|
||||
[](https://jquery.com )
|
||||
[](https://laravel.com)
|
||||
[](https://www.linux.org/)
|
||||
[](https://git.zakscode.com/ztimson/momentum)
|
||||
[](https://www.mongodb.com/)
|
||||
[](https://www.mysql.com/)
|
||||
[](https://nestjs.com/)
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](https://nextjs.org/)
|
||||
[](https://www.nginx.com/)
|
||||
[](https://nodejs.org/)
|
||||
[](https://p5js.org/)
|
||||
[](https://www.php.net/)
|
||||
[](https://www.postgresql.org/)
|
||||
[](https://www.python.org/)
|
||||
[](https://reactjs.org/)
|
||||
[](https://redis.com/)
|
||||
[](https://sass-lang.com/)
|
||||
[](https://en.wikipedia.org/wiki/Shell_script)
|
||||
[](https://www.microsoft.com/en-ca/sql-server)
|
||||
[](https://www.sqlite.org/index.html)
|
||||
[](https://svelte.dev/)
|
||||
[](https://typescriptlang.org/)
|
||||
[](https://microsoft.com/windows)
|
||||
[](https://vitejs.dev/)
|
||||
[](https://vuejs.org/)
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -101,12 +53,12 @@ Website: https://git.zakscode.com
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
#### Prerequisites
|
||||
- [Docker](https://docs.docker.com/install/)
|
||||
|
||||
#### Instructions
|
||||
1. Run the docker image: `docker run -p 80:80 git.zakscode.com/ztimson/template:latest`
|
||||
2. Open [http://localhost](http://localhost)
|
||||
1. Add the following to your gitea build:
|
||||
```yaml
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -120,13 +72,13 @@ Website: https://git.zakscode.com
|
||||
- [Node.js](https://nodejs.org/en/download)
|
||||
|
||||
#### Instructions
|
||||
1. Install the dependencies: `npm install`
|
||||
2. Start the Angular server: `npm run start`
|
||||
3. Open [http://localhost:4200](http://localhost:4200)
|
||||
1. Install the dependencies: `npm i`
|
||||
2. Build library: `npm run review`
|
||||
|
||||
</details>
|
||||
|
||||
## License
|
||||
Copyright © 2023 Zakary Timson | All Rights Reserved | Available under MIT Licensing
|
||||
|
||||
Copyright © 2025 Zakary Timson | All Rights Reserved | Available under MIT Licensing
|
||||
|
||||
See the [license](./LICENSE) for more information.
|
||||
|
||||
1743
package-lock.json
generated
Normal file
1743
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
119
src/review.mjs
119
src/review.mjs
@@ -1,58 +1,79 @@
|
||||
/**
|
||||
* Variables:
|
||||
* HOST - AI provider (ollama/anthropic/openai)
|
||||
* MODEL - AI model name
|
||||
* TOKEN - Access token (Omit with ollama)
|
||||
* ROOT - Path to project
|
||||
*/
|
||||
import {Ai} from '@ztimson/ai-utils';
|
||||
import {$} from '@ztimson/node-utils';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs';
|
||||
|
||||
const gitDiff = await $`git diff HEAD^..HEAD`;
|
||||
const root = process.env['ROOT'] || process.cwd(),
|
||||
host = process.env['HOST'],
|
||||
model = process.env['MODEL'],
|
||||
token = process.env['TOKEN'];
|
||||
(async () => {
|
||||
const
|
||||
git = process.env['GIT_HOST'],
|
||||
owner = process.env['GIT_OWNER'],
|
||||
repo = process.env['GIT_REPO'],
|
||||
auth = process.env['GIT_TOKEN'],
|
||||
pr = process.env['PULL_REQUEST'],
|
||||
root = process.argv[2] || process.cwd(),
|
||||
host = process.env['AI_HOST'],
|
||||
model = process.env['AI_MODEL'],
|
||||
token = process.env['AI_TOKEN'];
|
||||
|
||||
let options = {ollama: {model, host}};
|
||||
if(host === 'anthropic') options = {anthropic: {model, token}};
|
||||
else if(host === 'openai') options = {openAi: {model, token}};
|
||||
const comments = [];
|
||||
const commit = (await $`git log -1 --pretty=format:%H`).trim();
|
||||
const gitDiff = await $`git diff HEAD^..HEAD`;
|
||||
|
||||
const ai = new Ai({
|
||||
...options,
|
||||
model: [host, model],
|
||||
path: process.env['path'] || os.tmpdir(),
|
||||
system: 'You are a code reviewer, use the following git diff to find any bugs and recommend changes using the `recommend` tool.',
|
||||
tools: [{
|
||||
name: 'read_file',
|
||||
description: 'Read contents of a file',
|
||||
args: {
|
||||
path: {type: 'string', description: 'Path to file relative to project root'}
|
||||
},
|
||||
fn: (args) => fs.readFileSync(path.join(root, args.path), 'utf-8')
|
||||
}, {
|
||||
name: 'get_diff',
|
||||
description: 'Check a file for differences using git',
|
||||
args: {
|
||||
path: {type: 'string', description: 'Path to file relative to project root'}
|
||||
},
|
||||
fn: async (args) => await $`git diff HEAD^..HEAD -- ${path.join(root, args.path)}`
|
||||
}, {
|
||||
name: 'recommend',
|
||||
description: 'Make review recommendation',
|
||||
args: {
|
||||
file: {type: 'string', description: 'File path'},
|
||||
line: {type: 'number', description: 'Line number', optional: true},
|
||||
comment: {type: 'string', description: 'Review comment'}
|
||||
},
|
||||
fn: (args) => {
|
||||
console.log(`💬 ${args.file}${args.line ? `:${args.line}` : ''} - ${args.comment}`);
|
||||
// TODO: Add fetch to gitea to add comment to PR
|
||||
}
|
||||
}]
|
||||
});
|
||||
let options = {ollama: {model, host}};
|
||||
if(host === 'anthropic') options = {anthropic: {model, token}};
|
||||
else if(host === 'openai') options = {openAi: {model, token}};
|
||||
const ai = new Ai({
|
||||
...options,
|
||||
model: [host, model],
|
||||
path: process.env['path'] || os.tmpdir(),
|
||||
system: 'You are a code reviewer. Analyze the git diff and use the `recommend` tool for EACH issue you find. You must call `recommend` exactly once for every bug or improvement opportunity. After making all recommendations, provide a brief summary.',
|
||||
tools: [{
|
||||
name: 'read_file',
|
||||
description: 'Read contents of a file',
|
||||
args: {
|
||||
path: {type: 'string', description: 'Path to file relative to project root'}
|
||||
},
|
||||
fn: (args) => fs.readFileSync(path.join(root, args.path), 'utf-8')
|
||||
}, {
|
||||
name: 'get_diff',
|
||||
description: 'Check a file for differences using git',
|
||||
args: {
|
||||
path: {type: 'string', description: 'Path to file relative to project root'}
|
||||
},
|
||||
fn: async (args) => await $`git diff HEAD^..HEAD -- ${path.join(root, args.path)}`
|
||||
}, {
|
||||
name: 'recommend',
|
||||
description: 'REQUIRED: Call this once for every bug, improvement, or concern identified in the review.',
|
||||
args: {
|
||||
file: {type: 'string', description: 'File path'},
|
||||
line: {type: 'number', description: 'Line number in new file', optional: true},
|
||||
comment: {type: 'string', description: 'Review comment explaining the issue'}
|
||||
},
|
||||
fn: (args) => {
|
||||
comments.push({
|
||||
path: args.file,
|
||||
new_position: args.line,
|
||||
body: args.comment,
|
||||
});
|
||||
return 'Comment recorded, continue reviewing';
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
await ai.language.ask(gitDiff);
|
||||
const summary = await ai.language.ask(gitDiff);
|
||||
const res = await fetch(`${git}/api/v1/repos/${owner}/${repo}/pulls/${pr}/reviews`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `token ${auth}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: summary,
|
||||
commit_id: commit,
|
||||
event: 'COMMENT',
|
||||
comments,
|
||||
})
|
||||
});
|
||||
if(!res.ok) throw new Error(`${res.status} ${await res.text()}`);
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user