fix
This commit is contained in:
@@ -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
|
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user