Compare commits

...

18 Commits

Author SHA1 Message Date
Shaun Arman
ac568add3f fix: revert to two-dot diff — three-dot requires merge base unavailable in shallow clone
All checks were successful
Test / frontend-tests (pull_request) Successful in 1m14s
Test / rust-fmt-check (pull_request) Successful in 2m46s
PR Review Automation / review (pull_request) Successful in 4m4s
Test / rust-clippy (pull_request) Successful in 19m47s
Test / frontend-typecheck (pull_request) Successful in 1m16s
Test / rust-tests (pull_request) Successful in 21m16s
2026-04-12 17:11:54 -05:00
Shaun Arman
0b49899384 fix: replace github.server_url with hardcoded gogs.tftsr.com for container access
Some checks failed
Test / rust-fmt-check (pull_request) Successful in 3m11s
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
Test / frontend-typecheck (pull_request) Successful in 1m11s
Test / frontend-tests (pull_request) Successful in 1m9s
PR Review Automation / review (pull_request) Failing after 2m58s
2026-04-12 17:08:19 -05:00
Shaun Arman
bf1f81c563 fix: resolve AI review false positives and address high/medium issues
Some checks failed
PR Review Automation / review (pull_request) Failing after 4m22s
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
Test / frontend-tests (pull_request) Successful in 1m5s
Test / frontend-typecheck (pull_request) Successful in 1m11s
Test / rust-fmt-check (pull_request) Successful in 2m43s
Root cause of false-positive "critical" errors:
- sed pattern was matching api_key/token within YAML variable names
  (e.g. OLLAMA_API_KEY:) and redacting the ${{ secrets.X }} value,
  producing mangled syntax that confused the AI reviewer
- Fix: use [^$[:space:]] to skip values starting with $ (template
  expressions and shell variable references)

Other fixes:
- Replace --retry-all-errors with --retry-connrefused --retry-max-time 120
  to avoid wasting retries on unrecoverable 4xx errors
- Check HTTP_CODE before jq validation so error messages are meaningful
- Add permissions: pull-requests: write to job
- Add edited to pull_request.types so title changes trigger re-review
- Change git diff .. to git diff ... (three-dot merge-base diff)
- Replace hardcoded server/repo URLs with github.server_url and
  github.repository context variables (portability)
- Log review length before posting to detect truncation
2026-04-12 17:02:39 -05:00
Shaun Arman
e1724d9631 fix: harden pr-review workflow — URLs, DNS, correctness and reliability
Some checks failed
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
Test / frontend-tests (pull_request) Successful in 1m6s
Test / frontend-typecheck (pull_request) Successful in 1m12s
Test / rust-fmt-check (pull_request) Successful in 2m52s
PR Review Automation / review (pull_request) Successful in 3m43s
Security:
- Replace http://172.0.0.29:3000 git remote with https://gogs.tftsr.com
- Replace http://172.0.0.29:3000 Gitea API URL with https://gogs.tftsr.com
- Remove internal 172.0.0.29 from container DNS (keep 8.8.8.8, 1.1.1.1)
- Move PR_TITLE and PR_NUMBER to env vars to prevent shell injection

Correctness:
- Fix diff_size comparison from lexicographic > '0' to != '0'
- Strip leading whitespace from wc -l output via tr -d ' '
- Switch diff truncation from head -c 20000 to head -n 500 (line-safe)
- Add jq empty validation before parsing Ollama response

Reliability:
- Add --connect-timeout 30 and --retry 3 --retry-delay 5 to Ollama curl
- Add --connect-timeout 10 to review POST curl
- Change Post review comment to if: always() so it runs on analysis failure
- Post explicit failure comment when analysis produces no output
2026-04-12 16:50:12 -05:00
Shaun Arman
7492f33a5c fix: configure container DNS to resolve ollama-ui.tftsr.com
Some checks failed
Test / frontend-typecheck (pull_request) Successful in 1m11s
Test / frontend-tests (pull_request) Successful in 1m10s
Test / rust-fmt-check (pull_request) Successful in 2m45s
PR Review Automation / review (pull_request) Successful in 3m51s
Test / rust-tests (pull_request) Has been cancelled
Test / rust-clippy (pull_request) Has been cancelled
2026-04-12 16:39:43 -05:00
Shaun Arman
8f9529fd77 fix: harden pr-review workflow and sync versions to 0.2.50
Some checks failed
Test / frontend-tests (pull_request) Successful in 1m10s
Test / frontend-typecheck (pull_request) Successful in 1m11s
PR Review Automation / review (pull_request) Failing after 2m47s
Test / rust-fmt-check (pull_request) Successful in 2m49s
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
Workflow changes:
- Switch Ollama to https://ollama-ui.tftsr.com/ollama/v1 (OpenAI-compat)
  with OLLAMA_API_KEY secret — removes hardcoded internal IP
