diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml new file mode 100644 index 0000000..8dd1c31 --- /dev/null +++ b/.github/workflows/ai-review.yml @@ -0,0 +1,161 @@ +name: Ollama Model Setup and PR Review + +on: + pull_request: + branches: + - llm + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + ollama_workflow: + name: Ollama Setup, Model Download, and PR Review + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Ollama (amd64 bundle) + id: install_ollama + run: | + echo "--- Installing Ollama ---" + curl -fsSL https://ollama.com/install.sh | sh + echo "Ollama installation complete." + + - name: Start Ollama Service, Pull Model, and Prepare for Review + id: setup_ollama_and_pull_model + run: | + echo "--- Starting Ollama service in background ---" + nohup ollama serve &> ollama_serve.log & + OLLAMA_PID=$! + echo "Ollama server PID: $OLLAMA_PID" + + echo "Waiting for Ollama service to be ready..." + until curl http://localhost:11434/api/tags > /dev/null 2>&1; do + sleep 1 + done + echo "Ollama service is ready at http://localhost:11434." + + echo "--- Pulling qwen3:14b model ---" + START_TIME=$(date +%s) + ollama pull qwen3:14b + END_TIME=$(date +%s) + DOWNLOAD_DURATION=$((END_TIME - START_TIME)) + echo "::notice file=ollama_pull_duration.log::Model download time: ${DOWNLOAD_DURATION} seconds" + echo "download_duration=${DOWNLOAD_DURATION}" >> $GITHUB_OUTPUT + echo "ollama_pid=$OLLAMA_PID" >> $GITHUB_OUTPUT + env: + OLLAMA_HOST: 0.0.0.0:11434 + + - name: Display Measured Download Duration + run: | + echo "-------------------------------------------------------------------" + echo "Summary of Ollama Model Operation Times:" + echo "Model Download Duration: ${{ steps.setup_ollama_and_pull_model.outputs.download_duration }} seconds" + echo "Note: Initial model load time will occur during the first inference (PR review)." + echo "-------------------------------------------------------------------" + + - name: Get PR diff + run: | + git fetch origin ${{ github.base_ref }} + git diff origin/${{ github.base_ref }}...HEAD > pr_diff.txt + + - name: Review PR with Ollama and Custom Prompt + id: review_with_ollama + if: github.event_name == 'pull_request' + run: | + DIFF_FILE_PATH="pr_diff.txt" + if [ -f "$DIFF_FILE_PATH" ] && [ -s "$DIFF_FILE_PATH" ]; then + PR_DIFF=$(cat "$DIFF_FILE_PATH") + + CUSTOM_PROMPT="You are an expert code reviewer. Your task is to analyze the provided git diff, identify potential issues, suggest improvements, and summarize the key changes. Pay close attention to: + - Readability and code style adherence. + - Potential bugs or edge cases. + - Security vulnerabilities. + - Performance implications. + - Adherence to best practices. + - Missing tests or documentation. + + Here is the git diff: + \`\`\`diff + $PR_DIFF + \`\`\` + + Provide your review in a concise and actionable manner, using markdown formatting including code blocks where necessary. Start with a brief summary, then list specific findings and suggestions. Output only the review comments for the code changes. Do not include your reasoning or thinking process. + End with a summary of the most critical issues found." + + echo "--- Sending diff to Ollama for review ---" + echo "$CUSTOM_PROMPT" | ollama run qwen3:14b > ollama_raw_review.txt + + echo "--- Ollama PR Review Result ---" + echo "::group::Ollama Review Output" + cat ollama_raw_review.txt + echo "::endgroup::" + else + echo "No diff content found or diff file is empty. Skipping PR review." + echo "" > ollama_raw_review.txt + fi + env: + OLLAMA_HOST: 0.0.0.0:11434 + + - name: Remove LLM thinking process from review + if: github.event_name == 'pull_request' + run: | + awk ' + BEGIN {skip=0} + /^Thinking\.\.\./ {skip=1; next} + /^\.\.\.done thinking\./ {skip=0; next} + skip==0 {print} + ' ollama_raw_review.txt > ollama_clean_review.txt + + - name: Set review output as environment variable + id: set_review_output + if: github.event_name == 'pull_request' + run: | + echo "REVIEW_RESULT<> $GITHUB_ENV + cat ollama_clean_review.txt >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Create PR Comment + uses: actions/github-script@v6 + with: + script: | + const reviewOutput = process.env.REVIEW_RESULT; + if (reviewOutput && reviewOutput.trim() !== "") { + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## 🤖 Ollama Code Review (qwen3:14b)\n\n${reviewOutput}` + }); + } else { + console.log('No review content generated. Skipping PR comment.'); + } + env: + REVIEW_RESULT: ${{ env.REVIEW_RESULT }} + if: always() && github.event_name == 'pull_request' && env.REVIEW_RESULT != '' + + - name: Stop Ollama Service (Cleanup) + if: always() + run: | + echo "--- Stopping Ollama service ---" + OLLAMA_PID="${{ steps.setup_ollama_and_pull_model.outputs.ollama_pid }}" + if [ -n "$OLLAMA_PID" ] && ps -p "$OLLAMA_PID" > /dev/null; then + kill "$OLLAMA_PID" + echo "Ollama service with PID $OLLAMA_PID stopped." + else + echo "Ollama service not running or already stopped." + fi + + - name: Show diff file + run: | + cat pr_diff.txt || echo "No diff file found" + echo "Diff file size: $(wc -c < pr_diff.txt)" \ No newline at end of file diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..34fdac7 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,102 @@ +name: Static Analysis + +on: + pull_request: + branches: [ main ] + paths: + - '**.go' + - '**.js' + - '**.jsx' + - '.github/workflows/static-analysis.yml' + +jobs: + lint_and_fix_go: + name: Go Static Analysis + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.21' + + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + ./code/go.sum + key: ${{ runner.os }}-go-${{ hashFiles('./code/go.sum') }} + + - name: Ensure Go module exists and tidy + run: | + if [ ! -f go.mod ]; then + go mod init github.com/${{ github.repository }} + fi + go mod tidy + working-directory: ./code + + - name: Install reviewdog + run: | + curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b /usr/local/bin + + - name: Install golangci-lint + run: | + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.57.2 + echo "$(go env GOPATH)/bin" >> $GITHUB_PATH + working-directory: ./code + + - name: Run golangci-lint and report via reviewdog + run: | + golangci-lint run --out-format=checkstyle ./... | reviewdog -f=checkstyle -name="golangci-lint" -reporter=github-pr-review -fail-on-error=true + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: ./code + + - name: Upload golangci-lint summary + if: always() + uses: actions/upload-artifact@v4 + with: + name: golangci-lint-summary + path: ./code + + - name: Summarize lint results + if: always() + uses: actions/github-script@v7 + with: + script: | + core.summary.addRaw("## Go Static Analysis Run in ./code\n") + + + lint_js: + name: JavaScript Static Analysis + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v4 + + - name: Find all JS/TS projects + id: find_projects + run: | + find . -name package.json -not -path "*/node_modules/*" > projects.txt + cat projects.txt + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Run ESLint via reviewdog (PR review) + uses: reviewdog/action-eslint@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-review + eslint_flags: '**/*.{js,jsx,ts,tsx} --no-error-on-unmatched-pattern' + fail_level: error + workdir: ./js-code \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..40dfc46 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,26 @@ +linters: + enable: + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - staticcheck + - unused + - errcheck + +linters-settings: + gofmt: + simplify: true + goimports: + local-prefixes: github.com/keploy/code-review-agent + +issues: + exclude-rules: + - path: _test\.go + linters: + - errcheck + +run: + timeout: 5m \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..54621d2 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Code Review Agent + + diff --git a/code/main.go b/code/main.go new file mode 100644 index 0000000..e85e253 --- /dev/null +++ b/code/main.go @@ -0,0 +1 @@ +package code diff --git a/go.mod b/go.mod deleted file mode 100644 index ece20c9..0000000 --- a/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/keploy/code-review-agent - -go 1.24.3 diff --git a/js-code/.eslintrc.json b/js-code/.eslintrc.json new file mode 100644 index 0000000..801b1c1 --- /dev/null +++ b/js-code/.eslintrc.json @@ -0,0 +1,19 @@ +// runs-on: ubuntu-latest +{ + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "rules": { + "no-unused-vars": "warn", + "semi": ["error", "always"], + "no-console": "off" + + } +} \ No newline at end of file diff --git a/js-code/main.js b/js-code/main.js new file mode 100644 index 0000000..e69de29 diff --git a/js-code/package.json b/js-code/package.json new file mode 100644 index 0000000..952023d --- /dev/null +++ b/js-code/package.json @@ -0,0 +1,15 @@ +{ + "name": "js-code", + "version": "1.0.0", + "description": "JavaScript code for static analysis", + "main": "main.js", + "scripts": { + "lint": "eslint . --ext .js,.jsx" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "eslint": "^8.0.0" + } +} diff --git a/main.go b/main.go deleted file mode 100644 index 21ccb8c..0000000 --- a/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package codereviewagent - -func main(){ - // init codereview agent -} \ No newline at end of file