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,6 +3,9 @@
// 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,
@@ -10,16 +13,16 @@ const config = {
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}`);
} }
@@ -29,21 +32,17 @@ function darkTeaOpsError(depth, message, details = "") {
// ──────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────
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,
),
); );
}); });
@@ -61,19 +60,19 @@ async function fetchPRDiff() {
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}.`
); );
} }
@@ -86,10 +85,8 @@ async function fetchPRDiff() {
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;
} }
@@ -134,17 +131,17 @@ 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}`
); );
} }
@@ -152,27 +149,23 @@ End of diff transmission.`;
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 };
@@ -182,7 +175,7 @@ End of diff transmission.`;
// 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)_
@@ -198,20 +191,20 @@ ${summary}
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}`
); );
} }
} }
@@ -225,22 +218,22 @@ async function run() {
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);