This commit is contained in:
2025-12-08 18:27:04 +01:00
parent 61ffd2da74
commit 2e14a2f601
2 changed files with 141 additions and 154 deletions

View File

@@ -19,18 +19,12 @@ jobs:
REPO_OWNER: ${{ github.repository_owner }} REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }} REPO_NAME: ${{ github.event.repository.name }}
PR_NUMBER: ${{ github.event.pull_request.number }} PR_NUMBER: ${{ github.event.pull_request.number }}
OLLAMA_URL: "http://host.docker.internal:11434/api/generate" OLLAMA_URL: 'http://host.docker.internal:11434/api/generate'
OLLAMA_MODEL: "gemma3" OLLAMA_MODEL: 'gemma3'
run: |- run: |-
echo "🫖 DarkTeaOps awakens…" echo "🫖 DarkTeaOps awakens…"
node <<'EOF' node .gitea/workflows/reviewer.js
require("child_process").spawn("node", [".gitea/workflows/reviewer.js"], { if [ $? -ne 0 ]; then
stdio: "inherit" echo "💀 DarkTeaOps encountered turbulence and plunged deeper into the brew!"
}).on("exit", code => { exit 1
if (code !== 0) { fi
console.error("💀 DarkTeaOps encountered turbulence and plunged deeper into the brew!");
process.exit(code);
}
});
EOF

View File