- Update endpoint to /chat/completions and response parsing to
  .choices[0].message.content for OpenAI-compat format
- Add concurrency block to prevent racing on same PR number
- Add shell: bash + set -euo pipefail to all steps
- Add TF_TOKEN presence validation before posting review
- Add --max-time 30 and HTTP status check to comment POST curl
- Redact common secret patterns from diff before sending to Ollama
- Add binary diff warning via grep for "^Binary files"
- Add UTC timestamps to Ollama call and review post log lines
- Add always-run Cleanup step to remove /tmp artifacts

Version consistency:
- Sync Cargo.toml and package.json from 0.1.0 to 0.2.50 to match
  tauri.conf.json
2026-04-12 16:32:15 -05:00
Shaun Arman
02b40bb12d fix: restore migration 014, bump version to 0.2.50, harden pr-review workflow
Some checks failed
Test / frontend-tests (pull_request) Successful in 1m10s
Test / frontend-typecheck (pull_request) Successful in 1m13s
Test / rust-fmt-check (pull_request) Successful in 2m35s
PR Review Automation / review (pull_request) Successful in 3m40s
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
- Restore 014_create_ai_providers migration and tests missing due to
  branch diverging from master before PR #34 merged
- Bump version from 0.2.10 to 0.2.50 to match master and avoid regression
- Trim diff input to 20 KB to prevent Ollama token overflow
- Add --max-time 120 to curl to prevent workflow hanging indefinitely
2026-04-12 16:20:18 -05:00
Shaun Arman
2f7be85e64 fix: use bash shell and remove bash-only substring expansion in pr-review
Some checks failed
Test / frontend-tests (pull_request) Successful in 1m18s
Test / frontend-typecheck (pull_request) Successful in 1m23s
Test / rust-fmt-check (pull_request) Successful in 2m56s
PR Review Automation / review (pull_request) Successful in 4m0s
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
2026-04-12 16:07:39 -05:00
Shaun Arman
0105e321d2 fix: add diagnostics to identify empty Ollama response root cause
Some checks failed
Test / frontend-tests (pull_request) Successful in 1m13s
Test / frontend-typecheck (pull_request) Successful in 1m20s
PR Review Automation / review (pull_request) Failing after 3m7s
Test / rust-fmt-check (pull_request) Successful in 3m23s
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
2026-04-12 16:03:27 -05:00
Shaun Arman
dbd567972f fix: correct Ollama URL, API endpoint, and JSON construction in pr-review workflow
Some checks failed
Test / frontend-tests (pull_request) Successful in 1m19s
Test / frontend-typecheck (pull_request) Successful in 1m34s
Test / rust-fmt-check (pull_request) Successful in 3m31s
PR Review Automation / review (pull_request) Successful in 4m22s
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
- Fix OLLAMA_URL to point at actual Ollama server (172.0.1.42:11434)
- Fix API path from /v1/chat to /api/chat (Ollama native endpoint)
- Fix response parsing from OpenAI format to Ollama native (.message.content)
- Use jq to safely construct JSON bodies in both Analyze and Post steps
- Add HTTP status code check and response body logging for diagnostics
2026-04-12 15:56:13 -05:00
Shaun Arman
313dcf7cdc fix: add debugging output for Ollamaresponse
All checks were successful
Test / frontend-typecheck (pull_request) Successful in 1m14s
Test / frontend-tests (pull_request) Successful in 1m13s
Test / rust-fmt-check (pull_request) Successful in 2m50s
PR Review Automation / review (pull_request) Successful in 2m52s
Test / rust-clippy (pull_request) Successful in 20m50s
Test / rust-tests (pull_request) Successful in 22m5s
2026-04-10 16:16:55 -05:00
Shaun Arman
135f115b98 fix: simplified workflow syntax
Some checks failed
Test / frontend-tests (pull_request) Failing after 1m5s
Test / frontend-typecheck (pull_request) Successful in 1m13s
PR Review Automation / review (pull_request) Failing after 2m43s
Test / rust-fmt-check (pull_request) Successful in 2m45s
Test / rust-clippy (pull_request) Successful in 19m7s
Test / rust-tests (pull_request) Successful in 20m32s
2026-04-10 14:59:41 -05:00
Shaun Arman
0e5ee6dc1b fix: use IP addresses for internal services
Some checks failed
Test / rust-fmt-check (pull_request) Successful in 2m29s
Test / frontend-tests (pull_request) Successful in 1m13s
Test / frontend-typecheck (pull_request) Successful in 1m15s
Test / rust-clippy (pull_request) Successful in 19m59s
Test / rust-tests (pull_request) Successful in 19m51s
PR Review Automation / review (pull_request) Has been cancelled
2026-04-10 14:08:48 -05:00
Shaun Arman
bb296a226c fix: use actions/checkout with token auth and self-hosted runner
Some checks failed
Test / frontend-typecheck (pull_request) Successful in 1m10s
Test / frontend-tests (pull_request) Successful in 1m4s
Test / rust-fmt-check (pull_request) Successful in 2m39s
PR Review Automation / review (pull_request) Has been cancelled
Test / rust-clippy (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
2026-04-10 14:07:54 -05:00
Shaun Arman
ad34bda097 fix: use ubuntu container with git installed
Some checks failed
PR Review Automation / review (pull_request) Failing after 1m49s
Test / rust-fmt-check (pull_request) Successful in 2m35s
Test / frontend-typecheck (pull_request) Successful in 1m14s
Test / frontend-tests (pull_request) Successful in 1m8s
Test / rust-tests (pull_request) Has been cancelled
Test / rust-clippy (pull_request) Has been cancelled
2026-04-10 13:51:50 -05:00
Shaun Arman
0e5e8bee69 fix: remove actions/checkout to avoid Node.js dependency
Some checks failed
Test / frontend-tests (pull_request) Successful in 1m8s
Test / frontend-typecheck (pull_request) Successful in 1m10s
Test / rust-fmt-check (pull_request) Successful in 2m35s
PR Review Automation / review (pull_request) Failing after 3s
Test / rust-clippy (pull_request) Successful in 22m24s
Test / rust-tests (pull_request) Successful in 23m43s
2026-04-10 13:43:43 -05:00
Shaun Arman
6c3d6fc2ec fix: rename GITEA_TOKEN to TF_TOKEN to comply with naming restrictions
Some checks failed
Test / frontend-tests (pull_request) Successful in 1m13s
Test / frontend-typecheck (pull_request) Successful in 1m15s
Test / rust-fmt-check (pull_request) Successful in 3m7s
Test / rust-clippy (pull_request) Successful in 28m59s
Test / rust-tests (pull_request) Successful in 31m16s
PR Review Automation / review (pull_request) Failing after 10s
2026-04-10 13:04:04 -05:00
Shaun Arman
c077bf3348 feat: add automated PR review workflow with Ollama AI 2026-04-10 12:52:17 -05:00
5 changed files with 229 additions and 3 deletions

View File

@ -0,0 +1,134 @@
name: PR Review Automation
on:
pull_request:
types: [opened, synchronize, reopened, edited]
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
permissions:
pull-requests: write
container:
image: ubuntu:22.04
options: --dns 8.8.8.8 --dns 1.1.1.1
steps:
- name: Install dependencies
shell: bash
run: |
set -euo pipefail
apt-get update -qq && apt-get install -y -qq git curl jq
- name: Checkout code
shell: bash
env:
REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
git init
git remote add origin "https://gogs.tftsr.com/${REPOSITORY}.git"
git fetch --depth=1 origin ${{ github.head_ref }}
git checkout FETCH_HEAD
- name: Get PR diff
id: diff
shell: bash
run: |
set -euo pipefail
git fetch origin ${{ github.base_ref }}
git diff origin/${{ github.base_ref }}..HEAD > /tmp/pr_diff.txt
echo "diff_size=$(wc -l < /tmp/pr_diff.txt | tr -d ' ')" >> $GITHUB_OUTPUT
- name: Analyze with Ollama
id: analyze
if: steps.diff.outputs.diff_size != '0'
shell: bash
env:
OLLAMA_URL: https://ollama-ui.tftsr.com/ollama/v1
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
if grep -q "^Binary files" /tmp/pr_diff.txt; then
echo "WARNING: Binary file changes detected — they will be excluded from analysis"
fi
DIFF_CONTENT=$(head -n 500 /tmp/pr_diff.txt \
| sed -E 's/(password|token|secret|api_key|private_key)[[:space:]]*[=:][[:space:]]+([^$[:space:]][^[:space:]]*)/\1=[REDACTED]/gi')
PROMPT="Analyze the following code changes for correctness, security issues, and best practices. PR Title: ${PR_TITLE}\n\nDiff:\n${DIFF_CONTENT}\n\nProvide a review with: 1) Summary, 2) Bugs/errors, 3) Security issues, 4) Best practices. Give specific comments with suggested fixes."
BODY=$(jq -n \
--arg model "qwen3-coder-next:latest" \
--arg content "$PROMPT" \
'{model: $model, messages: [{role: "user", content: $content}], stream: false}')
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] PR #${PR_NUMBER} - Calling Ollama API (${#BODY} bytes)..."
HTTP_CODE=$(curl -s --max-time 120 --connect-timeout 30 \
--retry 3 --retry-delay 5 --retry-connrefused --retry-max-time 120 \
-o /tmp/ollama_response.json -w "%{http_code}" \
-X POST "$OLLAMA_URL/chat/completions" \
-H "Authorization: Bearer $OLLAMA_API_KEY" \
-H "Content-Type: application/json" \
-d "$BODY")
echo "HTTP status: $HTTP_CODE"
echo "Response file size: $(wc -c < /tmp/ollama_response.json) bytes"
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Ollama returned HTTP $HTTP_CODE"
cat /tmp/ollama_response.json
exit 1
fi
if ! jq empty /tmp/ollama_response.json 2>/dev/null; then
echo "ERROR: Invalid JSON response from Ollama"
cat /tmp/ollama_response.json
exit 1
fi
jq . /tmp/ollama_response.json
REVIEW=$(jq -r '.choices[0].message.content // empty' /tmp/ollama_response.json)
if [ -z "$REVIEW" ]; then
echo "ERROR: No content in Ollama response"
exit 1
fi
echo "Review length: ${#REVIEW} chars"
echo "$REVIEW" > /tmp/pr_review.txt
- name: Post review comment
if: always() && steps.diff.outputs.diff_size != '0'
shell: bash
env:
TF_TOKEN: ${{ secrets.TFT_GITEA_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
if [ -z "${TF_TOKEN:-}" ]; then
echo "ERROR: TFT_GITEA_TOKEN secret is not set"
exit 1
fi
if [ -f "/tmp/pr_review.txt" ] && [ -s "/tmp/pr_review.txt" ]; then
REVIEW_BODY=$(head -c 65536 /tmp/pr_review.txt)
BODY=$(jq -n \
--arg body "🤖 Automated PR Review:\n\n${REVIEW_BODY}\n\n---\n*this is an automated review from Ollama*" \
'{body: $body, event: "COMMENT"}')
else
BODY=$(jq -n \
'{body: "⚠️ Automated PR Review could not be completed — Ollama analysis failed or produced no output.", event: "COMMENT"}')
fi
HTTP_CODE=$(curl -s --max-time 30 --connect-timeout 10 \
-o /tmp/review_post_response.json -w "%{http_code}" \
-X POST "https://gogs.tftsr.com/api/v1/repos/${REPOSITORY}/pulls/${PR_NUMBER}/reviews" \
-H "Authorization: token $TF_TOKEN" \
-H "Content-Type: application/json" \
-d "$BODY")
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Post review HTTP status: $HTTP_CODE"
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
echo "ERROR: Failed to post review (HTTP $HTTP_CODE)"
cat /tmp/review_post_response.json
exit 1
fi
- name: Cleanup
if: always()
shell: bash
run: rm -f /tmp/pr_diff.txt /tmp/ollama_response.json /tmp/pr_review.txt /tmp/review_post_response.json

View File

@ -1,7 +1,7 @@
{
"name": "tftsr",
"private": true,
"version": "0.1.0",
"version": "0.2.50",
"type": "module",
"scripts": {
"dev": "vite",

View File

@ -1,6 +1,6 @@
[package]
name = "trcaa"
version = "0.1.0"
version = "0.2.50"
edition = "2021"
[lib]

View File

@ -170,6 +170,27 @@ pub fn run_migrations(conn: &Connection) -> anyhow::Result<()> {
is_paste INTEGER NOT NULL DEFAULT 0
);",
),
(
"014_create_ai_providers",
"CREATE TABLE IF NOT EXISTS ai_providers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
provider_type TEXT NOT NULL,
api_url TEXT NOT NULL,
encrypted_api_key TEXT NOT NULL,
model TEXT NOT NULL,
max_tokens INTEGER,
temperature REAL,
custom_endpoint_path TEXT,
custom_auth_header TEXT,
custom_auth_prefix TEXT,
api_format TEXT,
user_id TEXT,
use_datastore_upload INTEGER,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);",
),
];
for (name, sql) in migrations {
@ -468,4 +489,75 @@ mod tests {
assert_eq!(mime_type, "image/png");
assert_eq!(is_paste, 0);
}
#[test]
fn test_create_ai_providers_table() {
let conn = setup_test_db();
let count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='ai_providers'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(count, 1);
let mut stmt = conn.prepare("PRAGMA table_info(ai_providers)").unwrap();
let columns: Vec<String> = stmt
.query_map([], |row| row.get::<_, String>(1))
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert!(columns.contains(&"id".to_string()));
assert!(columns.contains(&"name".to_string()));
assert!(columns.contains(&"provider_type".to_string()));
assert!(columns.contains(&"api_url".to_string()));
assert!(columns.contains(&"encrypted_api_key".to_string()));
assert!(columns.contains(&"model".to_string()));
assert!(columns.contains(&"max_tokens".to_string()));
assert!(columns.contains(&"temperature".to_string()));
assert!(columns.contains(&"custom_endpoint_path".to_string()));
assert!(columns.contains(&"custom_auth_header".to_string()));
assert!(columns.contains(&"custom_auth_prefix".to_string()));
assert!(columns.contains(&"api_format".to_string()));
assert!(columns.contains(&"user_id".to_string()));
assert!(columns.contains(&"use_datastore_upload".to_string()));
assert!(columns.contains(&"created_at".to_string()));
assert!(columns.contains(&"updated_at".to_string()));
}
#[test]
fn test_store_and_retrieve_ai_provider() {
let conn = setup_test_db();
conn.execute(
"INSERT INTO ai_providers (id, name, provider_type, api_url, encrypted_api_key, model)
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
rusqlite::params![
"test-provider-1",
"My OpenAI",
"openai",
"https://api.openai.com/v1",
"encrypted_key_123",
"gpt-4o"
],
)
.unwrap();
let (name, provider_type, api_url, encrypted_key, model): (String, String, String, String, String) = conn
.query_row(
"SELECT name, provider_type, api_url, encrypted_api_key, model FROM ai_providers WHERE name = ?1",
["My OpenAI"],
|r| Ok((r.get(0)?, r.get(1)?, r.get(2)?, r.get(3)?, r.get(4)?)),
)
.unwrap();
assert_eq!(name, "My OpenAI");
assert_eq!(provider_type, "openai");
assert_eq!(api_url, "https://api.openai.com/v1");
assert_eq!(encrypted_key, "encrypted_key_123");
assert_eq!(model, "gpt-4o");
}
}

View File

@ -1,6 +1,6 @@
{
"productName": "Troubleshooting and RCA Assistant",
"version": "0.2.10",
"version": "0.2.50",
"identifier": "com.trcaa.app",
"build": {
"frontendDist": "../dist",