mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-17 04:32:31 +03:00
Merge branch 'release/v3.8.47' into feat/3512-usage-entry-type
This commit is contained in:
@@ -26,12 +26,15 @@
|
||||
// env ALLOW_CHANGELOG_REMOVALS=1 report-only (never fails)
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
||||
const CHANGELOG = "CHANGELOG.md";
|
||||
const FRAGMENTS_DIR = "changelog.d";
|
||||
const FRAGMENT_SECTIONS = ["features", "fixes", "maintenance"];
|
||||
const FRAGMENT_SKIP = new Set(["README.md", ".gitkeep"]);
|
||||
|
||||
/** Extract the set of bullet lines (trimmed) from a CHANGELOG text. */
|
||||
export function extractBullets(text) {
|
||||
@@ -56,6 +59,49 @@ export function findLostBullets(baseText, headText) {
|
||||
return lost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate changelog FRAGMENTS (changelog.d/<section>/*.md — see changelog.d/README.md).
|
||||
* A fragment must be a well-formed markdown bullet ("- ...") with no merge-conflict
|
||||
* markers, and must live in a known section dir. Returns [{file, error}]. Pure over
|
||||
* the filesystem — unit-tested via a tmp root.
|
||||
*/
|
||||
export function findInvalidFragments(root = ROOT) {
|
||||
const invalid = [];
|
||||
const base = join(root, FRAGMENTS_DIR);
|
||||
if (!existsSync(base)) return invalid;
|
||||
const entries = readdirSync(base, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile()) {
|
||||
if (!FRAGMENT_SKIP.has(entry.name)) {
|
||||
invalid.push({
|
||||
file: `${FRAGMENTS_DIR}/${entry.name}`,
|
||||
error: `fragments live in a section dir (${FRAGMENT_SECTIONS.join("|")}), not at changelog.d root`,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!FRAGMENT_SECTIONS.includes(entry.name)) {
|
||||
invalid.push({
|
||||
file: `${FRAGMENTS_DIR}/${entry.name}/`,
|
||||
error: `unknown section dir (expected ${FRAGMENT_SECTIONS.join("|")})`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
for (const f of readdirSync(join(base, entry.name))) {
|
||||
if (FRAGMENT_SKIP.has(f) || !f.endsWith(".md")) continue;
|
||||
const file = `${FRAGMENTS_DIR}/${entry.name}/${f}`;
|
||||
const text = readFileSync(join(base, entry.name, f), "utf8");
|
||||
const firstContent = text.split("\n").find((l) => l.trim().length > 0);
|
||||
if (!firstContent) invalid.push({ file, error: "empty fragment" });
|
||||
else if (!firstContent.trimStart().startsWith("- "))
|
||||
invalid.push({ file, error: 'fragment must start with a markdown bullet ("- ")' });
|
||||
else if (/^(<{7}|={7}|>{7})/m.test(text))
|
||||
invalid.push({ file, error: "fragment contains merge-conflict markers" });
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
function git(args) {
|
||||
return execFileSync("git", args, { cwd: ROOT, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
|
||||
}
|
||||
@@ -77,6 +123,16 @@ function resolveBaseRef() {
|
||||
}
|
||||
|
||||
function main() {
|
||||
// Fragment well-formedness first (changelog.d/ — the fragments pattern makes the
|
||||
// eat-guard below structurally unnecessary for PRs that stop editing CHANGELOG.md).
|
||||
const invalidFragments = findInvalidFragments();
|
||||
if (invalidFragments.length > 0) {
|
||||
console.error(`[changelog-integrity] ${invalidFragments.length} invalid changelog fragment(s):`);
|
||||
for (const { file, error } of invalidFragments) console.error(` ✗ ${file}: ${error}`);
|
||||
console.error("\nSee changelog.d/README.md for the fragment convention.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const baseRef = resolveBaseRef();
|
||||
if (!baseRef) {
|
||||
console.log("[changelog-integrity] SKIP — could not resolve a base ref (offline/fresh clone).");
|
||||
|
||||
@@ -56,6 +56,7 @@ export const INTENTIONALLY_INTERNAL = new Set([
|
||||
"healthCheck", // db-internal: importado por db/core.ts (runDbHealthCheck)
|
||||
"jsonMigration", // intentionally-internal: src/app/api/settings/import-json/route.ts
|
||||
"migrationRunner", // db-internal: importado por db/core.ts (runMigrations ao inicializar o DB)
|
||||
"modelCapabilityOverrides", // intentionally-internal: src/app/api/model-capability-overrides/route.ts via import direto "@/lib/db/modelCapabilityOverrides" (#6727 — evita empurrar localDb.ts para o cap de 800 linhas)
|
||||
"notion", // intentionally-internal: settings/notion API route + open-sse/mcp-server/tools/notionTools.ts
|
||||
"obsidian", // intentionally-internal: src/lib/obsidianSync.ts + settings/obsidian route + MCP obsidianTools.ts
|
||||
"optimizationSettings", // db-internal: imported by db/core.ts for SQLite PRAGMA application helpers that require the live adapter
|
||||
@@ -63,6 +64,7 @@ export const INTENTIONALLY_INTERNAL = new Set([
|
||||
"prompts", // DEAD? (production): zero callers de produção encontrados; domínio domain/prompts.ts é independente; testado por tests/integration/proxy-pipeline.test.ts
|
||||
"providerNodeSelect", // db-internal: importado só por db/providers.ts (selectProviderNodeForConnection — lógica pura de seleção de provider node split do providers.ts, #4421)
|
||||
"providerStats", // intentionally-internal: src/app/api/provider-stats/route.ts
|
||||
"proxyLatency", // intentionally-internal: imported directly by src/lib/db/proxies.ts (anti-barrel, #6798)
|
||||
"recovery", // intentionally-internal: bin/cli/runtime.mjs (import() dinâmico) + tests
|
||||
"schemaColumns", // db-internal: importado só por db/core.ts (ensureProviderConnections/UsageHistory/CallLogsColumns + hasColumn/hasTable/getTableColumns — schema-column reconciliation split do core.ts, #4948)
|
||||
"secrets", // intentionally-internal: src/instrumentation-node.ts (import() dinâmico na inicialização)
|
||||
|
||||
Reference in New Issue
Block a user