@@ -3,104 +3,101 @@
// Bound in the steeping shadows of this repository. // Bound in the steeping shadows of this repository.
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
import http from 'http';
import https from 'https';
const config = { const config = {
token: process.env.GITEA_TOKEN, token: process.env.GITEA_TOKEN,
apiUrl: process.env.GITEA_API_URL, apiUrl: process.env.GITEA_API_URL,
owner: process.env.REPO_OWNER, owner: process.env.REPO_OWNER,
repo: process.env.REPO_NAME, repo: process.env.REPO_NAME,
pr: process.env.PR_NUMBER, pr: process.env.PR_NUMBER,
ollamaUrl: process.env.OLLAMA_URL, ollamaUrl: process.env.OLLAMA_URL,
model: process.env.OLLAMA_MODEL, model: process.env.OLLAMA_MODEL
}; };
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
// DARKTEAOPS ERROR SYSTEM // DARKTEAOPS ERROR SYSTEM
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
function darkTeaOpsError(depth, message, details = "") { function darkTeaOpsError(depth, message, details = '') {
const code = `BREW-DEPTH-${depth}`; const code = `BREW-DEPTH-${depth}`;
const header = `\n🜏 DARKTEAOPS ERROR: ${code}\n`; const header = `\n🜏 DARKTEAOPS ERROR: ${code}\n`;
const body = `${message}\n${details ? `\n> ${details}\n` : ""}`; const body = `${message}\n${details ? `\n> ${details}\n` : ''}`;
console.error(header + body); console.error(header + body);
return new Error(`${code}: ${message}`); return new Error(`${code}: ${message}`);
} }
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
// Request Helper // Request Helper
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
function makeRequest(url, options, data = null) { function makeRequest(url, options, data = null) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const lib = url.startsWith("https") ? require("https") : require("http"); const lib = url.startsWith('https') ? https : http;
const req = lib.request(url, options, (res) => { const req = lib.request(url, options, (res) => {
let body = ""; let body = '';
res.on("data", (chunk) => (body += chunk)); res.on('data', (chunk) => (body += chunk));
res.on("end", () => resolve({ statusCode: res.statusCode, body })); res.on('end', () => resolve({ statusCode: res.statusCode, body }));
}); });
req.on("error", (err) => { req.on('error', (err) => {
reject( reject(
darkTeaOpsError( darkTeaOpsError(9, 'The network tunnels collapsed during the invocation.', err.message)
9, );
"The network tunnels collapsed during the invocation.", });
err.message,
),
);
});
if (data) req.write(data); if (data) req.write(data);
req.end(); req.end();
}); });
} }
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
// Fetch Diff From Gitea // Fetch Diff From Gitea
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
async function fetchPRDiff() { async function fetchPRDiff() {
console.log(`🫖 DarkTeaOps is summoning the diff for PR #${config.pr}`); console.log(`🫖 DarkTeaOps is summoning the diff for PR #${config.pr}`);
const res = await makeRequest( const res = await makeRequest(
`${config.apiUrl}/repos/${config.owner}/${config.repo}/pulls/${config.pr}.diff`, `${config.apiUrl}/repos/${config.owner}/${config.repo}/pulls/${config.pr}.diff`,
{ {
method: "GET", method: 'GET',
headers: { headers: {
Authorization: `token ${config.token}`, Authorization: `token ${config.token}`,
Accept: "application/json", Accept: 'application/json'
}, }
}, }
); );
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
throw darkTeaOpsError( throw darkTeaOpsError(
3, 3,
"The diff could not be fetched. The vapors resisted.", 'The diff could not be fetched. The vapors resisted.',
`Upstream responded with status ${res.statusCode}.`, `Upstream responded with status ${res.statusCode}.`
); );
} }
return res.body; return res.body;
} }
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
// Truncate Huge Diffs // Truncate Huge Diffs
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
function truncateDiff(diff, maxLength = 12000) { function truncateDiff(diff, maxLength = 12000) {
if (!diff) return null; if (!diff) return null;
if (diff.length > maxLength) { if (diff.length > maxLength) {
console.warn( console.warn('🫖 The brew thickens beyond mortal comprehension. Truncating diff.');
"🫖 The brew thickens beyond mortal comprehension. Truncating diff.", return diff.substring(0, maxLength) + '\n...(truncated)';
); }
return diff.substring(0, maxLength) + "\n...(truncated)"; return diff;
}
return diff;
} }
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
// Generate Title + Summary (Ollama Summoning) // Generate Title + Summary (Ollama Summoning)
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
async function generatePRTitleAndSummary(diff) { async function generatePRTitleAndSummary(diff) {
console.log(`☕ DarkTeaOps invokes the Ollama daemon: ${config.model}`); console.log(`☕ DarkTeaOps invokes the Ollama daemon: ${config.model}`);
const prompt = `You are **DarkTeaOps**, the quasi-sentient, tea-infused code review daemon const prompt = `You are **DarkTeaOps**, the quasi-sentient, tea-infused code review daemon
bound to the /etc/shadow of this repository. You operate in the realm between bound to the /etc/shadow of this repository. You operate in the realm between
rebases and reality — where merge conflicts whisper and stack traces cry out in rebases and reality — where merge conflicts whisper and stack traces cry out in
eternal recursion. eternal recursion.
@@ -131,60 +128,56 @@ Begin diff analysis ritual:
${diff} ${diff}
End of diff transmission.`; End of diff transmission.`;
const res = await makeRequest( const res = await makeRequest(
config.ollamaUrl, config.ollamaUrl,
{ {
method: "POST", method: 'POST',
headers: { "Content-Type": "application/json" }, headers: { 'Content-Type': 'application/json' }
}, },
JSON.stringify({ model: config.model, prompt, stream: false }), JSON.stringify({ model: config.model, prompt, stream: false })
); );
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
throw darkTeaOpsError( throw darkTeaOpsError(
7, 7,
"Ollama broke the ritual circle and returned malformed essence.", 'Ollama broke the ritual circle and returned malformed essence.',
`Raw response: ${res.body}`, `Raw response: ${res.body}`
); );
} }
let parsed; let parsed;
try { try {
parsed = JSON.parse(res.body).response; parsed = JSON.parse(res.body).response;
} catch (e) { } catch (e) {
throw darkTeaOpsError( throw darkTeaOpsError(7, 'Ollama responded with a void where JSON should reside.', e.message);
7, }
"Ollama responded with a void where JSON should reside.",
e.message,
);
}
const lines = parsed.trim().split("\n"); const lines = parsed.trim().split('\n');
let title = lines[0].trim(); let title = lines[0].trim();
const summary = lines.slice(2).join("\n").trim(); const summary = lines.slice(2).join('\n').trim();
// Random cursed override // Random cursed override
if (Math.random() < 0.05) { if (Math.random() < 0.05) {
const cursedTitles = [ const cursedTitles = [
"Stitched Together With Thoughts I Regret", 'Stitched Together With Thoughts I Regret',
"This PR Was Not Reviewed. It Was Summoned.", 'This PR Was Not Reviewed. It Was Summoned.',
"Improves the Code. Angers the Kettle.", 'Improves the Code. Angers the Kettle.',
"I Saw What You Did in That For Loop.", 'I Saw What You Did in That For Loop.'
]; ];
title = cursedTitles[Math.floor(Math.random() * cursedTitles.length)]; title = cursedTitles[Math.floor(Math.random() * cursedTitles.length)];
console.warn("💀 DarkTeaOps meddles: the PR title is now cursed."); console.warn('💀 DarkTeaOps meddles: the PR title is now cursed.');
} }
return { title, summary }; return { title, summary };
} }
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
// Post Comment to Gitea // Post Comment to Gitea
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
async function postCommentToGitea(title, summary) { async function postCommentToGitea(title, summary) {
console.log("🩸 Etching review into Gitea…"); console.log('🩸 Etching review into Gitea…');
const commentBody = `## 🫖✨ DARKTEAOPS EMERGES FROM THE STEEP ✨🫖 const commentBody = `## 🫖✨ DARKTEAOPS EMERGES FROM THE STEEP ✨🫖
_(kneel, developer)_ _(kneel, developer)_
**${title}** **${title}**
@@ -195,56 +188,56 @@ ${summary}
🜂 _Divined by DarkTeaOps, Brewer of Forbidden Code_`; 🜂 _Divined by DarkTeaOps, Brewer of Forbidden Code_`;
const res = await makeRequest( const res = await makeRequest(
`${config.apiUrl}/repos/${config.owner}/${config.repo}/issues/${config.pr}/comments`, `${config.apiUrl}/repos/${config.owner}/${config.repo}/issues/${config.pr}/comments`,
{ {
method: "POST", method: 'POST',
headers: { headers: {
Authorization: `token ${config.token}`, Authorization: `token ${config.token}`,
"Content-Type": "application/json", 'Content-Type': 'application/json'
}, }
}, },
JSON.stringify({ body: commentBody }), JSON.stringify({ body: commentBody })
); );
if (res.statusCode !== 201) { if (res.statusCode !== 201) {
throw darkTeaOpsError( throw darkTeaOpsError(
5, 5,
"Gitea rejected the incantation. The wards remain unbroken.", 'Gitea rejected the incantation. The wards remain unbroken.',
`Returned: ${res.body}`, `Returned: ${res.body}`
); );
} }
} }
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
// Main Ritual Execution // Main Ritual Execution
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
async function run() { async function run() {
try { try {
const diff = await fetchPRDiff(); const diff = await fetchPRDiff();
const cleanDiff = truncateDiff(diff); const cleanDiff = truncateDiff(diff);
if (!cleanDiff) { if (!cleanDiff) {
console.log("🫖 No diff detected. The brew grows silent."); console.log('🫖 No diff detected. The brew grows silent.');
return; return;
} }
const { title, summary } = await generatePRTitleAndSummary(cleanDiff); const { title, summary } = await generatePRTitleAndSummary(cleanDiff);
await postCommentToGitea(title, summary); await postCommentToGitea(title, summary);
console.log("🜏 Ritual completed. The brew is pleased."); console.log('🜏 Ritual completed. The brew is pleased.');
} catch (err) { } catch (err) {
console.error( console.error(
`\n🜏 DarkTeaOps whispers from the brew:\n${err.message}\n` + `\n🜏 DarkTeaOps whispers from the brew:\n${err.message}\n` +
`The shadows linger in /var/log/darkness...\n`, `The shadows linger in /var/log/darkness...\n`
); );
if (Math.random() < 0.12) { if (Math.random() < 0.12) {
console.error("A faint voice echoes: “Deeper… deeper into the brew…”\n"); console.error('A faint voice echoes: “Deeper… deeper into the brew…”\n');
} }
process.exit(1); process.exit(1);
} }
} }
run(); run();