133 lines
4.1 KiB
TypeScript
Executable File
133 lines
4.1 KiB
TypeScript
Executable File
#!/usr/bin/env runts -- --allow-all
|
|
|
|
import {createInterface} from "node:readline/promises";
|
|
import {stdin, stdout} from "node:process";
|
|
import {
|
|
execCommand,
|
|
execCommandShell,
|
|
fetchDataWithTimeout,
|
|
getSecretValue,
|
|
log,
|
|
ProcessBar,
|
|
term,
|
|
} from "https://script.hatter.ink/@61/deno-commons-mod.ts";
|
|
import {getGitLocalRev, getGitRemoteRev, getGitStatus,} from "https://script.hatter.ink/@2/deno-git-mod.ts";
|
|
|
|
async function checkRev(): Promise<boolean> {
|
|
const localRev = await getGitLocalRev();
|
|
const remoteRev = await new ProcessBar("Checking rev").call(
|
|
async (): Promise<string> => {
|
|
return await getGitRemoteRev();
|
|
},
|
|
);
|
|
if (localRev === remoteRev) {
|
|
console.log(term.green(`Check rev successfully, rev: ${localRev}`));
|
|
} else {
|
|
throw `Check rev failed, local rev: ${localRev} vs remote rev: ${remoteRev}`;
|
|
}
|
|
}
|
|
|
|
async function summarize(): Promise<string> {
|
|
const gitStatus = (await execCommand("git", ["status"]))
|
|
.assertSuccess().getStdoutAsStringThenTrim();
|
|
const gitDiff = (await execCommand("git", ["diff"]))
|
|
.assertSuccess().getStdoutAsStringThenTrim();
|
|
const response = await fetchDataWithTimeout(
|
|
"https://hatter.ink/ai/commit-summarize.json",
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Authorization": "Bearer " +
|
|
await getSecretValue("ai-commit-summarize-token"),
|
|
},
|
|
body: new URLSearchParams({
|
|
"gitStatus": gitStatus,
|
|
"gitDiff": gitDiff,
|
|
}),
|
|
},
|
|
);
|
|
if (response.status != 200) {
|
|
console.log(
|
|
term.yellow(
|
|
`Summarize commit message failed, status: ${response.status}`,
|
|
),
|
|
);
|
|
return null;
|
|
}
|
|
return (await response.json())["data"]["summary"];
|
|
}
|
|
|
|
async function main() {
|
|
await checkRev(); // check local rev <--> remote rev equals
|
|
const gitStatus = await getGitStatus();
|
|
if (gitStatus === null) {
|
|
log.warn("No git status found");
|
|
return;
|
|
}
|
|
log.info("Git status:", gitStatus);
|
|
|
|
const summary = await new ProcessBar("AI summarizing").call(
|
|
async (): Promise<string> => {
|
|
return await summarize();
|
|
},
|
|
);
|
|
if (summary != null) {
|
|
log.success(`AI summarized git commit message: ${summary}`);
|
|
}
|
|
|
|
let emptyCount = 0;
|
|
let message = "empty message";
|
|
const readline = createInterface({ input: stdin, output: stdout });
|
|
do {
|
|
if (emptyCount++ === 3) {
|
|
readline.close();
|
|
console.log("Too many empty messages, then exit");
|
|
return;
|
|
}
|
|
message = await readline.question(
|
|
"Input your commit message (Enter for AI summary) > ",
|
|
);
|
|
if (message.trim().length == 0) {
|
|
message = summary ?? "";
|
|
}
|
|
} while (message.length == 0);
|
|
readline.close();
|
|
|
|
const gitCommitArgs: string[] = [];
|
|
gitCommitArgs.push("commit");
|
|
for (const file of gitStatus.modified) {
|
|
gitCommitArgs.push(file);
|
|
}
|
|
for (const file of gitStatus.deleted) {
|
|
gitCommitArgs.push(file);
|
|
}
|
|
for (const file of gitStatus.untracked) {
|
|
gitCommitArgs.push(file);
|
|
}
|
|
gitCommitArgs.push("-m");
|
|
gitCommitArgs.push(message);
|
|
|
|
if (gitStatus.untracked.length > 0) {
|
|
const gitAddArgs = ["add"];
|
|
for (const file of gitStatus.untracked) {
|
|
gitAddArgs.push(file);
|
|
}
|
|
log.info(">>>>>", ["git", gitAddArgs]);
|
|
await execCommandShell("git", gitAddArgs);
|
|
}
|
|
log.info(">>>>>", ["git", gitCommitArgs]);
|
|
await execCommandShell("git", gitCommitArgs);
|
|
log.info(">>>>>", ["git", "push"]);
|
|
await new ProcessBar("Git pushing").call(async (): Promise<void> => {
|
|
await execCommandShell("git", ["push"]);
|
|
});
|
|
}
|
|
|
|
main().catch((err) => {
|
|
log.error(err);
|
|
process.exit(0);
|
|
}).then(() => process.exit(0));
|
|
|
|
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20260404T230316+08:00.MEYCIQDPcT9WH0J7pH4eZ+3v
|
|
// eShmOZRyUpCu268LAvn08Y5AoQIhAK+zj3K5SuBmtRrz+6TpIZPv/C8L8VSj8FsdhZdMn8rL
|