自动化

This commit is contained in:
2026-05-03 07:28:13 +08:00
Unverified
parent 2929bff081
commit a12be5bfd4
3 changed files with 332 additions and 0 deletions
+164
View File
@@ -0,0 +1,164 @@
import { readFileSync, writeFileSync } from "node:fs";
import { relative, resolve } from "node:path";
const peoplePath = resolve(process.env.PEOPLE_PATH ?? "src/data/people.ts");
const issueBody = process.env.ISSUE_BODY ?? "";
const fields = [
"slug",
"initial",
"name",
"photo",
"location",
"school",
"direction",
"keywords",
"text",
"currentStatus",
"highlight",
"toPastSelf",
"messageToClass",
"favoriteMemory",
"contact"
];
const requiredFields = [
"slug",
"initial",
"name",
"location",
"school",
"direction",
"keywords",
"text",
"currentStatus",
"highlight",
"toPastSelf",
"messageToClass",
"favoriteMemory"
];
const sectionAliases = new Map(fields.map((field) => [field.toLowerCase(), field]));
function normalizeValue(value) {
const trimmed = value.trim();
return trimmed === "_No response_" ? "" : trimmed;
}
function parseIssueForm(body) {
const result = {};
const sectionPattern = /^###\s+(.+?)\s*$/gm;
const matches = [...body.matchAll(sectionPattern)];
for (let index = 0; index < matches.length; index += 1) {
const rawLabel = matches[index][1].trim().toLowerCase();
const field = sectionAliases.get(rawLabel);
if (!field) continue;
const start = matches[index].index + matches[index][0].length;
const end = index + 1 < matches.length ? matches[index + 1].index : body.length;
result[field] = normalizeValue(body.slice(start, end));
}
return result;
}
function assertValidInput(input) {
for (const field of requiredFields) {
if (!input[field]) {
throw new Error(`Issue form is missing required field: ${field}`);
}
}
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(input.slug)) {
throw new Error("slug must use lowercase letters, numbers, and single hyphens only.");
}
}
function readPeopleModule(filePath) {
const source = readFileSync(filePath, "utf8");
const executable = source
.replace("export const peopleIntro =", "const peopleIntro =")
.replace("export const people =", "const people =");
return Function(`${executable}; return { peopleIntro, people };`)();
}
function splitKeywords(value) {
return value
.split(/[、,]/)
.map((keyword) => keyword.trim())
.filter(Boolean);
}
function buildPerson(input, existing = {}) {
const person = { ...existing };
for (const field of fields) {
if (field === "keywords") continue;
if (input[field] || !(field in person)) {
person[field] = input[field] ?? "";
}
}
if (input.keywords) {
person.keywords = splitKeywords(input.keywords);
} else if (!Array.isArray(person.keywords)) {
person.keywords = [];
}
return person;
}
function quote(value) {
return JSON.stringify(value);
}
function formatPerson(person) {
return ` {
slug: ${quote(person.slug)},
initial: ${quote(person.initial)},
name: ${quote(person.name)},
photo: ${quote(person.photo ?? "")},
location: ${quote(person.location)},
school: ${quote(person.school)},
direction: ${quote(person.direction)},
keywords: [${person.keywords.map(quote).join(", ")}],
text: ${quote(person.text)},
currentStatus: ${quote(person.currentStatus)},
highlight: ${quote(person.highlight)},
toPastSelf: ${quote(person.toPastSelf)},
messageToClass: ${quote(person.messageToClass)},
favoriteMemory: ${quote(person.favoriteMemory)},
contact: ${quote(person.contact ?? "")}
}`;
}
function formatPeopleModule(peopleIntro, people) {
return `export const peopleIntro = {
title: ${quote(peopleIntro.title)},
text: ${quote(peopleIntro.text)}
};
export const people = [
${people.map(formatPerson).join(",\n")}
];
`;
}
const input = parseIssueForm(issueBody);
assertValidInput(input);
const { peopleIntro, people } = readPeopleModule(peoplePath);
const existingIndex = people.findIndex((person) => person.slug === input.slug);
if (existingIndex >= 0) {
people[existingIndex] = buildPerson(input, people[existingIndex]);
console.log(`Updated existing person: ${input.slug}`);
} else {
people.push(buildPerson(input));
console.log(`Added new person: ${input.slug}`);
}
writeFileSync(peoplePath, formatPeopleModule(peopleIntro, people), "utf8");
console.log(`Wrote ${relative(process.cwd(), peoplePath)}`);